From a46b96b3a32a5d274adeb338f79c83162663d6c1 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias Date: Thu, 12 May 2016 17:05:22 +0200 Subject: [PATCH 01/79] starts to remove 3rdparty --- .gitmodules | 3 + 3rdparty/CMakeLists.txt | 8 +- 3rdparty/pugixml | 1 + 3rdparty/pugixml-1.6/CMakeLists.txt | 1 - 3rdparty/pugixml-1.6/contrib/foreach.hpp | 63 - 3rdparty/pugixml-1.6/readme.txt | 52 - 3rdparty/pugixml-1.6/scripts/CMakeLists.txt | 23 - 3rdparty/pugixml-1.6/scripts/premake4.lua | 92 - .../scripts/pugixml.xcodeproj/project.pbxproj | 212 - .../pugixml-1.6/scripts/pugixml_airplay.mkf | 13 - .../scripts/pugixml_codeblocks.cbp | 44 - .../scripts/pugixml_codelite.project | 56 - .../pugixml-1.6/scripts/pugixml_vs2005.vcproj | 343 - .../scripts/pugixml_vs2005_static.vcproj | 343 - .../pugixml-1.6/scripts/pugixml_vs2008.vcproj | 339 - .../scripts/pugixml_vs2008_static.vcproj | 339 - .../scripts/pugixml_vs2010.vcxproj | 191 - .../scripts/pugixml_vs2010_static.vcxproj | 191 - 3rdparty/pugixml-1.6/src/pugiconfig.hpp | 71 - 3rdparty/pugixml-1.6/src/pugixml.cpp | 11554 ---------------- 3rdparty/pugixml-1.6/src/pugixml.hpp | 1366 -- 3rdparty/pugixml_build/CMakeLists.txt | 5 + 3rdparty/svmlight/CMakeLists.txt | 1 - CMakeLists.txt | 16 +- common/CMakeLists.txt | 5 +- common/utils/ODFrameGenerator.h | 2 +- detectors/global2D/CMakeLists.txt | 44 +- .../global2D/detection/ODCascadeDetector.h | 4 +- detectors/global2D/detection/ODHOGDetector.h | 6 +- detectors/global3D/CMakeLists.txt | 2 +- detectors/local2D/CMakeLists.txt | 6 +- .../gsoc2016_blog_giacomo.md | 33 +- examples/apps/CMakeLists.txt | 6 +- examples/objectdetector/CMakeLists.txt | 18 +- 34 files changed, 110 insertions(+), 15343 deletions(-) create mode 100644 .gitmodules create mode 160000 3rdparty/pugixml delete mode 100644 3rdparty/pugixml-1.6/CMakeLists.txt delete mode 100644 3rdparty/pugixml-1.6/contrib/foreach.hpp delete mode 100644 3rdparty/pugixml-1.6/readme.txt delete mode 100644 3rdparty/pugixml-1.6/scripts/CMakeLists.txt delete mode 100644 3rdparty/pugixml-1.6/scripts/premake4.lua delete mode 100644 3rdparty/pugixml-1.6/scripts/pugixml.xcodeproj/project.pbxproj delete mode 100644 3rdparty/pugixml-1.6/scripts/pugixml_airplay.mkf delete mode 100644 3rdparty/pugixml-1.6/scripts/pugixml_codeblocks.cbp delete mode 100644 3rdparty/pugixml-1.6/scripts/pugixml_codelite.project delete mode 100644 3rdparty/pugixml-1.6/scripts/pugixml_vs2005.vcproj delete mode 100644 3rdparty/pugixml-1.6/scripts/pugixml_vs2005_static.vcproj delete mode 100644 3rdparty/pugixml-1.6/scripts/pugixml_vs2008.vcproj delete mode 100644 3rdparty/pugixml-1.6/scripts/pugixml_vs2008_static.vcproj delete mode 100644 3rdparty/pugixml-1.6/scripts/pugixml_vs2010.vcxproj delete mode 100644 3rdparty/pugixml-1.6/scripts/pugixml_vs2010_static.vcxproj delete mode 100644 3rdparty/pugixml-1.6/src/pugiconfig.hpp delete mode 100644 3rdparty/pugixml-1.6/src/pugixml.cpp delete mode 100644 3rdparty/pugixml-1.6/src/pugixml.hpp create mode 100644 3rdparty/pugixml_build/CMakeLists.txt delete mode 100644 3rdparty/svmlight/CMakeLists.txt diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..0dfef3df --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "3rdparty/pugixml"] + path = 3rdparty/pugixml + url = https://github.com/zeux/pugixml.git diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index ef76da1c..1b07260c 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -1,9 +1,9 @@ #add mandatory 3rd party builds -add_subdirectory(pugixml-1.6) +add_subdirectory(pugixml_build) -if(WITH_SVMLIGHT) - add_subdirectory(svmlight) -endif(WITH_SVMLIGHT) +#if(WITH_SVMLIGHT) +# add_subdirectory(svmlight) +#endif(WITH_SVMLIGHT) if(WITH_GPU) add_subdirectory(SiftGPU) diff --git a/3rdparty/pugixml b/3rdparty/pugixml new file mode 160000 index 00000000..2d5980b4 --- /dev/null +++ b/3rdparty/pugixml @@ -0,0 +1 @@ +Subproject commit 2d5980b406fc1efaa63b8f18bcc1b25ab8ec8268 diff --git a/3rdparty/pugixml-1.6/CMakeLists.txt b/3rdparty/pugixml-1.6/CMakeLists.txt deleted file mode 100644 index a5ecf8cb..00000000 --- a/3rdparty/pugixml-1.6/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -add_subdirectory(scripts) \ No newline at end of file diff --git a/3rdparty/pugixml-1.6/contrib/foreach.hpp b/3rdparty/pugixml-1.6/contrib/foreach.hpp deleted file mode 100644 index c4231519..00000000 --- a/3rdparty/pugixml-1.6/contrib/foreach.hpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Boost.Foreach support for pugixml classes. - * This file is provided to the public domain. - * Written by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) - */ - -#ifndef HEADER_PUGIXML_FOREACH_HPP -#define HEADER_PUGIXML_FOREACH_HPP - -#include - -#include "pugixml.hpp" - -/* - * These types add support for BOOST_FOREACH macro to xml_node and xml_document classes (child iteration only). - * Example usage: - * BOOST_FOREACH(xml_node n, doc) {} - */ - -namespace boost -{ - template<> struct range_mutable_iterator - { - typedef pugi::xml_node::iterator type; - }; - - template<> struct range_const_iterator - { - typedef pugi::xml_node::iterator type; - }; - - template<> struct range_mutable_iterator - { - typedef pugi::xml_document::iterator type; - }; - - template<> struct range_const_iterator - { - typedef pugi::xml_document::iterator type; - }; -} - -/* - * These types add support for BOOST_FOREACH macro to xml_node and xml_document classes (child/attribute iteration). - * Example usage: - * BOOST_FOREACH(xml_node n, children(doc)) {} - * BOOST_FOREACH(xml_node n, attributes(doc)) {} - */ - -namespace pugi -{ - inline xml_object_range children(const pugi::xml_node& node) - { - return node.children(); - } - - inline xml_object_range attributes(const pugi::xml_node& node) - { - return node.attributes(); - } -} - -#endif diff --git a/3rdparty/pugixml-1.6/readme.txt b/3rdparty/pugixml-1.6/readme.txt deleted file mode 100644 index faa41d37..00000000 --- a/3rdparty/pugixml-1.6/readme.txt +++ /dev/null @@ -1,52 +0,0 @@ -pugixml 1.6 - an XML processing library - -Copyright (C) 2006-2015, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) -Report bugs and download new versions at http://pugixml.org/ - -This is the distribution of pugixml, which is a C++ XML processing library, -which consists of a DOM-like interface with rich traversal/modification -capabilities, an extremely fast XML parser which constructs the DOM tree from -an XML file/buffer, and an XPath 1.0 implementation for complex data-driven -tree queries. Full Unicode support is also available, with Unicode interface -variants and conversions between different Unicode encodings (which happen -automatically during parsing/saving). - -The distribution contains the following folders: - - contrib/ - various contributions to pugixml - - docs/ - documentation - docs/samples - pugixml usage examples - docs/quickstart.html - quick start guide - docs/manual.html - complete manual - - scripts/ - project files for IDE/build systems - - src/ - header and source files - - readme.txt - this file. - -This library is distributed under the MIT License: - -Copyright (c) 2006-2015 Arseny Kapoulkine - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. diff --git a/3rdparty/pugixml-1.6/scripts/CMakeLists.txt b/3rdparty/pugixml-1.6/scripts/CMakeLists.txt deleted file mode 100644 index 094d1ca2..00000000 --- a/3rdparty/pugixml-1.6/scripts/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -project(od_pugixml) - -cmake_minimum_required(VERSION 2.6) - -# Pre-defines standard install locations on *nix systems. -include(GNUInstallDirs) -mark_as_advanced(CLEAR CMAKE_INSTALL_LIBDIR CMAKE_INSTALL_INCLUDEDIR) - -set(HEADERS ../src/pugixml.hpp ../src/pugiconfig.hpp) -set(SOURCES ${HEADERS} ../src/pugixml.cpp) - -add_library(pugixml SHARED ${SOURCES}) - -set_target_properties(pugixml PROPERTIES VERSION 1.6 SOVERSION 1) - -install(TARGETS pugixml EXPORT pugixml-config - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} -) - -install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) -install(EXPORT pugixml-config DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/pugixml) diff --git a/3rdparty/pugixml-1.6/scripts/premake4.lua b/3rdparty/pugixml-1.6/scripts/premake4.lua deleted file mode 100644 index f1b89a0a..00000000 --- a/3rdparty/pugixml-1.6/scripts/premake4.lua +++ /dev/null @@ -1,92 +0,0 @@ --- Reset RNG seed to get consistent results across runs (i.e. XCode) -math.randomseed(12345) - -local static = _ARGS[1] == 'static' -local action = premake.action.current() - -if string.startswith(_ACTION, "vs") then - if action then - -- Disable solution generation - function action.onsolution(sln) - sln.vstudio_configs = premake.vstudio_buildconfigs(sln) - end - - -- Rename output file - function action.onproject(prj) - local name = "%%_" .. _ACTION .. (static and "_static" or "") - - if static then - for k, v in pairs(prj.project.__configs) do - v.objectsdir = v.objectsdir .. "Static" - end - end - - if _ACTION == "vs2010" then - premake.generate(prj, name .. ".vcxproj", premake.vs2010_vcxproj) - else - premake.generate(prj, name .. ".vcproj", premake.vs200x_vcproj) - end - end - end -elseif _ACTION == "codeblocks" then - action.onsolution = nil - - function action.onproject(prj) - premake.generate(prj, "%%_" .. _ACTION .. ".cbp", premake.codeblocks_cbp) - end -elseif _ACTION == "codelite" then - action.onsolution = nil - - function action.onproject(prj) - premake.generate(prj, "%%_" .. _ACTION .. ".project", premake.codelite_project) - end -end - -solution "pugixml" - objdir(_ACTION) - targetdir(_ACTION) - -if string.startswith(_ACTION, "vs") then - if _ACTION ~= "vs2002" and _ACTION ~= "vs2003" then - platforms { "x32", "x64" } - - configuration "x32" targetdir(_ACTION .. "/x32") - configuration "x64" targetdir(_ACTION .. "/x64") - end - - configurations { "Debug", "Release" } - - if static then - configuration "Debug" targetsuffix "sd" - configuration "Release" targetsuffix "s" - else - configuration "Debug" targetsuffix "d" - end -else - if _ACTION == "xcode3" then - platforms "universal" - end - - configurations { "Debug", "Release" } - - configuration "Debug" targetsuffix "d" -end - -project "pugixml" - kind "StaticLib" - language "C++" - files { "../src/pugixml.hpp", "../src/pugiconfig.hpp", "../src/pugixml.cpp" } - flags { "NoPCH", "NoMinimalRebuild", "NoEditAndContinue", "Symbols" } - uuid "89A1E353-E2DC-495C-B403-742BE206ACED" - -configuration "Debug" - defines { "_DEBUG" } - -configuration "Release" - defines { "NDEBUG" } - flags { "Optimize" } - -if static then - configuration "*" - flags { "StaticRuntime" } -end diff --git a/3rdparty/pugixml-1.6/scripts/pugixml.xcodeproj/project.pbxproj b/3rdparty/pugixml-1.6/scripts/pugixml.xcodeproj/project.pbxproj deleted file mode 100644 index 7d56bafd..00000000 --- a/3rdparty/pugixml-1.6/scripts/pugixml.xcodeproj/project.pbxproj +++ /dev/null @@ -1,212 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 45; - objects = { - -/* Begin PBXBuildFile section */ - 0424128F67AB5C730232235E /* pugixml.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 47481C4F0E03673E0E780637 /* pugixml.cpp */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - 0B66463C5F896E6449051D38 /* pugiconfig.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = "pugiconfig.hpp"; path = "pugiconfig.hpp"; sourceTree = ""; }; - 47481C4F0E03673E0E780637 /* pugixml.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "pugixml.cpp"; path = "pugixml.cpp"; sourceTree = ""; }; - 6C911F0460FC44CD3B1B5624 /* pugixml.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = "pugixml.hpp"; path = "pugixml.hpp"; sourceTree = ""; }; - 1DA04ADC64C3566D16C45B6D /* libpugixmld.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = "libpugixmld.a"; path = "libpugixmld.a"; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 2BA00212518037166623673F /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 19E0517F3CF26ED63AE23641 /* pugixml */ = { - isa = PBXGroup; - children = ( - 578963B4309E714F05E01D71 /* src */, - 219F66186DDF392149043810 /* Products */, - ); - name = "pugixml"; - sourceTree = ""; - }; - 578963B4309E714F05E01D71 /* src */ = { - isa = PBXGroup; - children = ( - 0B66463C5F896E6449051D38 /* pugiconfig.hpp */, - 47481C4F0E03673E0E780637 /* pugixml.cpp */, - 6C911F0460FC44CD3B1B5624 /* pugixml.hpp */, - ); - name = "src"; - path = ../src; - sourceTree = ""; - }; - 219F66186DDF392149043810 /* Products */ = { - isa = PBXGroup; - children = ( - 1DA04ADC64C3566D16C45B6D /* libpugixmld.a */, - ); - name = "Products"; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 6B55152571905B6C3A6F39D0 /* pugixml */ = { - isa = PBXNativeTarget; - buildConfigurationList = 73BF376C14AA1ECC0AC517ED /* Build configuration list for PBXNativeTarget "pugixml" */; - buildPhases = ( - 6CA66B9B6252229A36E8733C /* Resources */, - 287808486FBF545206A47CC1 /* Sources */, - 2BA00212518037166623673F /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "pugixml"; - productName = "pugixml"; - productReference = 1DA04ADC64C3566D16C45B6D /* libpugixmld.a */; - productType = "com.apple.product-type.library.static"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 08FB7793FE84155DC02AAC07 /* Project object */ = { - isa = PBXProject; - buildConfigurationList = 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "pugixml" */; - compatibilityVersion = "Xcode 3.1"; - hasScannedForEncodings = 1; - mainGroup = 19E0517F3CF26ED63AE23641 /* pugixml */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 6B55152571905B6C3A6F39D0 /* libpugixmld.a */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 6CA66B9B6252229A36E8733C /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 287808486FBF545206A47CC1 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 0424128F67AB5C730232235E /* pugixml.cpp in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 4FDB54E4253E36FC55CE27E8 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CONFIGURATION_BUILD_DIR = xcode3; - GCC_DYNAMIC_NO_PIC = NO; - GCC_MODEL_TUNING = G5; - INSTALL_PATH = /usr/local/lib; - PRODUCT_NAME = "pugixmld"; - }; - name = "Debug"; - }; - 0A4C28F553990E0405306C15 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CONFIGURATION_BUILD_DIR = xcode3; - GCC_DYNAMIC_NO_PIC = NO; - GCC_MODEL_TUNING = G5; - INSTALL_PATH = /usr/local/lib; - PRODUCT_NAME = "pugixml"; - }; - name = "Release"; - }; - 65DB0F6D27EA20852B6E3BB4 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; - CONFIGURATION_BUILD_DIR = "$(SYMROOT)"; - CONFIGURATION_TEMP_DIR = "$(OBJROOT)"; - COPY_PHASE_STRIP = NO; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "_DEBUG", - ); - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - OBJROOT = "xcode3/Universal/Debug"; - ONLY_ACTIVE_ARCH = NO; - PREBINDING = NO; - SYMROOT = "xcode3"; - }; - name = "Debug"; - }; - 5314084032B57C1A11945858 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; - CONFIGURATION_BUILD_DIR = "$(SYMROOT)"; - CONFIGURATION_TEMP_DIR = "$(OBJROOT)"; - COPY_PHASE_STRIP = NO; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_OPTIMIZATION_LEVEL = s; - GCC_PREPROCESSOR_DEFINITIONS = ( - "NDEBUG", - ); - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - OBJROOT = "xcode3/Universal/Release"; - ONLY_ACTIVE_ARCH = NO; - PREBINDING = NO; - SYMROOT = "xcode3"; - }; - name = "Release"; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 73BF376C14AA1ECC0AC517ED /* Build configuration list for PBXNativeTarget "libpugixmld.a" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 4FDB54E4253E36FC55CE27E8 /* Debug */, - 0A4C28F553990E0405306C15 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = "Debug"; - }; - 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "pugixml" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 65DB0F6D27EA20852B6E3BB4 /* Debug */, - 5314084032B57C1A11945858 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = "Debug"; - }; -/* End XCConfigurationList section */ - - }; - rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; -} diff --git a/3rdparty/pugixml-1.6/scripts/pugixml_airplay.mkf b/3rdparty/pugixml-1.6/scripts/pugixml_airplay.mkf deleted file mode 100644 index 477f54a9..00000000 --- a/3rdparty/pugixml-1.6/scripts/pugixml_airplay.mkf +++ /dev/null @@ -1,13 +0,0 @@ -includepaths -{ -"../src" -} - -files -{ -("../src") -pugiconfig.hpp -pugixml.cpp -pugixml.hpp -} - diff --git a/3rdparty/pugixml-1.6/scripts/pugixml_codeblocks.cbp b/3rdparty/pugixml-1.6/scripts/pugixml_codeblocks.cbp deleted file mode 100644 index e18ccfca..00000000 --- a/3rdparty/pugixml-1.6/scripts/pugixml_codeblocks.cbp +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - diff --git a/3rdparty/pugixml-1.6/scripts/pugixml_codelite.project b/3rdparty/pugixml-1.6/scripts/pugixml_codelite.project deleted file mode 100644 index 637a81da..00000000 --- a/3rdparty/pugixml-1.6/scripts/pugixml_codelite.project +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - None - - - - - - - - - - - - - - - - - - - - - None - - - - - - - - - - - - - diff --git a/3rdparty/pugixml-1.6/scripts/pugixml_vs2005.vcproj b/3rdparty/pugixml-1.6/scripts/pugixml_vs2005.vcproj deleted file mode 100644 index b60f5af8..00000000 --- a/3rdparty/pugixml-1.6/scripts/pugixml_vs2005.vcproj +++ /dev/null @@ -1,343 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/3rdparty/pugixml-1.6/scripts/pugixml_vs2005_static.vcproj b/3rdparty/pugixml-1.6/scripts/pugixml_vs2005_static.vcproj deleted file mode 100644 index 065e0eb4..00000000 --- a/3rdparty/pugixml-1.6/scripts/pugixml_vs2005_static.vcproj +++ /dev/null @@ -1,343 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/3rdparty/pugixml-1.6/scripts/pugixml_vs2008.vcproj b/3rdparty/pugixml-1.6/scripts/pugixml_vs2008.vcproj deleted file mode 100644 index 72186182..00000000 --- a/3rdparty/pugixml-1.6/scripts/pugixml_vs2008.vcproj +++ /dev/null @@ -1,339 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/3rdparty/pugixml-1.6/scripts/pugixml_vs2008_static.vcproj b/3rdparty/pugixml-1.6/scripts/pugixml_vs2008_static.vcproj deleted file mode 100644 index a00e9d1f..00000000 --- a/3rdparty/pugixml-1.6/scripts/pugixml_vs2008_static.vcproj +++ /dev/null @@ -1,339 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/3rdparty/pugixml-1.6/scripts/pugixml_vs2010.vcxproj b/3rdparty/pugixml-1.6/scripts/pugixml_vs2010.vcxproj deleted file mode 100644 index c29b4c57..00000000 --- a/3rdparty/pugixml-1.6/scripts/pugixml_vs2010.vcxproj +++ /dev/null @@ -1,191 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {89A1E353-E2DC-495C-B403-742BE206ACED} - pugixml - Win32Proj - - - - StaticLibrary - MultiByte - true - - - StaticLibrary - MultiByte - true - - - StaticLibrary - MultiByte - true - false - - - StaticLibrary - MultiByte - true - false - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - vs2010\x32\ - vs2010\x32\Debug\ - pugixmld - vs2010\x64\ - vs2010\x64\Debug\ - pugixmld - vs2010\x32\ - vs2010\x32\Release\ - pugixml - vs2010\x64\ - vs2010\x64\Release\ - pugixml - - - - Disabled - _DEBUG;%(PreprocessorDefinitions) - false - EnableFastChecks - MultiThreadedDebugDLL - true - - Level3 - ProgramDatabase - - - _DEBUG;%(PreprocessorDefinitions) - - - $(OutDir)pugixmld.lib - - - Windows - true - $(OutDir)pugixmld.pdb - - - - - Disabled - _DEBUG;%(PreprocessorDefinitions) - false - EnableFastChecks - MultiThreadedDebugDLL - true - - Level3 - ProgramDatabase - - - _DEBUG;%(PreprocessorDefinitions) - - - $(OutDir)pugixmld.lib - - - Windows - true - $(OutDir)pugixmld.pdb - - - - - Full - NDEBUG;%(PreprocessorDefinitions) - false - true - MultiThreadedDLL - true - - Level3 - ProgramDatabase - - - NDEBUG;%(PreprocessorDefinitions) - - - $(OutDir)pugixml.lib - - - Windows - true - true - true - $(OutDir)pugixml.pdb - - - - - Full - NDEBUG;%(PreprocessorDefinitions) - false - true - MultiThreadedDLL - true - - Level3 - ProgramDatabase - - - NDEBUG;%(PreprocessorDefinitions) - - - $(OutDir)pugixml.lib - - - Windows - true - true - true - $(OutDir)pugixml.pdb - - - - - - - - - - - - - - diff --git a/3rdparty/pugixml-1.6/scripts/pugixml_vs2010_static.vcxproj b/3rdparty/pugixml-1.6/scripts/pugixml_vs2010_static.vcxproj deleted file mode 100644 index c1e133ce..00000000 --- a/3rdparty/pugixml-1.6/scripts/pugixml_vs2010_static.vcxproj +++ /dev/null @@ -1,191 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {89A1E353-E2DC-495C-B403-742BE206ACED} - pugixml - Win32Proj - - - - StaticLibrary - MultiByte - true - - - StaticLibrary - MultiByte - true - - - StaticLibrary - MultiByte - true - false - - - StaticLibrary - MultiByte - true - false - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - vs2010\x32\ - vs2010\x32\DebugStatic\ - pugixmlsd - vs2010\x64\ - vs2010\x64\DebugStatic\ - pugixmlsd - vs2010\x32\ - vs2010\x32\ReleaseStatic\ - pugixmls - vs2010\x64\ - vs2010\x64\ReleaseStatic\ - pugixmls - - - - Disabled - _DEBUG;%(PreprocessorDefinitions) - false - EnableFastChecks - MultiThreadedDebug - true - - Level3 - ProgramDatabase - - - _DEBUG;%(PreprocessorDefinitions) - - - $(OutDir)pugixmlsd.lib - - - Windows - true - $(OutDir)pugixmlsd.pdb - - - - - Disabled - _DEBUG;%(PreprocessorDefinitions) - false - EnableFastChecks - MultiThreadedDebug - true - - Level3 - ProgramDatabase - - - _DEBUG;%(PreprocessorDefinitions) - - - $(OutDir)pugixmlsd.lib - - - Windows - true - $(OutDir)pugixmlsd.pdb - - - - - Full - NDEBUG;%(PreprocessorDefinitions) - false - true - MultiThreaded - true - - Level3 - ProgramDatabase - - - NDEBUG;%(PreprocessorDefinitions) - - - $(OutDir)pugixmls.lib - - - Windows - true - true - true - $(OutDir)pugixmls.pdb - - - - - Full - NDEBUG;%(PreprocessorDefinitions) - false - true - MultiThreaded - true - - Level3 - ProgramDatabase - - - NDEBUG;%(PreprocessorDefinitions) - - - $(OutDir)pugixmls.lib - - - Windows - true - true - true - $(OutDir)pugixmls.pdb - - - - - - - - - - - - - - diff --git a/3rdparty/pugixml-1.6/src/pugiconfig.hpp b/3rdparty/pugixml-1.6/src/pugiconfig.hpp deleted file mode 100644 index 5ee5131f..00000000 --- a/3rdparty/pugixml-1.6/src/pugiconfig.hpp +++ /dev/null @@ -1,71 +0,0 @@ -/** - * pugixml parser - version 1.6 - * -------------------------------------------------------- - * Copyright (C) 2006-2015, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) - * Report bugs and download new versions at http://pugixml.org/ - * - * This library is distributed under the MIT License. See notice at the end - * of this file. - * - * This work is based on the pugxml parser, which is: - * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) - */ - -#ifndef HEADER_PUGICONFIG_HPP -#define HEADER_PUGICONFIG_HPP - -// Uncomment this to enable wchar_t mode -// #define PUGIXML_WCHAR_MODE - -// Uncomment this to disable XPath -// #define PUGIXML_NO_XPATH - -// Uncomment this to disable STL -// #define PUGIXML_NO_STL - -// Uncomment this to disable exceptions -// #define PUGIXML_NO_EXCEPTIONS - -// Set this to control attributes for public classes/functions, i.e.: -// #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL -// #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL -// #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall -// In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead - -// Tune these constants to adjust memory-related behavior -// #define PUGIXML_MEMORY_PAGE_SIZE 32768 -// #define PUGIXML_MEMORY_OUTPUT_STACK 10240 -// #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096 - -// Uncomment this to switch to header-only version -// #define PUGIXML_HEADER_ONLY - -// Uncomment this to enable long long support -// #define PUGIXML_HAS_LONG_LONG - -#endif - -/** - * Copyright (c) 2006-2015 Arseny Kapoulkine - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ diff --git a/3rdparty/pugixml-1.6/src/pugixml.cpp b/3rdparty/pugixml-1.6/src/pugixml.cpp deleted file mode 100644 index 5b77a271..00000000 --- a/3rdparty/pugixml-1.6/src/pugixml.cpp +++ /dev/null @@ -1,11554 +0,0 @@ -/** - * pugixml parser - version 1.6 - * -------------------------------------------------------- - * Copyright (C) 2006-2015, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) - * Report bugs and download new versions at http://pugixml.org/ - * - * This library is distributed under the MIT License. See notice at the end - * of this file. - * - * This work is based on the pugxml parser, which is: - * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) - */ - -#ifndef SOURCE_PUGIXML_CPP -#define SOURCE_PUGIXML_CPP - -#include "pugixml.hpp" - -#include -#include -#include -#include - -#ifdef PUGIXML_WCHAR_MODE -# include -#endif - -#ifndef PUGIXML_NO_XPATH -# include -# include -# ifdef PUGIXML_NO_EXCEPTIONS -# include -# endif -#endif - -#ifndef PUGIXML_NO_STL -# include -# include -# include -#endif - -// For placement new -#include - -#ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable: 4127) // conditional expression is constant -# pragma warning(disable: 4324) // structure was padded due to __declspec(align()) -# pragma warning(disable: 4611) // interaction between '_setjmp' and C++ object destruction is non-portable -# pragma warning(disable: 4702) // unreachable code -# pragma warning(disable: 4996) // this function or variable may be unsafe -# pragma warning(disable: 4793) // function compiled as native: presence of '_setjmp' makes a function unmanaged -#endif - -#ifdef __INTEL_COMPILER -# pragma warning(disable: 177) // function was declared but never referenced -# pragma warning(disable: 279) // controlling expression is constant -# pragma warning(disable: 1478 1786) // function was declared "deprecated" -# pragma warning(disable: 1684) // conversion from pointer to same-sized integral type -#endif - -#if defined(__BORLANDC__) && defined(PUGIXML_HEADER_ONLY) -# pragma warn -8080 // symbol is declared but never used; disabling this inside push/pop bracket does not make the warning go away -#endif - -#ifdef __BORLANDC__ -# pragma option push -# pragma warn -8008 // condition is always false -# pragma warn -8066 // unreachable code -#endif - -#ifdef __SNC__ -// Using diag_push/diag_pop does not disable the warnings inside templates due to a compiler bug -# pragma diag_suppress=178 // function was declared but never referenced -# pragma diag_suppress=237 // controlling expression is constant -#endif - -// Inlining controls -#if defined(_MSC_VER) && _MSC_VER >= 1300 -# define PUGI__NO_INLINE __declspec(noinline) -#elif defined(__GNUC__) -# define PUGI__NO_INLINE __attribute__((noinline)) -#else -# define PUGI__NO_INLINE -#endif - -// Branch weight controls -#if defined(__GNUC__) -# define PUGI__UNLIKELY(cond) __builtin_expect(cond, 0) -#else -# define PUGI__UNLIKELY(cond) (cond) -#endif - -// Simple static assertion -#define PUGI__STATIC_ASSERT(cond) { static const char condition_failed[(cond) ? 1 : -1] = {0}; (void)condition_failed[0]; } - -// Digital Mars C++ bug workaround for passing char loaded from memory via stack -#ifdef __DMC__ -# define PUGI__DMC_VOLATILE volatile -#else -# define PUGI__DMC_VOLATILE -#endif - -// Borland C++ bug workaround for not defining ::memcpy depending on header include order (can't always use std::memcpy because some compilers don't have it at all) -#if defined(__BORLANDC__) && !defined(__MEM_H_USING_LIST) -using std::memcpy; -using std::memmove; -#endif - -// In some environments MSVC is a compiler but the CRT lacks certain MSVC-specific features -#if defined(_MSC_VER) && !defined(__S3E__) -# define PUGI__MSVC_CRT_VERSION _MSC_VER -#endif - -#ifdef PUGIXML_HEADER_ONLY -# define PUGI__NS_BEGIN namespace pugi { namespace impl { -# define PUGI__NS_END } } -# define PUGI__FN inline -# define PUGI__FN_NO_INLINE inline -#else -# if defined(_MSC_VER) && _MSC_VER < 1300 // MSVC6 seems to have an amusing bug with anonymous namespaces inside namespaces -# define PUGI__NS_BEGIN namespace pugi { namespace impl { -# define PUGI__NS_END } } -# else -# define PUGI__NS_BEGIN namespace pugi { namespace impl { namespace { -# define PUGI__NS_END } } } -# endif -# define PUGI__FN -# define PUGI__FN_NO_INLINE PUGI__NO_INLINE -#endif - -// uintptr_t -#if !defined(_MSC_VER) || _MSC_VER >= 1600 -# include -#else -# ifndef _UINTPTR_T_DEFINED -// No native uintptr_t in MSVC6 and in some WinCE versions -typedef size_t uintptr_t; -#define _UINTPTR_T_DEFINED -# endif -PUGI__NS_BEGIN - typedef unsigned __int8 uint8_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int32 uint32_t; -PUGI__NS_END -#endif - -// Memory allocation -PUGI__NS_BEGIN - PUGI__FN void* default_allocate(size_t size) - { - return malloc(size); - } - - PUGI__FN void default_deallocate(void* ptr) - { - free(ptr); - } - - template - struct xml_memory_management_function_storage - { - static allocation_function allocate; - static deallocation_function deallocate; - }; - - // Global allocation functions are stored in class statics so that in header mode linker deduplicates them - // Without a template<> we'll get multiple definitions of the same static - template allocation_function xml_memory_management_function_storage::allocate = default_allocate; - template deallocation_function xml_memory_management_function_storage::deallocate = default_deallocate; - - typedef xml_memory_management_function_storage xml_memory; -PUGI__NS_END - -// String utilities -PUGI__NS_BEGIN - // Get string length - PUGI__FN size_t strlength(const char_t* s) - { - assert(s); - - #ifdef PUGIXML_WCHAR_MODE - return wcslen(s); - #else - return strlen(s); - #endif - } - - // Compare two strings - PUGI__FN bool strequal(const char_t* src, const char_t* dst) - { - assert(src && dst); - - #ifdef PUGIXML_WCHAR_MODE - return wcscmp(src, dst) == 0; - #else - return strcmp(src, dst) == 0; - #endif - } - - // Compare lhs with [rhs_begin, rhs_end) - PUGI__FN bool strequalrange(const char_t* lhs, const char_t* rhs, size_t count) - { - for (size_t i = 0; i < count; ++i) - if (lhs[i] != rhs[i]) - return false; - - return lhs[count] == 0; - } - - // Get length of wide string, even if CRT lacks wide character support - PUGI__FN size_t strlength_wide(const wchar_t* s) - { - assert(s); - - #ifdef PUGIXML_WCHAR_MODE - return wcslen(s); - #else - const wchar_t* end = s; - while (*end) end++; - return static_cast(end - s); - #endif - } - -#ifdef PUGIXML_WCHAR_MODE - // Convert string to wide string, assuming all symbols are ASCII - PUGI__FN void widen_ascii(wchar_t* dest, const char* source) - { - for (const char* i = source; *i; ++i) *dest++ = *i; - *dest = 0; - } -#endif -PUGI__NS_END - -#if !defined(PUGIXML_NO_STL) || !defined(PUGIXML_NO_XPATH) -// auto_ptr-like buffer holder for exception recovery -PUGI__NS_BEGIN - struct buffer_holder - { - void* data; - void (*deleter)(void*); - - buffer_holder(void* data_, void (*deleter_)(void*)): data(data_), deleter(deleter_) - { - } - - ~buffer_holder() - { - if (data) deleter(data); - } - - void* release() - { - void* result = data; - data = 0; - return result; - } - }; -PUGI__NS_END -#endif - -PUGI__NS_BEGIN - static const size_t xml_memory_page_size = - #ifdef PUGIXML_MEMORY_PAGE_SIZE - PUGIXML_MEMORY_PAGE_SIZE - #else - 32768 - #endif - ; - - static const uintptr_t xml_memory_page_alignment = 64; - static const uintptr_t xml_memory_page_pointer_mask = ~(xml_memory_page_alignment - 1); - static const uintptr_t xml_memory_page_contents_shared_mask = 32; - static const uintptr_t xml_memory_page_name_allocated_mask = 16; - static const uintptr_t xml_memory_page_value_allocated_mask = 8; - static const uintptr_t xml_memory_page_type_mask = 7; - static const uintptr_t xml_memory_page_name_allocated_or_shared_mask = xml_memory_page_name_allocated_mask | xml_memory_page_contents_shared_mask; - static const uintptr_t xml_memory_page_value_allocated_or_shared_mask = xml_memory_page_value_allocated_mask | xml_memory_page_contents_shared_mask; - - #define PUGI__NODETYPE(n) static_cast(((n)->header & impl::xml_memory_page_type_mask) + 1) - - struct xml_allocator; - - struct xml_memory_page - { - static xml_memory_page* construct(void* memory) - { - xml_memory_page* result = static_cast(memory); - - result->allocator = 0; - result->prev = 0; - result->next = 0; - result->busy_size = 0; - result->freed_size = 0; - - return result; - } - - xml_allocator* allocator; - - xml_memory_page* prev; - xml_memory_page* next; - - size_t busy_size; - size_t freed_size; - }; - - struct xml_memory_string_header - { - uint16_t page_offset; // offset from page->data - uint16_t full_size; // 0 if string occupies whole page - }; - - struct xml_allocator - { - xml_allocator(xml_memory_page* root): _root(root), _busy_size(root->busy_size) - { - } - - xml_memory_page* allocate_page(size_t data_size) - { - size_t size = sizeof(xml_memory_page) + data_size; - - // allocate block with some alignment, leaving memory for worst-case padding - void* memory = xml_memory::allocate(size + xml_memory_page_alignment); - if (!memory) return 0; - - // align to next page boundary (note: this guarantees at least 1 usable byte before the page) - char* page_memory = reinterpret_cast((reinterpret_cast(memory) + xml_memory_page_alignment) & ~(xml_memory_page_alignment - 1)); - - // prepare page structure - xml_memory_page* page = xml_memory_page::construct(page_memory); - assert(page); - - page->allocator = _root->allocator; - - // record the offset for freeing the memory block - assert(page_memory > memory && page_memory - static_cast(memory) <= 127); - page_memory[-1] = static_cast(page_memory - static_cast(memory)); - - return page; - } - - static void deallocate_page(xml_memory_page* page) - { - char* page_memory = reinterpret_cast(page); - - xml_memory::deallocate(page_memory - page_memory[-1]); - } - - void* allocate_memory_oob(size_t size, xml_memory_page*& out_page); - - void* allocate_memory(size_t size, xml_memory_page*& out_page) - { - if (_busy_size + size > xml_memory_page_size) return allocate_memory_oob(size, out_page); - - void* buf = reinterpret_cast(_root) + sizeof(xml_memory_page) + _busy_size; - - _busy_size += size; - - out_page = _root; - - return buf; - } - - void deallocate_memory(void* ptr, size_t size, xml_memory_page* page) - { - if (page == _root) page->busy_size = _busy_size; - - assert(ptr >= reinterpret_cast(page) + sizeof(xml_memory_page) && ptr < reinterpret_cast(page) + sizeof(xml_memory_page) + page->busy_size); - (void)!ptr; - - page->freed_size += size; - assert(page->freed_size <= page->busy_size); - - if (page->freed_size == page->busy_size) - { - if (page->next == 0) - { - assert(_root == page); - - // top page freed, just reset sizes - page->busy_size = page->freed_size = 0; - _busy_size = 0; - } - else - { - assert(_root != page); - assert(page->prev); - - // remove from the list - page->prev->next = page->next; - page->next->prev = page->prev; - - // deallocate - deallocate_page(page); - } - } - } - - char_t* allocate_string(size_t length) - { - static const size_t max_encoded_offset = (1 << 16) * sizeof(void*); - - PUGI__STATIC_ASSERT(xml_memory_page_size <= max_encoded_offset); - - // allocate memory for string and header block - size_t size = sizeof(xml_memory_string_header) + length * sizeof(char_t); - - // round size up to pointer alignment boundary - size_t full_size = (size + (sizeof(void*) - 1)) & ~(sizeof(void*) - 1); - - xml_memory_page* page; - xml_memory_string_header* header = static_cast(allocate_memory(full_size, page)); - - if (!header) return 0; - - // setup header - ptrdiff_t page_offset = reinterpret_cast(header) - reinterpret_cast(page) - sizeof(xml_memory_page); - - assert(page_offset % sizeof(void*) == 0); - assert(page_offset >= 0 && static_cast(page_offset) < max_encoded_offset); - header->page_offset = static_cast(static_cast(page_offset) / sizeof(void*)); - - // full_size == 0 for large strings that occupy the whole page - assert(full_size % sizeof(void*) == 0); - assert(full_size < max_encoded_offset || (page->busy_size == full_size && page_offset == 0)); - header->full_size = static_cast(full_size < max_encoded_offset ? full_size / sizeof(void*) : 0); - - // round-trip through void* to avoid 'cast increases required alignment of target type' warning - // header is guaranteed a pointer-sized alignment, which should be enough for char_t - return static_cast(static_cast(header + 1)); - } - - void deallocate_string(char_t* string) - { - // this function casts pointers through void* to avoid 'cast increases required alignment of target type' warnings - // we're guaranteed the proper (pointer-sized) alignment on the input string if it was allocated via allocate_string - - // get header - xml_memory_string_header* header = static_cast(static_cast(string)) - 1; - assert(header); - - // deallocate - size_t page_offset = sizeof(xml_memory_page) + header->page_offset * sizeof(void*); - xml_memory_page* page = reinterpret_cast(static_cast(reinterpret_cast(header) - page_offset)); - - // if full_size == 0 then this string occupies the whole page - size_t full_size = header->full_size == 0 ? page->busy_size : header->full_size * sizeof(void*); - - deallocate_memory(header, full_size, page); - } - - xml_memory_page* _root; - size_t _busy_size; - }; - - PUGI__FN_NO_INLINE void* xml_allocator::allocate_memory_oob(size_t size, xml_memory_page*& out_page) - { - const size_t large_allocation_threshold = xml_memory_page_size / 4; - - xml_memory_page* page = allocate_page(size <= large_allocation_threshold ? xml_memory_page_size : size); - out_page = page; - - if (!page) return 0; - - if (size <= large_allocation_threshold) - { - _root->busy_size = _busy_size; - - // insert page at the end of linked list - page->prev = _root; - _root->next = page; - _root = page; - - _busy_size = size; - } - else - { - // insert page before the end of linked list, so that it is deleted as soon as possible - // the last page is not deleted even if it's empty (see deallocate_memory) - assert(_root->prev); - - page->prev = _root->prev; - page->next = _root; - - _root->prev->next = page; - _root->prev = page; - } - - // allocate inside page - page->busy_size = size; - - return reinterpret_cast(page) + sizeof(xml_memory_page); - } -PUGI__NS_END - -namespace pugi -{ - /// A 'name=value' XML attribute structure. - struct xml_attribute_struct - { - /// Default ctor - xml_attribute_struct(impl::xml_memory_page* page): header(reinterpret_cast(page)), name(0), value(0), prev_attribute_c(0), next_attribute(0) - { - } - - uintptr_t header; - - char_t* name; ///< Pointer to attribute name. - char_t* value; ///< Pointer to attribute value. - - xml_attribute_struct* prev_attribute_c; ///< Previous attribute (cyclic list) - xml_attribute_struct* next_attribute; ///< Next attribute - }; - - /// An XML document tree node. - struct xml_node_struct - { - /// Default ctor - /// \param type - node type - xml_node_struct(impl::xml_memory_page* page, xml_node_type type): header(reinterpret_cast(page) | (type - 1)), parent(0), name(0), value(0), first_child(0), prev_sibling_c(0), next_sibling(0), first_attribute(0) - { - } - - uintptr_t header; - - xml_node_struct* parent; ///< Pointer to parent - - char_t* name; ///< Pointer to element name. - char_t* value; ///< Pointer to any associated string data. - - xml_node_struct* first_child; ///< First child - - xml_node_struct* prev_sibling_c; ///< Left brother (cyclic list) - xml_node_struct* next_sibling; ///< Right brother - - xml_attribute_struct* first_attribute; ///< First attribute - }; -} - -PUGI__NS_BEGIN - struct xml_extra_buffer - { - char_t* buffer; - xml_extra_buffer* next; - }; - - struct xml_document_struct: public xml_node_struct, public xml_allocator - { - xml_document_struct(xml_memory_page* page): xml_node_struct(page, node_document), xml_allocator(page), buffer(0), extra_buffers(0) - { - } - - const char_t* buffer; - - xml_extra_buffer* extra_buffers; - }; - - inline xml_allocator& get_allocator(const xml_node_struct* node) - { - assert(node); - - return *reinterpret_cast(node->header & xml_memory_page_pointer_mask)->allocator; - } - - template inline xml_document_struct& get_document(const Object* object) - { - assert(object); - - return *static_cast(reinterpret_cast(object->header & xml_memory_page_pointer_mask)->allocator); - } -PUGI__NS_END - -// Low-level DOM operations -PUGI__NS_BEGIN - inline xml_attribute_struct* allocate_attribute(xml_allocator& alloc) - { - xml_memory_page* page; - void* memory = alloc.allocate_memory(sizeof(xml_attribute_struct), page); - - return new (memory) xml_attribute_struct(page); - } - - inline xml_node_struct* allocate_node(xml_allocator& alloc, xml_node_type type) - { - xml_memory_page* page; - void* memory = alloc.allocate_memory(sizeof(xml_node_struct), page); - - return new (memory) xml_node_struct(page, type); - } - - inline void destroy_attribute(xml_attribute_struct* a, xml_allocator& alloc) - { - uintptr_t header = a->header; - - if (header & impl::xml_memory_page_name_allocated_mask) alloc.deallocate_string(a->name); - if (header & impl::xml_memory_page_value_allocated_mask) alloc.deallocate_string(a->value); - - alloc.deallocate_memory(a, sizeof(xml_attribute_struct), reinterpret_cast(header & xml_memory_page_pointer_mask)); - } - - inline void destroy_node(xml_node_struct* n, xml_allocator& alloc) - { - uintptr_t header = n->header; - - if (header & impl::xml_memory_page_name_allocated_mask) alloc.deallocate_string(n->name); - if (header & impl::xml_memory_page_value_allocated_mask) alloc.deallocate_string(n->value); - - for (xml_attribute_struct* attr = n->first_attribute; attr; ) - { - xml_attribute_struct* next = attr->next_attribute; - - destroy_attribute(attr, alloc); - - attr = next; - } - - for (xml_node_struct* child = n->first_child; child; ) - { - xml_node_struct* next = child->next_sibling; - - destroy_node(child, alloc); - - child = next; - } - - alloc.deallocate_memory(n, sizeof(xml_node_struct), reinterpret_cast(header & xml_memory_page_pointer_mask)); - } - - inline void append_node(xml_node_struct* child, xml_node_struct* node) - { - child->parent = node; - - xml_node_struct* head = node->first_child; - - if (head) - { - xml_node_struct* tail = head->prev_sibling_c; - - tail->next_sibling = child; - child->prev_sibling_c = tail; - head->prev_sibling_c = child; - } - else - { - node->first_child = child; - child->prev_sibling_c = child; - } - } - - inline void prepend_node(xml_node_struct* child, xml_node_struct* node) - { - child->parent = node; - - xml_node_struct* head = node->first_child; - - if (head) - { - child->prev_sibling_c = head->prev_sibling_c; - head->prev_sibling_c = child; - } - else - child->prev_sibling_c = child; - - child->next_sibling = head; - node->first_child = child; - } - - inline void insert_node_after(xml_node_struct* child, xml_node_struct* node) - { - xml_node_struct* parent = node->parent; - - child->parent = parent; - - if (node->next_sibling) - node->next_sibling->prev_sibling_c = child; - else - parent->first_child->prev_sibling_c = child; - - child->next_sibling = node->next_sibling; - child->prev_sibling_c = node; - - node->next_sibling = child; - } - - inline void insert_node_before(xml_node_struct* child, xml_node_struct* node) - { - xml_node_struct* parent = node->parent; - - child->parent = parent; - - if (node->prev_sibling_c->next_sibling) - node->prev_sibling_c->next_sibling = child; - else - parent->first_child = child; - - child->prev_sibling_c = node->prev_sibling_c; - child->next_sibling = node; - - node->prev_sibling_c = child; - } - - inline void remove_node(xml_node_struct* node) - { - xml_node_struct* parent = node->parent; - - if (node->next_sibling) - node->next_sibling->prev_sibling_c = node->prev_sibling_c; - else - parent->first_child->prev_sibling_c = node->prev_sibling_c; - - if (node->prev_sibling_c->next_sibling) - node->prev_sibling_c->next_sibling = node->next_sibling; - else - parent->first_child = node->next_sibling; - - node->parent = 0; - node->prev_sibling_c = 0; - node->next_sibling = 0; - } - - inline void append_attribute(xml_attribute_struct* attr, xml_node_struct* node) - { - xml_attribute_struct* head = node->first_attribute; - - if (head) - { - xml_attribute_struct* tail = head->prev_attribute_c; - - tail->next_attribute = attr; - attr->prev_attribute_c = tail; - head->prev_attribute_c = attr; - } - else - { - node->first_attribute = attr; - attr->prev_attribute_c = attr; - } - } - - inline void prepend_attribute(xml_attribute_struct* attr, xml_node_struct* node) - { - xml_attribute_struct* head = node->first_attribute; - - if (head) - { - attr->prev_attribute_c = head->prev_attribute_c; - head->prev_attribute_c = attr; - } - else - attr->prev_attribute_c = attr; - - attr->next_attribute = head; - node->first_attribute = attr; - } - - inline void insert_attribute_after(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node) - { - if (place->next_attribute) - place->next_attribute->prev_attribute_c = attr; - else - node->first_attribute->prev_attribute_c = attr; - - attr->next_attribute = place->next_attribute; - attr->prev_attribute_c = place; - place->next_attribute = attr; - } - - inline void insert_attribute_before(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node) - { - if (place->prev_attribute_c->next_attribute) - place->prev_attribute_c->next_attribute = attr; - else - node->first_attribute = attr; - - attr->prev_attribute_c = place->prev_attribute_c; - attr->next_attribute = place; - place->prev_attribute_c = attr; - } - - inline void remove_attribute(xml_attribute_struct* attr, xml_node_struct* node) - { - if (attr->next_attribute) - attr->next_attribute->prev_attribute_c = attr->prev_attribute_c; - else - node->first_attribute->prev_attribute_c = attr->prev_attribute_c; - - if (attr->prev_attribute_c->next_attribute) - attr->prev_attribute_c->next_attribute = attr->next_attribute; - else - node->first_attribute = attr->next_attribute; - - attr->prev_attribute_c = 0; - attr->next_attribute = 0; - } - - PUGI__FN_NO_INLINE xml_node_struct* append_new_node(xml_node_struct* node, xml_allocator& alloc, xml_node_type type = node_element) - { - xml_node_struct* child = allocate_node(alloc, type); - if (!child) return 0; - - append_node(child, node); - - return child; - } - - PUGI__FN_NO_INLINE xml_attribute_struct* append_new_attribute(xml_node_struct* node, xml_allocator& alloc) - { - xml_attribute_struct* attr = allocate_attribute(alloc); - if (!attr) return 0; - - append_attribute(attr, node); - - return attr; - } -PUGI__NS_END - -// Helper classes for code generation -PUGI__NS_BEGIN - struct opt_false - { - enum { value = 0 }; - }; - - struct opt_true - { - enum { value = 1 }; - }; -PUGI__NS_END - -// Unicode utilities -PUGI__NS_BEGIN - inline uint16_t endian_swap(uint16_t value) - { - return static_cast(((value & 0xff) << 8) | (value >> 8)); - } - - inline uint32_t endian_swap(uint32_t value) - { - return ((value & 0xff) << 24) | ((value & 0xff00) << 8) | ((value & 0xff0000) >> 8) | (value >> 24); - } - - struct utf8_counter - { - typedef size_t value_type; - - static value_type low(value_type result, uint32_t ch) - { - // U+0000..U+007F - if (ch < 0x80) return result + 1; - // U+0080..U+07FF - else if (ch < 0x800) return result + 2; - // U+0800..U+FFFF - else return result + 3; - } - - static value_type high(value_type result, uint32_t) - { - // U+10000..U+10FFFF - return result + 4; - } - }; - - struct utf8_writer - { - typedef uint8_t* value_type; - - static value_type low(value_type result, uint32_t ch) - { - // U+0000..U+007F - if (ch < 0x80) - { - *result = static_cast(ch); - return result + 1; - } - // U+0080..U+07FF - else if (ch < 0x800) - { - result[0] = static_cast(0xC0 | (ch >> 6)); - result[1] = static_cast(0x80 | (ch & 0x3F)); - return result + 2; - } - // U+0800..U+FFFF - else - { - result[0] = static_cast(0xE0 | (ch >> 12)); - result[1] = static_cast(0x80 | ((ch >> 6) & 0x3F)); - result[2] = static_cast(0x80 | (ch & 0x3F)); - return result + 3; - } - } - - static value_type high(value_type result, uint32_t ch) - { - // U+10000..U+10FFFF - result[0] = static_cast(0xF0 | (ch >> 18)); - result[1] = static_cast(0x80 | ((ch >> 12) & 0x3F)); - result[2] = static_cast(0x80 | ((ch >> 6) & 0x3F)); - result[3] = static_cast(0x80 | (ch & 0x3F)); - return result + 4; - } - - static value_type any(value_type result, uint32_t ch) - { - return (ch < 0x10000) ? low(result, ch) : high(result, ch); - } - }; - - struct utf16_counter - { - typedef size_t value_type; - - static value_type low(value_type result, uint32_t) - { - return result + 1; - } - - static value_type high(value_type result, uint32_t) - { - return result + 2; - } - }; - - struct utf16_writer - { - typedef uint16_t* value_type; - - static value_type low(value_type result, uint32_t ch) - { - *result = static_cast(ch); - - return result + 1; - } - - static value_type high(value_type result, uint32_t ch) - { - uint32_t msh = static_cast(ch - 0x10000) >> 10; - uint32_t lsh = static_cast(ch - 0x10000) & 0x3ff; - - result[0] = static_cast(0xD800 + msh); - result[1] = static_cast(0xDC00 + lsh); - - return result + 2; - } - - static value_type any(value_type result, uint32_t ch) - { - return (ch < 0x10000) ? low(result, ch) : high(result, ch); - } - }; - - struct utf32_counter - { - typedef size_t value_type; - - static value_type low(value_type result, uint32_t) - { - return result + 1; - } - - static value_type high(value_type result, uint32_t) - { - return result + 1; - } - }; - - struct utf32_writer - { - typedef uint32_t* value_type; - - static value_type low(value_type result, uint32_t ch) - { - *result = ch; - - return result + 1; - } - - static value_type high(value_type result, uint32_t ch) - { - *result = ch; - - return result + 1; - } - - static value_type any(value_type result, uint32_t ch) - { - *result = ch; - - return result + 1; - } - }; - - struct latin1_writer - { - typedef uint8_t* value_type; - - static value_type low(value_type result, uint32_t ch) - { - *result = static_cast(ch > 255 ? '?' : ch); - - return result + 1; - } - - static value_type high(value_type result, uint32_t ch) - { - (void)ch; - - *result = '?'; - - return result + 1; - } - }; - - template struct wchar_selector; - - template <> struct wchar_selector<2> - { - typedef uint16_t type; - typedef utf16_counter counter; - typedef utf16_writer writer; - }; - - template <> struct wchar_selector<4> - { - typedef uint32_t type; - typedef utf32_counter counter; - typedef utf32_writer writer; - }; - - typedef wchar_selector::counter wchar_counter; - typedef wchar_selector::writer wchar_writer; - - template struct utf_decoder - { - static inline typename Traits::value_type decode_utf8_block(const uint8_t* data, size_t size, typename Traits::value_type result) - { - const uint8_t utf8_byte_mask = 0x3f; - - while (size) - { - uint8_t lead = *data; - - // 0xxxxxxx -> U+0000..U+007F - if (lead < 0x80) - { - result = Traits::low(result, lead); - data += 1; - size -= 1; - - // process aligned single-byte (ascii) blocks - if ((reinterpret_cast(data) & 3) == 0) - { - // round-trip through void* to silence 'cast increases required alignment of target type' warnings - while (size >= 4 && (*static_cast(static_cast(data)) & 0x80808080) == 0) - { - result = Traits::low(result, data[0]); - result = Traits::low(result, data[1]); - result = Traits::low(result, data[2]); - result = Traits::low(result, data[3]); - data += 4; - size -= 4; - } - } - } - // 110xxxxx -> U+0080..U+07FF - else if (static_cast(lead - 0xC0) < 0x20 && size >= 2 && (data[1] & 0xc0) == 0x80) - { - result = Traits::low(result, ((lead & ~0xC0) << 6) | (data[1] & utf8_byte_mask)); - data += 2; - size -= 2; - } - // 1110xxxx -> U+0800-U+FFFF - else if (static_cast(lead - 0xE0) < 0x10 && size >= 3 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80) - { - result = Traits::low(result, ((lead & ~0xE0) << 12) | ((data[1] & utf8_byte_mask) << 6) | (data[2] & utf8_byte_mask)); - data += 3; - size -= 3; - } - // 11110xxx -> U+10000..U+10FFFF - else if (static_cast(lead - 0xF0) < 0x08 && size >= 4 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80 && (data[3] & 0xc0) == 0x80) - { - result = Traits::high(result, ((lead & ~0xF0) << 18) | ((data[1] & utf8_byte_mask) << 12) | ((data[2] & utf8_byte_mask) << 6) | (data[3] & utf8_byte_mask)); - data += 4; - size -= 4; - } - // 10xxxxxx or 11111xxx -> invalid - else - { - data += 1; - size -= 1; - } - } - - return result; - } - - static inline typename Traits::value_type decode_utf16_block(const uint16_t* data, size_t size, typename Traits::value_type result) - { - const uint16_t* end = data + size; - - while (data < end) - { - unsigned int lead = opt_swap::value ? endian_swap(*data) : *data; - - // U+0000..U+D7FF - if (lead < 0xD800) - { - result = Traits::low(result, lead); - data += 1; - } - // U+E000..U+FFFF - else if (static_cast(lead - 0xE000) < 0x2000) - { - result = Traits::low(result, lead); - data += 1; - } - // surrogate pair lead - else if (static_cast(lead - 0xD800) < 0x400 && data + 1 < end) - { - uint16_t next = opt_swap::value ? endian_swap(data[1]) : data[1]; - - if (static_cast(next - 0xDC00) < 0x400) - { - result = Traits::high(result, 0x10000 + ((lead & 0x3ff) << 10) + (next & 0x3ff)); - data += 2; - } - else - { - data += 1; - } - } - else - { - data += 1; - } - } - - return result; - } - - static inline typename Traits::value_type decode_utf32_block(const uint32_t* data, size_t size, typename Traits::value_type result) - { - const uint32_t* end = data + size; - - while (data < end) - { - uint32_t lead = opt_swap::value ? endian_swap(*data) : *data; - - // U+0000..U+FFFF - if (lead < 0x10000) - { - result = Traits::low(result, lead); - data += 1; - } - // U+10000..U+10FFFF - else - { - result = Traits::high(result, lead); - data += 1; - } - } - - return result; - } - - static inline typename Traits::value_type decode_latin1_block(const uint8_t* data, size_t size, typename Traits::value_type result) - { - for (size_t i = 0; i < size; ++i) - { - result = Traits::low(result, data[i]); - } - - return result; - } - - static inline typename Traits::value_type decode_wchar_block_impl(const uint16_t* data, size_t size, typename Traits::value_type result) - { - return decode_utf16_block(data, size, result); - } - - static inline typename Traits::value_type decode_wchar_block_impl(const uint32_t* data, size_t size, typename Traits::value_type result) - { - return decode_utf32_block(data, size, result); - } - - static inline typename Traits::value_type decode_wchar_block(const wchar_t* data, size_t size, typename Traits::value_type result) - { - return decode_wchar_block_impl(reinterpret_cast::type*>(data), size, result); - } - }; - - template PUGI__FN void convert_utf_endian_swap(T* result, const T* data, size_t length) - { - for (size_t i = 0; i < length; ++i) result[i] = endian_swap(data[i]); - } - -#ifdef PUGIXML_WCHAR_MODE - PUGI__FN void convert_wchar_endian_swap(wchar_t* result, const wchar_t* data, size_t length) - { - for (size_t i = 0; i < length; ++i) result[i] = static_cast(endian_swap(static_cast::type>(data[i]))); - } -#endif -PUGI__NS_END - -PUGI__NS_BEGIN - enum chartype_t - { - ct_parse_pcdata = 1, // \0, &, \r, < - ct_parse_attr = 2, // \0, &, \r, ', " - ct_parse_attr_ws = 4, // \0, &, \r, ', ", \n, tab - ct_space = 8, // \r, \n, space, tab - ct_parse_cdata = 16, // \0, ], >, \r - ct_parse_comment = 32, // \0, -, >, \r - ct_symbol = 64, // Any symbol > 127, a-z, A-Z, 0-9, _, :, -, . - ct_start_symbol = 128 // Any symbol > 127, a-z, A-Z, _, : - }; - - static const unsigned char chartype_table[256] = - { - 55, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 0, 0, 63, 0, 0, // 0-15 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31 - 8, 0, 6, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 96, 64, 0, // 32-47 - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 192, 0, 1, 0, 48, 0, // 48-63 - 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 64-79 - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 16, 0, 192, // 80-95 - 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 96-111 - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 0, 0, 0, // 112-127 - - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 128+ - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192 - }; - - enum chartypex_t - { - ctx_special_pcdata = 1, // Any symbol >= 0 and < 32 (except \t, \r, \n), &, <, > - ctx_special_attr = 2, // Any symbol >= 0 and < 32 (except \t), &, <, >, " - ctx_start_symbol = 4, // Any symbol > 127, a-z, A-Z, _ - ctx_digit = 8, // 0-9 - ctx_symbol = 16 // Any symbol > 127, a-z, A-Z, 0-9, _, -, . - }; - - static const unsigned char chartypex_table[256] = - { - 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 2, 3, 3, 2, 3, 3, // 0-15 - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 16-31 - 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 16, 16, 0, // 32-47 - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 3, 0, 3, 0, // 48-63 - - 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 64-79 - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 20, // 80-95 - 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 96-111 - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 0, // 112-127 - - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 128+ - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20 - }; - -#ifdef PUGIXML_WCHAR_MODE - #define PUGI__IS_CHARTYPE_IMPL(c, ct, table) ((static_cast(c) < 128 ? table[static_cast(c)] : table[128]) & (ct)) -#else - #define PUGI__IS_CHARTYPE_IMPL(c, ct, table) (table[static_cast(c)] & (ct)) -#endif - - #define PUGI__IS_CHARTYPE(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartype_table) - #define PUGI__IS_CHARTYPEX(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartypex_table) - - PUGI__FN bool is_little_endian() - { - unsigned int ui = 1; - - return *reinterpret_cast(&ui) == 1; - } - - PUGI__FN xml_encoding get_wchar_encoding() - { - PUGI__STATIC_ASSERT(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4); - - if (sizeof(wchar_t) == 2) - return is_little_endian() ? encoding_utf16_le : encoding_utf16_be; - else - return is_little_endian() ? encoding_utf32_le : encoding_utf32_be; - } - - PUGI__FN xml_encoding guess_buffer_encoding(uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3) - { - // look for BOM in first few bytes - if (d0 == 0 && d1 == 0 && d2 == 0xfe && d3 == 0xff) return encoding_utf32_be; - if (d0 == 0xff && d1 == 0xfe && d2 == 0 && d3 == 0) return encoding_utf32_le; - if (d0 == 0xfe && d1 == 0xff) return encoding_utf16_be; - if (d0 == 0xff && d1 == 0xfe) return encoding_utf16_le; - if (d0 == 0xef && d1 == 0xbb && d2 == 0xbf) return encoding_utf8; - - // look for <, (contents); - - PUGI__DMC_VOLATILE uint8_t d0 = data[0], d1 = data[1], d2 = data[2], d3 = data[3]; - - return guess_buffer_encoding(d0, d1, d2, d3); - } - - PUGI__FN bool get_mutable_buffer(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) - { - size_t length = size / sizeof(char_t); - - if (is_mutable) - { - out_buffer = static_cast(const_cast(contents)); - out_length = length; - } - else - { - char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!buffer) return false; - - if (contents) - memcpy(buffer, contents, length * sizeof(char_t)); - else - assert(length == 0); - - buffer[length] = 0; - - out_buffer = buffer; - out_length = length + 1; - } - - return true; - } - -#ifdef PUGIXML_WCHAR_MODE - PUGI__FN bool need_endian_swap_utf(xml_encoding le, xml_encoding re) - { - return (le == encoding_utf16_be && re == encoding_utf16_le) || (le == encoding_utf16_le && re == encoding_utf16_be) || - (le == encoding_utf32_be && re == encoding_utf32_le) || (le == encoding_utf32_le && re == encoding_utf32_be); - } - - PUGI__FN bool convert_buffer_endian_swap(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) - { - const char_t* data = static_cast(contents); - size_t length = size / sizeof(char_t); - - if (is_mutable) - { - char_t* buffer = const_cast(data); - - convert_wchar_endian_swap(buffer, data, length); - - out_buffer = buffer; - out_length = length; - } - else - { - char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!buffer) return false; - - convert_wchar_endian_swap(buffer, data, length); - buffer[length] = 0; - - out_buffer = buffer; - out_length = length + 1; - } - - return true; - } - - PUGI__FN bool convert_buffer_utf8(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size) - { - const uint8_t* data = static_cast(contents); - size_t data_length = size; - - // first pass: get length in wchar_t units - size_t length = utf_decoder::decode_utf8_block(data, data_length, 0); - - // allocate buffer of suitable length - char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!buffer) return false; - - // second pass: convert utf8 input to wchar_t - wchar_writer::value_type obegin = reinterpret_cast(buffer); - wchar_writer::value_type oend = utf_decoder::decode_utf8_block(data, data_length, obegin); - - assert(oend == obegin + length); - *oend = 0; - - out_buffer = buffer; - out_length = length + 1; - - return true; - } - - template PUGI__FN bool convert_buffer_utf16(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap) - { - const uint16_t* data = static_cast(contents); - size_t data_length = size / sizeof(uint16_t); - - // first pass: get length in wchar_t units - size_t length = utf_decoder::decode_utf16_block(data, data_length, 0); - - // allocate buffer of suitable length - char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!buffer) return false; - - // second pass: convert utf16 input to wchar_t - wchar_writer::value_type obegin = reinterpret_cast(buffer); - wchar_writer::value_type oend = utf_decoder::decode_utf16_block(data, data_length, obegin); - - assert(oend == obegin + length); - *oend = 0; - - out_buffer = buffer; - out_length = length + 1; - - return true; - } - - template PUGI__FN bool convert_buffer_utf32(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap) - { - const uint32_t* data = static_cast(contents); - size_t data_length = size / sizeof(uint32_t); - - // first pass: get length in wchar_t units - size_t length = utf_decoder::decode_utf32_block(data, data_length, 0); - - // allocate buffer of suitable length - char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!buffer) return false; - - // second pass: convert utf32 input to wchar_t - wchar_writer::value_type obegin = reinterpret_cast(buffer); - wchar_writer::value_type oend = utf_decoder::decode_utf32_block(data, data_length, obegin); - - assert(oend == obegin + length); - *oend = 0; - - out_buffer = buffer; - out_length = length + 1; - - return true; - } - - PUGI__FN bool convert_buffer_latin1(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size) - { - const uint8_t* data = static_cast(contents); - size_t data_length = size; - - // get length in wchar_t units - size_t length = data_length; - - // allocate buffer of suitable length - char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!buffer) return false; - - // convert latin1 input to wchar_t - wchar_writer::value_type obegin = reinterpret_cast(buffer); - wchar_writer::value_type oend = utf_decoder::decode_latin1_block(data, data_length, obegin); - - assert(oend == obegin + length); - *oend = 0; - - out_buffer = buffer; - out_length = length + 1; - - return true; - } - - PUGI__FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable) - { - // get native encoding - xml_encoding wchar_encoding = get_wchar_encoding(); - - // fast path: no conversion required - if (encoding == wchar_encoding) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); - - // only endian-swapping is required - if (need_endian_swap_utf(encoding, wchar_encoding)) return convert_buffer_endian_swap(out_buffer, out_length, contents, size, is_mutable); - - // source encoding is utf8 - if (encoding == encoding_utf8) return convert_buffer_utf8(out_buffer, out_length, contents, size); - - // source encoding is utf16 - if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) - { - xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; - - return (native_encoding == encoding) ? - convert_buffer_utf16(out_buffer, out_length, contents, size, opt_false()) : - convert_buffer_utf16(out_buffer, out_length, contents, size, opt_true()); - } - - // source encoding is utf32 - if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) - { - xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; - - return (native_encoding == encoding) ? - convert_buffer_utf32(out_buffer, out_length, contents, size, opt_false()) : - convert_buffer_utf32(out_buffer, out_length, contents, size, opt_true()); - } - - // source encoding is latin1 - if (encoding == encoding_latin1) return convert_buffer_latin1(out_buffer, out_length, contents, size); - - assert(!"Invalid encoding"); - return false; - } -#else - template PUGI__FN bool convert_buffer_utf16(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap) - { - const uint16_t* data = static_cast(contents); - size_t data_length = size / sizeof(uint16_t); - - // first pass: get length in utf8 units - size_t length = utf_decoder::decode_utf16_block(data, data_length, 0); - - // allocate buffer of suitable length - char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!buffer) return false; - - // second pass: convert utf16 input to utf8 - uint8_t* obegin = reinterpret_cast(buffer); - uint8_t* oend = utf_decoder::decode_utf16_block(data, data_length, obegin); - - assert(oend == obegin + length); - *oend = 0; - - out_buffer = buffer; - out_length = length + 1; - - return true; - } - - template PUGI__FN bool convert_buffer_utf32(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap) - { - const uint32_t* data = static_cast(contents); - size_t data_length = size / sizeof(uint32_t); - - // first pass: get length in utf8 units - size_t length = utf_decoder::decode_utf32_block(data, data_length, 0); - - // allocate buffer of suitable length - char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!buffer) return false; - - // second pass: convert utf32 input to utf8 - uint8_t* obegin = reinterpret_cast(buffer); - uint8_t* oend = utf_decoder::decode_utf32_block(data, data_length, obegin); - - assert(oend == obegin + length); - *oend = 0; - - out_buffer = buffer; - out_length = length + 1; - - return true; - } - - PUGI__FN size_t get_latin1_7bit_prefix_length(const uint8_t* data, size_t size) - { - for (size_t i = 0; i < size; ++i) - if (data[i] > 127) - return i; - - return size; - } - - PUGI__FN bool convert_buffer_latin1(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) - { - const uint8_t* data = static_cast(contents); - size_t data_length = size; - - // get size of prefix that does not need utf8 conversion - size_t prefix_length = get_latin1_7bit_prefix_length(data, data_length); - assert(prefix_length <= data_length); - - const uint8_t* postfix = data + prefix_length; - size_t postfix_length = data_length - prefix_length; - - // if no conversion is needed, just return the original buffer - if (postfix_length == 0) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); - - // first pass: get length in utf8 units - size_t length = prefix_length + utf_decoder::decode_latin1_block(postfix, postfix_length, 0); - - // allocate buffer of suitable length - char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!buffer) return false; - - // second pass: convert latin1 input to utf8 - memcpy(buffer, data, prefix_length); - - uint8_t* obegin = reinterpret_cast(buffer); - uint8_t* oend = utf_decoder::decode_latin1_block(postfix, postfix_length, obegin + prefix_length); - - assert(oend == obegin + length); - *oend = 0; - - out_buffer = buffer; - out_length = length + 1; - - return true; - } - - PUGI__FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable) - { - // fast path: no conversion required - if (encoding == encoding_utf8) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); - - // source encoding is utf16 - if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) - { - xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; - - return (native_encoding == encoding) ? - convert_buffer_utf16(out_buffer, out_length, contents, size, opt_false()) : - convert_buffer_utf16(out_buffer, out_length, contents, size, opt_true()); - } - - // source encoding is utf32 - if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) - { - xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; - - return (native_encoding == encoding) ? - convert_buffer_utf32(out_buffer, out_length, contents, size, opt_false()) : - convert_buffer_utf32(out_buffer, out_length, contents, size, opt_true()); - } - - // source encoding is latin1 - if (encoding == encoding_latin1) return convert_buffer_latin1(out_buffer, out_length, contents, size, is_mutable); - - assert(!"Invalid encoding"); - return false; - } -#endif - - PUGI__FN size_t as_utf8_begin(const wchar_t* str, size_t length) - { - // get length in utf8 characters - return utf_decoder::decode_wchar_block(str, length, 0); - } - - PUGI__FN void as_utf8_end(char* buffer, size_t size, const wchar_t* str, size_t length) - { - // convert to utf8 - uint8_t* begin = reinterpret_cast(buffer); - uint8_t* end = utf_decoder::decode_wchar_block(str, length, begin); - - assert(begin + size == end); - (void)!end; - - // zero-terminate - buffer[size] = 0; - } - -#ifndef PUGIXML_NO_STL - PUGI__FN std::string as_utf8_impl(const wchar_t* str, size_t length) - { - // first pass: get length in utf8 characters - size_t size = as_utf8_begin(str, length); - - // allocate resulting string - std::string result; - result.resize(size); - - // second pass: convert to utf8 - if (size > 0) as_utf8_end(&result[0], size, str, length); - - return result; - } - - PUGI__FN std::basic_string as_wide_impl(const char* str, size_t size) - { - const uint8_t* data = reinterpret_cast(str); - - // first pass: get length in wchar_t units - size_t length = utf_decoder::decode_utf8_block(data, size, 0); - - // allocate resulting string - std::basic_string result; - result.resize(length); - - // second pass: convert to wchar_t - if (length > 0) - { - wchar_writer::value_type begin = reinterpret_cast(&result[0]); - wchar_writer::value_type end = utf_decoder::decode_utf8_block(data, size, begin); - - assert(begin + length == end); - (void)!end; - } - - return result; - } -#endif - - inline bool strcpy_insitu_allow(size_t length, uintptr_t header, uintptr_t header_mask, char_t* target) - { - // never reuse shared memory - if (header & xml_memory_page_contents_shared_mask) return false; - - size_t target_length = strlength(target); - - // always reuse document buffer memory if possible - if ((header & header_mask) == 0) return target_length >= length; - - // reuse heap memory if waste is not too great - const size_t reuse_threshold = 32; - - return target_length >= length && (target_length < reuse_threshold || target_length - length < target_length / 2); - } - - PUGI__FN bool strcpy_insitu(char_t*& dest, uintptr_t& header, uintptr_t header_mask, const char_t* source) - { - assert(header); - - size_t source_length = strlength(source); - - if (source_length == 0) - { - // empty string and null pointer are equivalent, so just deallocate old memory - xml_allocator* alloc = reinterpret_cast(header & xml_memory_page_pointer_mask)->allocator; - - if (header & header_mask) alloc->deallocate_string(dest); - - // mark the string as not allocated - dest = 0; - header &= ~header_mask; - - return true; - } - else if (dest && strcpy_insitu_allow(source_length, header, header_mask, dest)) - { - // we can reuse old buffer, so just copy the new data (including zero terminator) - memcpy(dest, source, (source_length + 1) * sizeof(char_t)); - - return true; - } - else - { - xml_allocator* alloc = reinterpret_cast(header & xml_memory_page_pointer_mask)->allocator; - - // allocate new buffer - char_t* buf = alloc->allocate_string(source_length + 1); - if (!buf) return false; - - // copy the string (including zero terminator) - memcpy(buf, source, (source_length + 1) * sizeof(char_t)); - - // deallocate old buffer (*after* the above to protect against overlapping memory and/or allocation failures) - if (header & header_mask) alloc->deallocate_string(dest); - - // the string is now allocated, so set the flag - dest = buf; - header |= header_mask; - - return true; - } - } - - struct gap - { - char_t* end; - size_t size; - - gap(): end(0), size(0) - { - } - - // Push new gap, move s count bytes further (skipping the gap). - // Collapse previous gap. - void push(char_t*& s, size_t count) - { - if (end) // there was a gap already; collapse it - { - // Move [old_gap_end, new_gap_start) to [old_gap_start, ...) - assert(s >= end); - memmove(end - size, end, reinterpret_cast(s) - reinterpret_cast(end)); - } - - s += count; // end of current gap - - // "merge" two gaps - end = s; - size += count; - } - - // Collapse all gaps, return past-the-end pointer - char_t* flush(char_t* s) - { - if (end) - { - // Move [old_gap_end, current_pos) to [old_gap_start, ...) - assert(s >= end); - memmove(end - size, end, reinterpret_cast(s) - reinterpret_cast(end)); - - return s - size; - } - else return s; - } - }; - - PUGI__FN char_t* strconv_escape(char_t* s, gap& g) - { - char_t* stre = s + 1; - - switch (*stre) - { - case '#': // &#... - { - unsigned int ucsc = 0; - - if (stre[1] == 'x') // &#x... (hex code) - { - stre += 2; - - char_t ch = *stre; - - if (ch == ';') return stre; - - for (;;) - { - if (static_cast(ch - '0') <= 9) - ucsc = 16 * ucsc + (ch - '0'); - else if (static_cast((ch | ' ') - 'a') <= 5) - ucsc = 16 * ucsc + ((ch | ' ') - 'a' + 10); - else if (ch == ';') - break; - else // cancel - return stre; - - ch = *++stre; - } - - ++stre; - } - else // &#... (dec code) - { - char_t ch = *++stre; - - if (ch == ';') return stre; - - for (;;) - { - if (static_cast(static_cast(ch) - '0') <= 9) - ucsc = 10 * ucsc + (ch - '0'); - else if (ch == ';') - break; - else // cancel - return stre; - - ch = *++stre; - } - - ++stre; - } - - #ifdef PUGIXML_WCHAR_MODE - s = reinterpret_cast(wchar_writer::any(reinterpret_cast(s), ucsc)); - #else - s = reinterpret_cast(utf8_writer::any(reinterpret_cast(s), ucsc)); - #endif - - g.push(s, stre - s); - return stre; - } - - case 'a': // &a - { - ++stre; - - if (*stre == 'm') // &am - { - if (*++stre == 'p' && *++stre == ';') // & - { - *s++ = '&'; - ++stre; - - g.push(s, stre - s); - return stre; - } - } - else if (*stre == 'p') // &ap - { - if (*++stre == 'o' && *++stre == 's' && *++stre == ';') // ' - { - *s++ = '\''; - ++stre; - - g.push(s, stre - s); - return stre; - } - } - break; - } - - case 'g': // &g - { - if (*++stre == 't' && *++stre == ';') // > - { - *s++ = '>'; - ++stre; - - g.push(s, stre - s); - return stre; - } - break; - } - - case 'l': // &l - { - if (*++stre == 't' && *++stre == ';') // < - { - *s++ = '<'; - ++stre; - - g.push(s, stre - s); - return stre; - } - break; - } - - case 'q': // &q - { - if (*++stre == 'u' && *++stre == 'o' && *++stre == 't' && *++stre == ';') // " - { - *s++ = '"'; - ++stre; - - g.push(s, stre - s); - return stre; - } - break; - } - - default: - break; - } - - return stre; - } - - // Parser utilities - #define PUGI__ENDSWITH(c, e) ((c) == (e) || ((c) == 0 && endch == (e))) - #define PUGI__SKIPWS() { while (PUGI__IS_CHARTYPE(*s, ct_space)) ++s; } - #define PUGI__OPTSET(OPT) ( optmsk & (OPT) ) - #define PUGI__PUSHNODE(TYPE) { cursor = append_new_node(cursor, alloc, TYPE); if (!cursor) PUGI__THROW_ERROR(status_out_of_memory, s); } - #define PUGI__POPNODE() { cursor = cursor->parent; } - #define PUGI__SCANFOR(X) { while (*s != 0 && !(X)) ++s; } - #define PUGI__SCANWHILE(X) { while (X) ++s; } - #define PUGI__SCANWHILE_UNROLL(X) { for (;;) { char_t ss = s[0]; if (PUGI__UNLIKELY(!(X))) { break; } ss = s[1]; if (PUGI__UNLIKELY(!(X))) { s += 1; break; } ss = s[2]; if (PUGI__UNLIKELY(!(X))) { s += 2; break; } ss = s[3]; if (PUGI__UNLIKELY(!(X))) { s += 3; break; } s += 4; } } - #define PUGI__ENDSEG() { ch = *s; *s = 0; ++s; } - #define PUGI__THROW_ERROR(err, m) return error_offset = m, error_status = err, static_cast(0) - #define PUGI__CHECK_ERROR(err, m) { if (*s == 0) PUGI__THROW_ERROR(err, m); } - - PUGI__FN char_t* strconv_comment(char_t* s, char_t endch) - { - gap g; - - while (true) - { - PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_comment)); - - if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair - { - *s++ = '\n'; // replace first one with 0x0a - - if (*s == '\n') g.push(s, 1); - } - else if (s[0] == '-' && s[1] == '-' && PUGI__ENDSWITH(s[2], '>')) // comment ends here - { - *g.flush(s) = 0; - - return s + (s[2] == '>' ? 3 : 2); - } - else if (*s == 0) - { - return 0; - } - else ++s; - } - } - - PUGI__FN char_t* strconv_cdata(char_t* s, char_t endch) - { - gap g; - - while (true) - { - PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_cdata)); - - if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair - { - *s++ = '\n'; // replace first one with 0x0a - - if (*s == '\n') g.push(s, 1); - } - else if (s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>')) // CDATA ends here - { - *g.flush(s) = 0; - - return s + 1; - } - else if (*s == 0) - { - return 0; - } - else ++s; - } - } - - typedef char_t* (*strconv_pcdata_t)(char_t*); - - template struct strconv_pcdata_impl - { - static char_t* parse(char_t* s) - { - gap g; - - char_t* begin = s; - - while (true) - { - PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_pcdata)); - - if (*s == '<') // PCDATA ends here - { - char_t* end = g.flush(s); - - if (opt_trim::value) - while (end > begin && PUGI__IS_CHARTYPE(end[-1], ct_space)) - --end; - - *end = 0; - - return s + 1; - } - else if (opt_eol::value && *s == '\r') // Either a single 0x0d or 0x0d 0x0a pair - { - *s++ = '\n'; // replace first one with 0x0a - - if (*s == '\n') g.push(s, 1); - } - else if (opt_escape::value && *s == '&') - { - s = strconv_escape(s, g); - } - else if (*s == 0) - { - char_t* end = g.flush(s); - - if (opt_trim::value) - while (end > begin && PUGI__IS_CHARTYPE(end[-1], ct_space)) - --end; - - *end = 0; - - return s; - } - else ++s; - } - } - }; - - PUGI__FN strconv_pcdata_t get_strconv_pcdata(unsigned int optmask) - { - PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_trim_pcdata == 0x0800); - - switch (((optmask >> 4) & 3) | ((optmask >> 9) & 4)) // get bitmask for flags (eol escapes trim) - { - case 0: return strconv_pcdata_impl::parse; - case 1: return strconv_pcdata_impl::parse; - case 2: return strconv_pcdata_impl::parse; - case 3: return strconv_pcdata_impl::parse; - case 4: return strconv_pcdata_impl::parse; - case 5: return strconv_pcdata_impl::parse; - case 6: return strconv_pcdata_impl::parse; - case 7: return strconv_pcdata_impl::parse; - default: assert(false); return 0; // should not get here - } - } - - typedef char_t* (*strconv_attribute_t)(char_t*, char_t); - - template struct strconv_attribute_impl - { - static char_t* parse_wnorm(char_t* s, char_t end_quote) - { - gap g; - - // trim leading whitespaces - if (PUGI__IS_CHARTYPE(*s, ct_space)) - { - char_t* str = s; - - do ++str; - while (PUGI__IS_CHARTYPE(*str, ct_space)); - - g.push(s, str - s); - } - - while (true) - { - PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr_ws | ct_space)); - - if (*s == end_quote) - { - char_t* str = g.flush(s); - - do *str-- = 0; - while (PUGI__IS_CHARTYPE(*str, ct_space)); - - return s + 1; - } - else if (PUGI__IS_CHARTYPE(*s, ct_space)) - { - *s++ = ' '; - - if (PUGI__IS_CHARTYPE(*s, ct_space)) - { - char_t* str = s + 1; - while (PUGI__IS_CHARTYPE(*str, ct_space)) ++str; - - g.push(s, str - s); - } - } - else if (opt_escape::value && *s == '&') - { - s = strconv_escape(s, g); - } - else if (!*s) - { - return 0; - } - else ++s; - } - } - - static char_t* parse_wconv(char_t* s, char_t end_quote) - { - gap g; - - while (true) - { - PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr_ws)); - - if (*s == end_quote) - { - *g.flush(s) = 0; - - return s + 1; - } - else if (PUGI__IS_CHARTYPE(*s, ct_space)) - { - if (*s == '\r') - { - *s++ = ' '; - - if (*s == '\n') g.push(s, 1); - } - else *s++ = ' '; - } - else if (opt_escape::value && *s == '&') - { - s = strconv_escape(s, g); - } - else if (!*s) - { - return 0; - } - else ++s; - } - } - - static char_t* parse_eol(char_t* s, char_t end_quote) - { - gap g; - - while (true) - { - PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr)); - - if (*s == end_quote) - { - *g.flush(s) = 0; - - return s + 1; - } - else if (*s == '\r') - { - *s++ = '\n'; - - if (*s == '\n') g.push(s, 1); - } - else if (opt_escape::value && *s == '&') - { - s = strconv_escape(s, g); - } - else if (!*s) - { - return 0; - } - else ++s; - } - } - - static char_t* parse_simple(char_t* s, char_t end_quote) - { - gap g; - - while (true) - { - PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr)); - - if (*s == end_quote) - { - *g.flush(s) = 0; - - return s + 1; - } - else if (opt_escape::value && *s == '&') - { - s = strconv_escape(s, g); - } - else if (!*s) - { - return 0; - } - else ++s; - } - } - }; - - PUGI__FN strconv_attribute_t get_strconv_attribute(unsigned int optmask) - { - PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_wconv_attribute == 0x40 && parse_wnorm_attribute == 0x80); - - switch ((optmask >> 4) & 15) // get bitmask for flags (wconv wnorm eol escapes) - { - case 0: return strconv_attribute_impl::parse_simple; - case 1: return strconv_attribute_impl::parse_simple; - case 2: return strconv_attribute_impl::parse_eol; - case 3: return strconv_attribute_impl::parse_eol; - case 4: return strconv_attribute_impl::parse_wconv; - case 5: return strconv_attribute_impl::parse_wconv; - case 6: return strconv_attribute_impl::parse_wconv; - case 7: return strconv_attribute_impl::parse_wconv; - case 8: return strconv_attribute_impl::parse_wnorm; - case 9: return strconv_attribute_impl::parse_wnorm; - case 10: return strconv_attribute_impl::parse_wnorm; - case 11: return strconv_attribute_impl::parse_wnorm; - case 12: return strconv_attribute_impl::parse_wnorm; - case 13: return strconv_attribute_impl::parse_wnorm; - case 14: return strconv_attribute_impl::parse_wnorm; - case 15: return strconv_attribute_impl::parse_wnorm; - default: assert(false); return 0; // should not get here - } - } - - inline xml_parse_result make_parse_result(xml_parse_status status, ptrdiff_t offset = 0) - { - xml_parse_result result; - result.status = status; - result.offset = offset; - - return result; - } - - struct xml_parser - { - xml_allocator alloc; - char_t* error_offset; - xml_parse_status error_status; - - xml_parser(const xml_allocator& alloc_): alloc(alloc_), error_offset(0), error_status(status_ok) - { - } - - // DOCTYPE consists of nested sections of the following possible types: - // , , "...", '...' - // - // - // First group can not contain nested groups - // Second group can contain nested groups of the same type - // Third group can contain all other groups - char_t* parse_doctype_primitive(char_t* s) - { - if (*s == '"' || *s == '\'') - { - // quoted string - char_t ch = *s++; - PUGI__SCANFOR(*s == ch); - if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s); - - s++; - } - else if (s[0] == '<' && s[1] == '?') - { - // - s += 2; - PUGI__SCANFOR(s[0] == '?' && s[1] == '>'); // no need for ENDSWITH because ?> can't terminate proper doctype - if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s); - - s += 2; - } - else if (s[0] == '<' && s[1] == '!' && s[2] == '-' && s[3] == '-') - { - s += 4; - PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && s[2] == '>'); // no need for ENDSWITH because --> can't terminate proper doctype - if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s); - - s += 3; - } - else PUGI__THROW_ERROR(status_bad_doctype, s); - - return s; - } - - char_t* parse_doctype_ignore(char_t* s) - { - size_t depth = 0; - - assert(s[0] == '<' && s[1] == '!' && s[2] == '['); - s += 3; - - while (*s) - { - if (s[0] == '<' && s[1] == '!' && s[2] == '[') - { - // nested ignore section - s += 3; - depth++; - } - else if (s[0] == ']' && s[1] == ']' && s[2] == '>') - { - // ignore section end - s += 3; - - if (depth == 0) - return s; - - depth--; - } - else s++; - } - - PUGI__THROW_ERROR(status_bad_doctype, s); - } - - char_t* parse_doctype_group(char_t* s, char_t endch) - { - size_t depth = 0; - - assert((s[0] == '<' || s[0] == 0) && s[1] == '!'); - s += 2; - - while (*s) - { - if (s[0] == '<' && s[1] == '!' && s[2] != '-') - { - if (s[2] == '[') - { - // ignore - s = parse_doctype_ignore(s); - if (!s) return s; - } - else - { - // some control group - s += 2; - depth++; - } - } - else if (s[0] == '<' || s[0] == '"' || s[0] == '\'') - { - // unknown tag (forbidden), or some primitive group - s = parse_doctype_primitive(s); - if (!s) return s; - } - else if (*s == '>') - { - if (depth == 0) - return s; - - depth--; - s++; - } - else s++; - } - - if (depth != 0 || endch != '>') PUGI__THROW_ERROR(status_bad_doctype, s); - - return s; - } - - char_t* parse_exclamation(char_t* s, xml_node_struct* cursor, unsigned int optmsk, char_t endch) - { - // parse node contents, starting with exclamation mark - ++s; - - if (*s == '-') // 'value = s; // Save the offset. - } - - if (PUGI__OPTSET(parse_eol) && PUGI__OPTSET(parse_comments)) - { - s = strconv_comment(s, endch); - - if (!s) PUGI__THROW_ERROR(status_bad_comment, cursor->value); - } - else - { - // Scan for terminating '-->'. - PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && PUGI__ENDSWITH(s[2], '>')); - PUGI__CHECK_ERROR(status_bad_comment, s); - - if (PUGI__OPTSET(parse_comments)) - *s = 0; // Zero-terminate this segment at the first terminating '-'. - - s += (s[2] == '>' ? 3 : 2); // Step over the '\0->'. - } - } - else PUGI__THROW_ERROR(status_bad_comment, s); - } - else if (*s == '[') - { - // 'value = s; // Save the offset. - - if (PUGI__OPTSET(parse_eol)) - { - s = strconv_cdata(s, endch); - - if (!s) PUGI__THROW_ERROR(status_bad_cdata, cursor->value); - } - else - { - // Scan for terminating ']]>'. - PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>')); - PUGI__CHECK_ERROR(status_bad_cdata, s); - - *s++ = 0; // Zero-terminate this segment. - } - } - else // Flagged for discard, but we still have to scan for the terminator. - { - // Scan for terminating ']]>'. - PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>')); - PUGI__CHECK_ERROR(status_bad_cdata, s); - - ++s; - } - - s += (s[1] == '>' ? 2 : 1); // Step over the last ']>'. - } - else PUGI__THROW_ERROR(status_bad_cdata, s); - } - else if (s[0] == 'D' && s[1] == 'O' && s[2] == 'C' && s[3] == 'T' && s[4] == 'Y' && s[5] == 'P' && PUGI__ENDSWITH(s[6], 'E')) - { - s -= 2; - - if (cursor->parent) PUGI__THROW_ERROR(status_bad_doctype, s); - - char_t* mark = s + 9; - - s = parse_doctype_group(s, endch); - if (!s) return s; - - assert((*s == 0 && endch == '>') || *s == '>'); - if (*s) *s++ = 0; - - if (PUGI__OPTSET(parse_doctype)) - { - while (PUGI__IS_CHARTYPE(*mark, ct_space)) ++mark; - - PUGI__PUSHNODE(node_doctype); - - cursor->value = mark; - } - } - else if (*s == 0 && endch == '-') PUGI__THROW_ERROR(status_bad_comment, s); - else if (*s == 0 && endch == '[') PUGI__THROW_ERROR(status_bad_cdata, s); - else PUGI__THROW_ERROR(status_unrecognized_tag, s); - - return s; - } - - char_t* parse_question(char_t* s, xml_node_struct*& ref_cursor, unsigned int optmsk, char_t endch) - { - // load into registers - xml_node_struct* cursor = ref_cursor; - char_t ch = 0; - - // parse node contents, starting with question mark - ++s; - - // read PI target - char_t* target = s; - - if (!PUGI__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_pi, s); - - PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol)); - PUGI__CHECK_ERROR(status_bad_pi, s); - - // determine node type; stricmp / strcasecmp is not portable - bool declaration = (target[0] | ' ') == 'x' && (target[1] | ' ') == 'm' && (target[2] | ' ') == 'l' && target + 3 == s; - - if (declaration ? PUGI__OPTSET(parse_declaration) : PUGI__OPTSET(parse_pi)) - { - if (declaration) - { - // disallow non top-level declarations - if (cursor->parent) PUGI__THROW_ERROR(status_bad_pi, s); - - PUGI__PUSHNODE(node_declaration); - } - else - { - PUGI__PUSHNODE(node_pi); - } - - cursor->name = target; - - PUGI__ENDSEG(); - - // parse value/attributes - if (ch == '?') - { - // empty node - if (!PUGI__ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_pi, s); - s += (*s == '>'); - - PUGI__POPNODE(); - } - else if (PUGI__IS_CHARTYPE(ch, ct_space)) - { - PUGI__SKIPWS(); - - // scan for tag end - char_t* value = s; - - PUGI__SCANFOR(s[0] == '?' && PUGI__ENDSWITH(s[1], '>')); - PUGI__CHECK_ERROR(status_bad_pi, s); - - if (declaration) - { - // replace ending ? with / so that 'element' terminates properly - *s = '/'; - - // we exit from this function with cursor at node_declaration, which is a signal to parse() to go to LOC_ATTRIBUTES - s = value; - } - else - { - // store value and step over > - cursor->value = value; - PUGI__POPNODE(); - - PUGI__ENDSEG(); - - s += (*s == '>'); - } - } - else PUGI__THROW_ERROR(status_bad_pi, s); - } - else - { - // scan for tag end - PUGI__SCANFOR(s[0] == '?' && PUGI__ENDSWITH(s[1], '>')); - PUGI__CHECK_ERROR(status_bad_pi, s); - - s += (s[1] == '>' ? 2 : 1); - } - - // store from registers - ref_cursor = cursor; - - return s; - } - - char_t* parse_tree(char_t* s, xml_node_struct* root, unsigned int optmsk, char_t endch) - { - strconv_attribute_t strconv_attribute = get_strconv_attribute(optmsk); - strconv_pcdata_t strconv_pcdata = get_strconv_pcdata(optmsk); - - char_t ch = 0; - xml_node_struct* cursor = root; - char_t* mark = s; - - while (*s != 0) - { - if (*s == '<') - { - ++s; - - LOC_TAG: - if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // '<#...' - { - PUGI__PUSHNODE(node_element); // Append a new node to the tree. - - cursor->name = s; - - PUGI__SCANWHILE_UNROLL(PUGI__IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator. - PUGI__ENDSEG(); // Save char in 'ch', terminate & step over. - - if (ch == '>') - { - // end of tag - } - else if (PUGI__IS_CHARTYPE(ch, ct_space)) - { - LOC_ATTRIBUTES: - while (true) - { - PUGI__SKIPWS(); // Eat any whitespace. - - if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // <... #... - { - xml_attribute_struct* a = append_new_attribute(cursor, alloc); // Make space for this attribute. - if (!a) PUGI__THROW_ERROR(status_out_of_memory, s); - - a->name = s; // Save the offset. - - PUGI__SCANWHILE_UNROLL(PUGI__IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator. - PUGI__ENDSEG(); // Save char in 'ch', terminate & step over. - - if (PUGI__IS_CHARTYPE(ch, ct_space)) - { - PUGI__SKIPWS(); // Eat any whitespace. - - ch = *s; - ++s; - } - - if (ch == '=') // '<... #=...' - { - PUGI__SKIPWS(); // Eat any whitespace. - - if (*s == '"' || *s == '\'') // '<... #="...' - { - ch = *s; // Save quote char to avoid breaking on "''" -or- '""'. - ++s; // Step over the quote. - a->value = s; // Save the offset. - - s = strconv_attribute(s, ch); - - if (!s) PUGI__THROW_ERROR(status_bad_attribute, a->value); - - // After this line the loop continues from the start; - // Whitespaces, / and > are ok, symbols and EOF are wrong, - // everything else will be detected - if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_attribute, s); - } - else PUGI__THROW_ERROR(status_bad_attribute, s); - } - else PUGI__THROW_ERROR(status_bad_attribute, s); - } - else if (*s == '/') - { - ++s; - - if (*s == '>') - { - PUGI__POPNODE(); - s++; - break; - } - else if (*s == 0 && endch == '>') - { - PUGI__POPNODE(); - break; - } - else PUGI__THROW_ERROR(status_bad_start_element, s); - } - else if (*s == '>') - { - ++s; - - break; - } - else if (*s == 0 && endch == '>') - { - break; - } - else PUGI__THROW_ERROR(status_bad_start_element, s); - } - - // !!! - } - else if (ch == '/') // '<#.../' - { - if (!PUGI__ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_start_element, s); - - PUGI__POPNODE(); // Pop. - - s += (*s == '>'); - } - else if (ch == 0) - { - // we stepped over null terminator, backtrack & handle closing tag - --s; - - if (endch != '>') PUGI__THROW_ERROR(status_bad_start_element, s); - } - else PUGI__THROW_ERROR(status_bad_start_element, s); - } - else if (*s == '/') - { - ++s; - - char_t* name = cursor->name; - if (!name) PUGI__THROW_ERROR(status_end_element_mismatch, s); - - while (PUGI__IS_CHARTYPE(*s, ct_symbol)) - { - if (*s++ != *name++) PUGI__THROW_ERROR(status_end_element_mismatch, s); - } - - if (*name) - { - if (*s == 0 && name[0] == endch && name[1] == 0) PUGI__THROW_ERROR(status_bad_end_element, s); - else PUGI__THROW_ERROR(status_end_element_mismatch, s); - } - - PUGI__POPNODE(); // Pop. - - PUGI__SKIPWS(); - - if (*s == 0) - { - if (endch != '>') PUGI__THROW_ERROR(status_bad_end_element, s); - } - else - { - if (*s != '>') PUGI__THROW_ERROR(status_bad_end_element, s); - ++s; - } - } - else if (*s == '?') // 'first_child) continue; - } - } - - if (!PUGI__OPTSET(parse_trim_pcdata)) - s = mark; - - if (cursor->parent || PUGI__OPTSET(parse_fragment)) - { - PUGI__PUSHNODE(node_pcdata); // Append a new node on the tree. - cursor->value = s; // Save the offset. - - s = strconv_pcdata(s); - - PUGI__POPNODE(); // Pop since this is a standalone. - - if (!*s) break; - } - else - { - PUGI__SCANFOR(*s == '<'); // '...<' - if (!*s) break; - - ++s; - } - - // We're after '<' - goto LOC_TAG; - } - } - - // check that last tag is closed - if (cursor != root) PUGI__THROW_ERROR(status_end_element_mismatch, s); - - return s; - } - - #ifdef PUGIXML_WCHAR_MODE - static char_t* parse_skip_bom(char_t* s) - { - unsigned int bom = 0xfeff; - return (s[0] == static_cast(bom)) ? s + 1 : s; - } - #else - static char_t* parse_skip_bom(char_t* s) - { - return (s[0] == '\xef' && s[1] == '\xbb' && s[2] == '\xbf') ? s + 3 : s; - } - #endif - - static bool has_element_node_siblings(xml_node_struct* node) - { - while (node) - { - if (PUGI__NODETYPE(node) == node_element) return true; - - node = node->next_sibling; - } - - return false; - } - - static xml_parse_result parse(char_t* buffer, size_t length, xml_document_struct* xmldoc, xml_node_struct* root, unsigned int optmsk) - { - // allocator object is a part of document object - xml_allocator& alloc_ = *static_cast(xmldoc); - - // early-out for empty documents - if (length == 0) - return make_parse_result(PUGI__OPTSET(parse_fragment) ? status_ok : status_no_document_element); - - // get last child of the root before parsing - xml_node_struct* last_root_child = root->first_child ? root->first_child->prev_sibling_c : 0; - - // create parser on stack - xml_parser parser(alloc_); - - // save last character and make buffer zero-terminated (speeds up parsing) - char_t endch = buffer[length - 1]; - buffer[length - 1] = 0; - - // skip BOM to make sure it does not end up as part of parse output - char_t* buffer_data = parse_skip_bom(buffer); - - // perform actual parsing - parser.parse_tree(buffer_data, root, optmsk, endch); - - // update allocator state - alloc_ = parser.alloc; - - xml_parse_result result = make_parse_result(parser.error_status, parser.error_offset ? parser.error_offset - buffer : 0); - assert(result.offset >= 0 && static_cast(result.offset) <= length); - - if (result) - { - // since we removed last character, we have to handle the only possible false positive (stray <) - if (endch == '<') - return make_parse_result(status_unrecognized_tag, length - 1); - - // check if there are any element nodes parsed - xml_node_struct* first_root_child_parsed = last_root_child ? last_root_child->next_sibling : root->first_child; - - if (!PUGI__OPTSET(parse_fragment) && !has_element_node_siblings(first_root_child_parsed)) - return make_parse_result(status_no_document_element, length - 1); - } - else - { - // roll back offset if it occurs on a null terminator in the source buffer - if (result.offset > 0 && static_cast(result.offset) == length - 1 && endch == 0) - result.offset--; - } - - return result; - } - }; - - // Output facilities - PUGI__FN xml_encoding get_write_native_encoding() - { - #ifdef PUGIXML_WCHAR_MODE - return get_wchar_encoding(); - #else - return encoding_utf8; - #endif - } - - PUGI__FN xml_encoding get_write_encoding(xml_encoding encoding) - { - // replace wchar encoding with utf implementation - if (encoding == encoding_wchar) return get_wchar_encoding(); - - // replace utf16 encoding with utf16 with specific endianness - if (encoding == encoding_utf16) return is_little_endian() ? encoding_utf16_le : encoding_utf16_be; - - // replace utf32 encoding with utf32 with specific endianness - if (encoding == encoding_utf32) return is_little_endian() ? encoding_utf32_le : encoding_utf32_be; - - // only do autodetection if no explicit encoding is requested - if (encoding != encoding_auto) return encoding; - - // assume utf8 encoding - return encoding_utf8; - } - -#ifdef PUGIXML_WCHAR_MODE - PUGI__FN size_t get_valid_length(const char_t* data, size_t length) - { - if (length < 1) return 0; - - // discard last character if it's the lead of a surrogate pair - return (sizeof(wchar_t) == 2 && static_cast(static_cast(data[length - 1]) - 0xD800) < 0x400) ? length - 1 : length; - } - - PUGI__FN size_t convert_buffer_output(char_t* r_char, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding) - { - // only endian-swapping is required - if (need_endian_swap_utf(encoding, get_wchar_encoding())) - { - convert_wchar_endian_swap(r_char, data, length); - - return length * sizeof(char_t); - } - - // convert to utf8 - if (encoding == encoding_utf8) - { - uint8_t* dest = r_u8; - uint8_t* end = utf_decoder::decode_wchar_block(data, length, dest); - - return static_cast(end - dest); - } - - // convert to utf16 - if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) - { - uint16_t* dest = r_u16; - - // convert to native utf16 - uint16_t* end = utf_decoder::decode_wchar_block(data, length, dest); - - // swap if necessary - xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; - - if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast(end - dest)); - - return static_cast(end - dest) * sizeof(uint16_t); - } - - // convert to utf32 - if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) - { - uint32_t* dest = r_u32; - - // convert to native utf32 - uint32_t* end = utf_decoder::decode_wchar_block(data, length, dest); - - // swap if necessary - xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; - - if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast(end - dest)); - - return static_cast(end - dest) * sizeof(uint32_t); - } - - // convert to latin1 - if (encoding == encoding_latin1) - { - uint8_t* dest = r_u8; - uint8_t* end = utf_decoder::decode_wchar_block(data, length, dest); - - return static_cast(end - dest); - } - - assert(!"Invalid encoding"); - return 0; - } -#else - PUGI__FN size_t get_valid_length(const char_t* data, size_t length) - { - if (length < 5) return 0; - - for (size_t i = 1; i <= 4; ++i) - { - uint8_t ch = static_cast(data[length - i]); - - // either a standalone character or a leading one - if ((ch & 0xc0) != 0x80) return length - i; - } - - // there are four non-leading characters at the end, sequence tail is broken so might as well process the whole chunk - return length; - } - - PUGI__FN size_t convert_buffer_output(char_t* /* r_char */, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding) - { - if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) - { - uint16_t* dest = r_u16; - - // convert to native utf16 - uint16_t* end = utf_decoder::decode_utf8_block(reinterpret_cast(data), length, dest); - - // swap if necessary - xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; - - if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast(end - dest)); - - return static_cast(end - dest) * sizeof(uint16_t); - } - - if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) - { - uint32_t* dest = r_u32; - - // convert to native utf32 - uint32_t* end = utf_decoder::decode_utf8_block(reinterpret_cast(data), length, dest); - - // swap if necessary - xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; - - if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast(end - dest)); - - return static_cast(end - dest) * sizeof(uint32_t); - } - - if (encoding == encoding_latin1) - { - uint8_t* dest = r_u8; - uint8_t* end = utf_decoder::decode_utf8_block(reinterpret_cast(data), length, dest); - - return static_cast(end - dest); - } - - assert(!"Invalid encoding"); - return 0; - } -#endif - - class xml_buffered_writer - { - xml_buffered_writer(const xml_buffered_writer&); - xml_buffered_writer& operator=(const xml_buffered_writer&); - - public: - xml_buffered_writer(xml_writer& writer_, xml_encoding user_encoding): writer(writer_), bufsize(0), encoding(get_write_encoding(user_encoding)) - { - PUGI__STATIC_ASSERT(bufcapacity >= 8); - } - - ~xml_buffered_writer() - { - flush(); - } - - size_t flush() - { - flush(buffer, bufsize); - bufsize = 0; - return 0; - } - - void flush(const char_t* data, size_t size) - { - if (size == 0) return; - - // fast path, just write data - if (encoding == get_write_native_encoding()) - writer.write(data, size * sizeof(char_t)); - else - { - // convert chunk - size_t result = convert_buffer_output(scratch.data_char, scratch.data_u8, scratch.data_u16, scratch.data_u32, data, size, encoding); - assert(result <= sizeof(scratch)); - - // write data - writer.write(scratch.data_u8, result); - } - } - - void write_direct(const char_t* data, size_t length) - { - // flush the remaining buffer contents - flush(); - - // handle large chunks - if (length > bufcapacity) - { - if (encoding == get_write_native_encoding()) - { - // fast path, can just write data chunk - writer.write(data, length * sizeof(char_t)); - return; - } - - // need to convert in suitable chunks - while (length > bufcapacity) - { - // get chunk size by selecting such number of characters that are guaranteed to fit into scratch buffer - // and form a complete codepoint sequence (i.e. discard start of last codepoint if necessary) - size_t chunk_size = get_valid_length(data, bufcapacity); - assert(chunk_size); - - // convert chunk and write - flush(data, chunk_size); - - // iterate - data += chunk_size; - length -= chunk_size; - } - - // small tail is copied below - bufsize = 0; - } - - memcpy(buffer + bufsize, data, length * sizeof(char_t)); - bufsize += length; - } - - void write_buffer(const char_t* data, size_t length) - { - size_t offset = bufsize; - - if (offset + length <= bufcapacity) - { - memcpy(buffer + offset, data, length * sizeof(char_t)); - bufsize = offset + length; - } - else - { - write_direct(data, length); - } - } - - void write_string(const char_t* data) - { - // write the part of the string that fits in the buffer - size_t offset = bufsize; - - while (*data && offset < bufcapacity) - buffer[offset++] = *data++; - - // write the rest - if (offset < bufcapacity) - { - bufsize = offset; - } - else - { - // backtrack a bit if we have split the codepoint - size_t length = offset - bufsize; - size_t extra = length - get_valid_length(data - length, length); - - bufsize = offset - extra; - - write_direct(data - extra, strlength(data) + extra); - } - } - - void write(char_t d0) - { - size_t offset = bufsize; - if (offset > bufcapacity - 1) offset = flush(); - - buffer[offset + 0] = d0; - bufsize = offset + 1; - } - - void write(char_t d0, char_t d1) - { - size_t offset = bufsize; - if (offset > bufcapacity - 2) offset = flush(); - - buffer[offset + 0] = d0; - buffer[offset + 1] = d1; - bufsize = offset + 2; - } - - void write(char_t d0, char_t d1, char_t d2) - { - size_t offset = bufsize; - if (offset > bufcapacity - 3) offset = flush(); - - buffer[offset + 0] = d0; - buffer[offset + 1] = d1; - buffer[offset + 2] = d2; - bufsize = offset + 3; - } - - void write(char_t d0, char_t d1, char_t d2, char_t d3) - { - size_t offset = bufsize; - if (offset > bufcapacity - 4) offset = flush(); - - buffer[offset + 0] = d0; - buffer[offset + 1] = d1; - buffer[offset + 2] = d2; - buffer[offset + 3] = d3; - bufsize = offset + 4; - } - - void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4) - { - size_t offset = bufsize; - if (offset > bufcapacity - 5) offset = flush(); - - buffer[offset + 0] = d0; - buffer[offset + 1] = d1; - buffer[offset + 2] = d2; - buffer[offset + 3] = d3; - buffer[offset + 4] = d4; - bufsize = offset + 5; - } - - void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4, char_t d5) - { - size_t offset = bufsize; - if (offset > bufcapacity - 6) offset = flush(); - - buffer[offset + 0] = d0; - buffer[offset + 1] = d1; - buffer[offset + 2] = d2; - buffer[offset + 3] = d3; - buffer[offset + 4] = d4; - buffer[offset + 5] = d5; - bufsize = offset + 6; - } - - // utf8 maximum expansion: x4 (-> utf32) - // utf16 maximum expansion: x2 (-> utf32) - // utf32 maximum expansion: x1 - enum - { - bufcapacitybytes = - #ifdef PUGIXML_MEMORY_OUTPUT_STACK - PUGIXML_MEMORY_OUTPUT_STACK - #else - 10240 - #endif - , - bufcapacity = bufcapacitybytes / (sizeof(char_t) + 4) - }; - - char_t buffer[bufcapacity]; - - union - { - uint8_t data_u8[4 * bufcapacity]; - uint16_t data_u16[2 * bufcapacity]; - uint32_t data_u32[bufcapacity]; - char_t data_char[bufcapacity]; - } scratch; - - xml_writer& writer; - size_t bufsize; - xml_encoding encoding; - }; - - PUGI__FN void text_output_escaped(xml_buffered_writer& writer, const char_t* s, chartypex_t type) - { - while (*s) - { - const char_t* prev = s; - - // While *s is a usual symbol - PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPEX(ss, type)); - - writer.write_buffer(prev, static_cast(s - prev)); - - switch (*s) - { - case 0: break; - case '&': - writer.write('&', 'a', 'm', 'p', ';'); - ++s; - break; - case '<': - writer.write('&', 'l', 't', ';'); - ++s; - break; - case '>': - writer.write('&', 'g', 't', ';'); - ++s; - break; - case '"': - writer.write('&', 'q', 'u', 'o', 't', ';'); - ++s; - break; - default: // s is not a usual symbol - { - unsigned int ch = static_cast(*s++); - assert(ch < 32); - - writer.write('&', '#', static_cast((ch / 10) + '0'), static_cast((ch % 10) + '0'), ';'); - } - } - } - } - - PUGI__FN void text_output(xml_buffered_writer& writer, const char_t* s, chartypex_t type, unsigned int flags) - { - if (flags & format_no_escapes) - writer.write_string(s); - else - text_output_escaped(writer, s, type); - } - - PUGI__FN void text_output_cdata(xml_buffered_writer& writer, const char_t* s) - { - do - { - writer.write('<', '!', '[', 'C', 'D'); - writer.write('A', 'T', 'A', '['); - - const char_t* prev = s; - - // look for ]]> sequence - we can't output it as is since it terminates CDATA - while (*s && !(s[0] == ']' && s[1] == ']' && s[2] == '>')) ++s; - - // skip ]] if we stopped at ]]>, > will go to the next CDATA section - if (*s) s += 2; - - writer.write_buffer(prev, static_cast(s - prev)); - - writer.write(']', ']', '>'); - } - while (*s); - } - - PUGI__FN void text_output_indent(xml_buffered_writer& writer, const char_t* indent, size_t indent_length, unsigned int depth) - { - switch (indent_length) - { - case 1: - { - for (unsigned int i = 0; i < depth; ++i) - writer.write(indent[0]); - break; - } - - case 2: - { - for (unsigned int i = 0; i < depth; ++i) - writer.write(indent[0], indent[1]); - break; - } - - case 3: - { - for (unsigned int i = 0; i < depth; ++i) - writer.write(indent[0], indent[1], indent[2]); - break; - } - - case 4: - { - for (unsigned int i = 0; i < depth; ++i) - writer.write(indent[0], indent[1], indent[2], indent[3]); - break; - } - - default: - { - for (unsigned int i = 0; i < depth; ++i) - writer.write_buffer(indent, indent_length); - } - } - } - - PUGI__FN void node_output_comment(xml_buffered_writer& writer, const char_t* s) - { - writer.write('<', '!', '-', '-'); - - while (*s) - { - const char_t* prev = s; - - // look for -\0 or -- sequence - we can't output it since -- is illegal in comment body - while (*s && !(s[0] == '-' && (s[1] == '-' || s[1] == 0))) ++s; - - writer.write_buffer(prev, static_cast(s - prev)); - - if (*s) - { - assert(*s == '-'); - - writer.write('-', ' '); - ++s; - } - } - - writer.write('-', '-', '>'); - } - - PUGI__FN void node_output_pi_value(xml_buffered_writer& writer, const char_t* s) - { - while (*s) - { - const char_t* prev = s; - - // look for ?> sequence - we can't output it since ?> terminates PI - while (*s && !(s[0] == '?' && s[1] == '>')) ++s; - - writer.write_buffer(prev, static_cast(s - prev)); - - if (*s) - { - assert(s[0] == '?' && s[1] == '>'); - - writer.write('?', ' ', '>'); - s += 2; - } - } - } - - PUGI__FN void node_output_attributes(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags) - { - const char_t* default_name = PUGIXML_TEXT(":anonymous"); - - for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute) - { - writer.write(' '); - writer.write_string(a->name ? a->name : default_name); - writer.write('=', '"'); - - if (a->value) - text_output(writer, a->value, ctx_special_attr, flags); - - writer.write('"'); - } - } - - PUGI__FN bool node_output_start(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags) - { - const char_t* default_name = PUGIXML_TEXT(":anonymous"); - const char_t* name = node->name ? node->name : default_name; - - writer.write('<'); - writer.write_string(name); - - if (node->first_attribute) - node_output_attributes(writer, node, flags); - - if (!node->first_child) - { - writer.write(' ', '/', '>'); - - return false; - } - else - { - writer.write('>'); - - return true; - } - } - - PUGI__FN void node_output_end(xml_buffered_writer& writer, xml_node_struct* node) - { - const char_t* default_name = PUGIXML_TEXT(":anonymous"); - const char_t* name = node->name ? node->name : default_name; - - writer.write('<', '/'); - writer.write_string(name); - writer.write('>'); - } - - PUGI__FN void node_output_simple(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags) - { - const char_t* default_name = PUGIXML_TEXT(":anonymous"); - - switch (PUGI__NODETYPE(node)) - { - case node_pcdata: - text_output(writer, node->value ? node->value : PUGIXML_TEXT(""), ctx_special_pcdata, flags); - break; - - case node_cdata: - text_output_cdata(writer, node->value ? node->value : PUGIXML_TEXT("")); - break; - - case node_comment: - node_output_comment(writer, node->value ? node->value : PUGIXML_TEXT("")); - break; - - case node_pi: - writer.write('<', '?'); - writer.write_string(node->name ? node->name : default_name); - - if (node->value) - { - writer.write(' '); - node_output_pi_value(writer, node->value); - } - - writer.write('?', '>'); - break; - - case node_declaration: - writer.write('<', '?'); - writer.write_string(node->name ? node->name : default_name); - node_output_attributes(writer, node, flags); - writer.write('?', '>'); - break; - - case node_doctype: - writer.write('<', '!', 'D', 'O', 'C'); - writer.write('T', 'Y', 'P', 'E'); - - if (node->value) - { - writer.write(' '); - writer.write_string(node->value); - } - - writer.write('>'); - break; - - default: - assert(!"Invalid node type"); - } - } - - enum indent_flags_t - { - indent_newline = 1, - indent_indent = 2 - }; - - PUGI__FN void node_output(xml_buffered_writer& writer, xml_node_struct* root, const char_t* indent, unsigned int flags, unsigned int depth) - { - size_t indent_length = ((flags & (format_indent | format_raw)) == format_indent) ? strlength(indent) : 0; - unsigned int indent_flags = indent_indent; - - xml_node_struct* node = root; - - do - { - assert(node); - - // begin writing current node - if (PUGI__NODETYPE(node) == node_pcdata || PUGI__NODETYPE(node) == node_cdata) - { - node_output_simple(writer, node, flags); - - indent_flags = 0; - } - else - { - if ((indent_flags & indent_newline) && (flags & format_raw) == 0) - writer.write('\n'); - - if ((indent_flags & indent_indent) && indent_length) - text_output_indent(writer, indent, indent_length, depth); - - if (PUGI__NODETYPE(node) == node_element) - { - indent_flags = indent_newline | indent_indent; - - if (node_output_start(writer, node, flags)) - { - node = node->first_child; - depth++; - continue; - } - } - else if (PUGI__NODETYPE(node) == node_document) - { - indent_flags = indent_indent; - - if (node->first_child) - { - node = node->first_child; - continue; - } - } - else - { - node_output_simple(writer, node, flags); - - indent_flags = indent_newline | indent_indent; - } - } - - // continue to the next node - while (node != root) - { - if (node->next_sibling) - { - node = node->next_sibling; - break; - } - - node = node->parent; - - // write closing node - if (PUGI__NODETYPE(node) == node_element) - { - depth--; - - if ((indent_flags & indent_newline) && (flags & format_raw) == 0) - writer.write('\n'); - - if ((indent_flags & indent_indent) && indent_length) - text_output_indent(writer, indent, indent_length, depth); - - node_output_end(writer, node); - - indent_flags = indent_newline | indent_indent; - } - } - } - while (node != root); - - if ((indent_flags & indent_newline) && (flags & format_raw) == 0) - writer.write('\n'); - } - - PUGI__FN bool has_declaration(xml_node_struct* node) - { - for (xml_node_struct* child = node->first_child; child; child = child->next_sibling) - { - xml_node_type type = PUGI__NODETYPE(child); - - if (type == node_declaration) return true; - if (type == node_element) return false; - } - - return false; - } - - PUGI__FN bool is_attribute_of(xml_attribute_struct* attr, xml_node_struct* node) - { - for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute) - if (a == attr) - return true; - - return false; - } - - PUGI__FN bool allow_insert_attribute(xml_node_type parent) - { - return parent == node_element || parent == node_declaration; - } - - PUGI__FN bool allow_insert_child(xml_node_type parent, xml_node_type child) - { - if (parent != node_document && parent != node_element) return false; - if (child == node_document || child == node_null) return false; - if (parent != node_document && (child == node_declaration || child == node_doctype)) return false; - - return true; - } - - PUGI__FN bool allow_move(xml_node parent, xml_node child) - { - // check that child can be a child of parent - if (!allow_insert_child(parent.type(), child.type())) - return false; - - // check that node is not moved between documents - if (parent.root() != child.root()) - return false; - - // check that new parent is not in the child subtree - xml_node cur = parent; - - while (cur) - { - if (cur == child) - return false; - - cur = cur.parent(); - } - - return true; - } - - PUGI__FN void node_copy_string(char_t*& dest, uintptr_t& header, uintptr_t header_mask, char_t* source, uintptr_t& source_header, xml_allocator* alloc) - { - assert(!dest && (header & header_mask) == 0); - - if (source) - { - if (alloc && (source_header & header_mask) == 0) - { - dest = source; - - // since strcpy_insitu can reuse document buffer memory we need to mark both source and dest as shared - header |= xml_memory_page_contents_shared_mask; - source_header |= xml_memory_page_contents_shared_mask; - } - else - strcpy_insitu(dest, header, header_mask, source); - } - } - - PUGI__FN void node_copy_contents(xml_node_struct* dn, xml_node_struct* sn, xml_allocator* shared_alloc) - { - node_copy_string(dn->name, dn->header, xml_memory_page_name_allocated_mask, sn->name, sn->header, shared_alloc); - node_copy_string(dn->value, dn->header, xml_memory_page_value_allocated_mask, sn->value, sn->header, shared_alloc); - - for (xml_attribute_struct* sa = sn->first_attribute; sa; sa = sa->next_attribute) - { - xml_attribute_struct* da = append_new_attribute(dn, get_allocator(dn)); - - if (da) - { - node_copy_string(da->name, da->header, xml_memory_page_name_allocated_mask, sa->name, sa->header, shared_alloc); - node_copy_string(da->value, da->header, xml_memory_page_value_allocated_mask, sa->value, sa->header, shared_alloc); - } - } - } - - PUGI__FN void node_copy_tree(xml_node_struct* dn, xml_node_struct* sn) - { - xml_allocator& alloc = get_allocator(dn); - xml_allocator* shared_alloc = (&alloc == &get_allocator(sn)) ? &alloc : 0; - - node_copy_contents(dn, sn, shared_alloc); - - xml_node_struct* dit = dn; - xml_node_struct* sit = sn->first_child; - - while (sit && sit != sn) - { - if (sit != dn) - { - xml_node_struct* copy = append_new_node(dit, alloc, PUGI__NODETYPE(sit)); - - if (copy) - { - node_copy_contents(copy, sit, shared_alloc); - - if (sit->first_child) - { - dit = copy; - sit = sit->first_child; - continue; - } - } - } - - // continue to the next node - do - { - if (sit->next_sibling) - { - sit = sit->next_sibling; - break; - } - - sit = sit->parent; - dit = dit->parent; - } - while (sit != sn); - } - } - - inline bool is_text_node(xml_node_struct* node) - { - xml_node_type type = PUGI__NODETYPE(node); - - return type == node_pcdata || type == node_cdata; - } - - // get value with conversion functions - PUGI__FN int get_integer_base(const char_t* value) - { - const char_t* s = value; - - while (PUGI__IS_CHARTYPE(*s, ct_space)) - s++; - - if (*s == '-') - s++; - - return (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) ? 16 : 10; - } - - PUGI__FN int get_value_int(const char_t* value, int def) - { - if (!value) return def; - - int base = get_integer_base(value); - - #ifdef PUGIXML_WCHAR_MODE - return static_cast(wcstol(value, 0, base)); - #else - return static_cast(strtol(value, 0, base)); - #endif - } - - PUGI__FN unsigned int get_value_uint(const char_t* value, unsigned int def) - { - if (!value) return def; - - int base = get_integer_base(value); - - #ifdef PUGIXML_WCHAR_MODE - return static_cast(wcstoul(value, 0, base)); - #else - return static_cast(strtoul(value, 0, base)); - #endif - } - - PUGI__FN double get_value_double(const char_t* value, double def) - { - if (!value) return def; - - #ifdef PUGIXML_WCHAR_MODE - return wcstod(value, 0); - #else - return strtod(value, 0); - #endif - } - - PUGI__FN float get_value_float(const char_t* value, float def) - { - if (!value) return def; - - #ifdef PUGIXML_WCHAR_MODE - return static_cast(wcstod(value, 0)); - #else - return static_cast(strtod(value, 0)); - #endif - } - - PUGI__FN bool get_value_bool(const char_t* value, bool def) - { - if (!value) return def; - - // only look at first char - char_t first = *value; - - // 1*, t* (true), T* (True), y* (yes), Y* (YES) - return (first == '1' || first == 't' || first == 'T' || first == 'y' || first == 'Y'); - } - -#ifdef PUGIXML_HAS_LONG_LONG - PUGI__FN long long get_value_llong(const char_t* value, long long def) - { - if (!value) return def; - - int base = get_integer_base(value); - - #ifdef PUGIXML_WCHAR_MODE - #ifdef PUGI__MSVC_CRT_VERSION - return _wcstoi64(value, 0, base); - #else - return wcstoll(value, 0, base); - #endif - #else - #ifdef PUGI__MSVC_CRT_VERSION - return _strtoi64(value, 0, base); - #else - return strtoll(value, 0, base); - #endif - #endif - } - - PUGI__FN unsigned long long get_value_ullong(const char_t* value, unsigned long long def) - { - if (!value) return def; - - int base = get_integer_base(value); - - #ifdef PUGIXML_WCHAR_MODE - #ifdef PUGI__MSVC_CRT_VERSION - return _wcstoui64(value, 0, base); - #else - return wcstoull(value, 0, base); - #endif - #else - #ifdef PUGI__MSVC_CRT_VERSION - return _strtoui64(value, 0, base); - #else - return strtoull(value, 0, base); - #endif - #endif - } -#endif - - // set value with conversion functions - PUGI__FN bool set_value_buffer(char_t*& dest, uintptr_t& header, uintptr_t header_mask, char (&buf)[128]) - { - #ifdef PUGIXML_WCHAR_MODE - char_t wbuf[128]; - impl::widen_ascii(wbuf, buf); - - return strcpy_insitu(dest, header, header_mask, wbuf); - #else - return strcpy_insitu(dest, header, header_mask, buf); - #endif - } - - PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, int value) - { - char buf[128]; - sprintf(buf, "%d", value); - - return set_value_buffer(dest, header, header_mask, buf); - } - - PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, unsigned int value) - { - char buf[128]; - sprintf(buf, "%u", value); - - return set_value_buffer(dest, header, header_mask, buf); - } - - PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, float value) - { - char buf[128]; - sprintf(buf, "%.9g", value); - - return set_value_buffer(dest, header, header_mask, buf); - } - - PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, double value) - { - char buf[128]; - sprintf(buf, "%.17g", value); - - return set_value_buffer(dest, header, header_mask, buf); - } - - PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, bool value) - { - return strcpy_insitu(dest, header, header_mask, value ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false")); - } - -#ifdef PUGIXML_HAS_LONG_LONG - PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, long long value) - { - char buf[128]; - sprintf(buf, "%lld", value); - - return set_value_buffer(dest, header, header_mask, buf); - } - - PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, unsigned long long value) - { - char buf[128]; - sprintf(buf, "%llu", value); - - return set_value_buffer(dest, header, header_mask, buf); - } -#endif - - // we need to get length of entire file to load it in memory; the only (relatively) sane way to do it is via seek/tell trick - PUGI__FN xml_parse_status get_file_size(FILE* file, size_t& out_result) - { - #if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE) - // there are 64-bit versions of fseek/ftell, let's use them - typedef __int64 length_type; - - _fseeki64(file, 0, SEEK_END); - length_type length = _ftelli64(file); - _fseeki64(file, 0, SEEK_SET); - #elif defined(__MINGW32__) && !defined(__NO_MINGW_LFS) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR)) - // there are 64-bit versions of fseek/ftell, let's use them - typedef off64_t length_type; - - fseeko64(file, 0, SEEK_END); - length_type length = ftello64(file); - fseeko64(file, 0, SEEK_SET); - #else - // if this is a 32-bit OS, long is enough; if this is a unix system, long is 64-bit, which is enough; otherwise we can't do anything anyway. - typedef long length_type; - - fseek(file, 0, SEEK_END); - length_type length = ftell(file); - fseek(file, 0, SEEK_SET); - #endif - - // check for I/O errors - if (length < 0) return status_io_error; - - // check for overflow - size_t result = static_cast(length); - - if (static_cast(result) != length) return status_out_of_memory; - - // finalize - out_result = result; - - return status_ok; - } - - PUGI__FN size_t zero_terminate_buffer(void* buffer, size_t size, xml_encoding encoding) - { - // We only need to zero-terminate if encoding conversion does not do it for us - #ifdef PUGIXML_WCHAR_MODE - xml_encoding wchar_encoding = get_wchar_encoding(); - - if (encoding == wchar_encoding || need_endian_swap_utf(encoding, wchar_encoding)) - { - size_t length = size / sizeof(char_t); - - static_cast(buffer)[length] = 0; - return (length + 1) * sizeof(char_t); - } - #else - if (encoding == encoding_utf8) - { - static_cast(buffer)[size] = 0; - return size + 1; - } - #endif - - return size; - } - - PUGI__FN xml_parse_result load_file_impl(xml_document& doc, FILE* file, unsigned int options, xml_encoding encoding) - { - if (!file) return make_parse_result(status_file_not_found); - - // get file size (can result in I/O errors) - size_t size = 0; - xml_parse_status size_status = get_file_size(file, size); - - if (size_status != status_ok) - { - fclose(file); - return make_parse_result(size_status); - } - - size_t max_suffix_size = sizeof(char_t); - - // allocate buffer for the whole file - char* contents = static_cast(xml_memory::allocate(size + max_suffix_size)); - - if (!contents) - { - fclose(file); - return make_parse_result(status_out_of_memory); - } - - // read file in memory - size_t read_size = fread(contents, 1, size, file); - fclose(file); - - if (read_size != size) - { - xml_memory::deallocate(contents); - return make_parse_result(status_io_error); - } - - xml_encoding real_encoding = get_buffer_encoding(encoding, contents, size); - - return doc.load_buffer_inplace_own(contents, zero_terminate_buffer(contents, size, real_encoding), options, real_encoding); - } - -#ifndef PUGIXML_NO_STL - template struct xml_stream_chunk - { - static xml_stream_chunk* create() - { - void* memory = xml_memory::allocate(sizeof(xml_stream_chunk)); - - return new (memory) xml_stream_chunk(); - } - - static void destroy(void* ptr) - { - xml_stream_chunk* chunk = static_cast(ptr); - - // free chunk chain - while (chunk) - { - xml_stream_chunk* next_ = chunk->next; - - xml_memory::deallocate(chunk); - - chunk = next_; - } - } - - xml_stream_chunk(): next(0), size(0) - { - } - - xml_stream_chunk* next; - size_t size; - - T data[xml_memory_page_size / sizeof(T)]; - }; - - template PUGI__FN xml_parse_status load_stream_data_noseek(std::basic_istream& stream, void** out_buffer, size_t* out_size) - { - buffer_holder chunks(0, xml_stream_chunk::destroy); - - // read file to a chunk list - size_t total = 0; - xml_stream_chunk* last = 0; - - while (!stream.eof()) - { - // allocate new chunk - xml_stream_chunk* chunk = xml_stream_chunk::create(); - if (!chunk) return status_out_of_memory; - - // append chunk to list - if (last) last = last->next = chunk; - else chunks.data = last = chunk; - - // read data to chunk - stream.read(chunk->data, static_cast(sizeof(chunk->data) / sizeof(T))); - chunk->size = static_cast(stream.gcount()) * sizeof(T); - - // read may set failbit | eofbit in case gcount() is less than read length, so check for other I/O errors - if (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error; - - // guard against huge files (chunk size is small enough to make this overflow check work) - if (total + chunk->size < total) return status_out_of_memory; - total += chunk->size; - } - - size_t max_suffix_size = sizeof(char_t); - - // copy chunk list to a contiguous buffer - char* buffer = static_cast(xml_memory::allocate(total + max_suffix_size)); - if (!buffer) return status_out_of_memory; - - char* write = buffer; - - for (xml_stream_chunk* chunk = static_cast*>(chunks.data); chunk; chunk = chunk->next) - { - assert(write + chunk->size <= buffer + total); - memcpy(write, chunk->data, chunk->size); - write += chunk->size; - } - - assert(write == buffer + total); - - // return buffer - *out_buffer = buffer; - *out_size = total; - - return status_ok; - } - - template PUGI__FN xml_parse_status load_stream_data_seek(std::basic_istream& stream, void** out_buffer, size_t* out_size) - { - // get length of remaining data in stream - typename std::basic_istream::pos_type pos = stream.tellg(); - stream.seekg(0, std::ios::end); - std::streamoff length = stream.tellg() - pos; - stream.seekg(pos); - - if (stream.fail() || pos < 0) return status_io_error; - - // guard against huge files - size_t read_length = static_cast(length); - - if (static_cast(read_length) != length || length < 0) return status_out_of_memory; - - size_t max_suffix_size = sizeof(char_t); - - // read stream data into memory (guard against stream exceptions with buffer holder) - buffer_holder buffer(xml_memory::allocate(read_length * sizeof(T) + max_suffix_size), xml_memory::deallocate); - if (!buffer.data) return status_out_of_memory; - - stream.read(static_cast(buffer.data), static_cast(read_length)); - - // read may set failbit | eofbit in case gcount() is less than read_length (i.e. line ending conversion), so check for other I/O errors - if (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error; - - // return buffer - size_t actual_length = static_cast(stream.gcount()); - assert(actual_length <= read_length); - - *out_buffer = buffer.release(); - *out_size = actual_length * sizeof(T); - - return status_ok; - } - - template PUGI__FN xml_parse_result load_stream_impl(xml_document& doc, std::basic_istream& stream, unsigned int options, xml_encoding encoding) - { - void* buffer = 0; - size_t size = 0; - xml_parse_status status = status_ok; - - // if stream has an error bit set, bail out (otherwise tellg() can fail and we'll clear error bits) - if (stream.fail()) return make_parse_result(status_io_error); - - // load stream to memory (using seek-based implementation if possible, since it's faster and takes less memory) - if (stream.tellg() < 0) - { - stream.clear(); // clear error flags that could be set by a failing tellg - status = load_stream_data_noseek(stream, &buffer, &size); - } - else - status = load_stream_data_seek(stream, &buffer, &size); - - if (status != status_ok) return make_parse_result(status); - - xml_encoding real_encoding = get_buffer_encoding(encoding, buffer, size); - - return doc.load_buffer_inplace_own(buffer, zero_terminate_buffer(buffer, size, real_encoding), options, real_encoding); - } -#endif - -#if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) || (defined(__MINGW32__) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR))) - PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode) - { - return _wfopen(path, mode); - } -#else - PUGI__FN char* convert_path_heap(const wchar_t* str) - { - assert(str); - - // first pass: get length in utf8 characters - size_t length = strlength_wide(str); - size_t size = as_utf8_begin(str, length); - - // allocate resulting string - char* result = static_cast(xml_memory::allocate(size + 1)); - if (!result) return 0; - - // second pass: convert to utf8 - as_utf8_end(result, size, str, length); - - return result; - } - - PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode) - { - // there is no standard function to open wide paths, so our best bet is to try utf8 path - char* path_utf8 = convert_path_heap(path); - if (!path_utf8) return 0; - - // convert mode to ASCII (we mirror _wfopen interface) - char mode_ascii[4] = {0}; - for (size_t i = 0; mode[i]; ++i) mode_ascii[i] = static_cast(mode[i]); - - // try to open the utf8 path - FILE* result = fopen(path_utf8, mode_ascii); - - // free dummy buffer - xml_memory::deallocate(path_utf8); - - return result; - } -#endif - - PUGI__FN bool save_file_impl(const xml_document& doc, FILE* file, const char_t* indent, unsigned int flags, xml_encoding encoding) - { - if (!file) return false; - - xml_writer_file writer(file); - doc.save(writer, indent, flags, encoding); - - int result = ferror(file); - - fclose(file); - - return result == 0; - } - - PUGI__FN xml_parse_result load_buffer_impl(xml_document_struct* doc, xml_node_struct* root, void* contents, size_t size, unsigned int options, xml_encoding encoding, bool is_mutable, bool own, char_t** out_buffer) - { - // check input buffer - if (!contents && size) return make_parse_result(status_io_error); - - // get actual encoding - xml_encoding buffer_encoding = impl::get_buffer_encoding(encoding, contents, size); - - // get private buffer - char_t* buffer = 0; - size_t length = 0; - - if (!impl::convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) return impl::make_parse_result(status_out_of_memory); - - // delete original buffer if we performed a conversion - if (own && buffer != contents && contents) impl::xml_memory::deallocate(contents); - - // store buffer for offset_debug - doc->buffer = buffer; - - // parse - xml_parse_result res = impl::xml_parser::parse(buffer, length, doc, root, options); - - // remember encoding - res.encoding = buffer_encoding; - - // grab onto buffer if it's our buffer, user is responsible for deallocating contents himself - if (own || buffer != contents) *out_buffer = buffer; - - return res; - } -PUGI__NS_END - -namespace pugi -{ - PUGI__FN xml_writer_file::xml_writer_file(void* file_): file(file_) - { - } - - PUGI__FN void xml_writer_file::write(const void* data, size_t size) - { - size_t result = fwrite(data, 1, size, static_cast(file)); - (void)!result; // unfortunately we can't do proper error handling here - } - -#ifndef PUGIXML_NO_STL - PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream >& stream): narrow_stream(&stream), wide_stream(0) - { - } - - PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream >& stream): narrow_stream(0), wide_stream(&stream) - { - } - - PUGI__FN void xml_writer_stream::write(const void* data, size_t size) - { - if (narrow_stream) - { - assert(!wide_stream); - narrow_stream->write(reinterpret_cast(data), static_cast(size)); - } - else - { - assert(wide_stream); - assert(size % sizeof(wchar_t) == 0); - - wide_stream->write(reinterpret_cast(data), static_cast(size / sizeof(wchar_t))); - } - } -#endif - - PUGI__FN xml_tree_walker::xml_tree_walker(): _depth(0) - { - } - - PUGI__FN xml_tree_walker::~xml_tree_walker() - { - } - - PUGI__FN int xml_tree_walker::depth() const - { - return _depth; - } - - PUGI__FN bool xml_tree_walker::begin(xml_node&) - { - return true; - } - - PUGI__FN bool xml_tree_walker::end(xml_node&) - { - return true; - } - - PUGI__FN xml_attribute::xml_attribute(): _attr(0) - { - } - - PUGI__FN xml_attribute::xml_attribute(xml_attribute_struct* attr): _attr(attr) - { - } - - PUGI__FN static void unspecified_bool_xml_attribute(xml_attribute***) - { - } - - PUGI__FN xml_attribute::operator xml_attribute::unspecified_bool_type() const - { - return _attr ? unspecified_bool_xml_attribute : 0; - } - - PUGI__FN bool xml_attribute::operator!() const - { - return !_attr; - } - - PUGI__FN bool xml_attribute::operator==(const xml_attribute& r) const - { - return (_attr == r._attr); - } - - PUGI__FN bool xml_attribute::operator!=(const xml_attribute& r) const - { - return (_attr != r._attr); - } - - PUGI__FN bool xml_attribute::operator<(const xml_attribute& r) const - { - return (_attr < r._attr); - } - - PUGI__FN bool xml_attribute::operator>(const xml_attribute& r) const - { - return (_attr > r._attr); - } - - PUGI__FN bool xml_attribute::operator<=(const xml_attribute& r) const - { - return (_attr <= r._attr); - } - - PUGI__FN bool xml_attribute::operator>=(const xml_attribute& r) const - { - return (_attr >= r._attr); - } - - PUGI__FN xml_attribute xml_attribute::next_attribute() const - { - return _attr ? xml_attribute(_attr->next_attribute) : xml_attribute(); - } - - PUGI__FN xml_attribute xml_attribute::previous_attribute() const - { - return _attr && _attr->prev_attribute_c->next_attribute ? xml_attribute(_attr->prev_attribute_c) : xml_attribute(); - } - - PUGI__FN const char_t* xml_attribute::as_string(const char_t* def) const - { - return (_attr && _attr->value) ? _attr->value : def; - } - - PUGI__FN int xml_attribute::as_int(int def) const - { - return impl::get_value_int(_attr ? _attr->value : 0, def); - } - - PUGI__FN unsigned int xml_attribute::as_uint(unsigned int def) const - { - return impl::get_value_uint(_attr ? _attr->value : 0, def); - } - - PUGI__FN double xml_attribute::as_double(double def) const - { - return impl::get_value_double(_attr ? _attr->value : 0, def); - } - - PUGI__FN float xml_attribute::as_float(float def) const - { - return impl::get_value_float(_attr ? _attr->value : 0, def); - } - - PUGI__FN bool xml_attribute::as_bool(bool def) const - { - return impl::get_value_bool(_attr ? _attr->value : 0, def); - } - -#ifdef PUGIXML_HAS_LONG_LONG - PUGI__FN long long xml_attribute::as_llong(long long def) const - { - return impl::get_value_llong(_attr ? _attr->value : 0, def); - } - - PUGI__FN unsigned long long xml_attribute::as_ullong(unsigned long long def) const - { - return impl::get_value_ullong(_attr ? _attr->value : 0, def); - } -#endif - - PUGI__FN bool xml_attribute::empty() const - { - return !_attr; - } - - PUGI__FN const char_t* xml_attribute::name() const - { - return (_attr && _attr->name) ? _attr->name : PUGIXML_TEXT(""); - } - - PUGI__FN const char_t* xml_attribute::value() const - { - return (_attr && _attr->value) ? _attr->value : PUGIXML_TEXT(""); - } - - PUGI__FN size_t xml_attribute::hash_value() const - { - return static_cast(reinterpret_cast(_attr) / sizeof(xml_attribute_struct)); - } - - PUGI__FN xml_attribute_struct* xml_attribute::internal_object() const - { - return _attr; - } - - PUGI__FN xml_attribute& xml_attribute::operator=(const char_t* rhs) - { - set_value(rhs); - return *this; - } - - PUGI__FN xml_attribute& xml_attribute::operator=(int rhs) - { - set_value(rhs); - return *this; - } - - PUGI__FN xml_attribute& xml_attribute::operator=(unsigned int rhs) - { - set_value(rhs); - return *this; - } - - PUGI__FN xml_attribute& xml_attribute::operator=(double rhs) - { - set_value(rhs); - return *this; - } - - PUGI__FN xml_attribute& xml_attribute::operator=(float rhs) - { - set_value(rhs); - return *this; - } - - PUGI__FN xml_attribute& xml_attribute::operator=(bool rhs) - { - set_value(rhs); - return *this; - } - -#ifdef PUGIXML_HAS_LONG_LONG - PUGI__FN xml_attribute& xml_attribute::operator=(long long rhs) - { - set_value(rhs); - return *this; - } - - PUGI__FN xml_attribute& xml_attribute::operator=(unsigned long long rhs) - { - set_value(rhs); - return *this; - } -#endif - - PUGI__FN bool xml_attribute::set_name(const char_t* rhs) - { - if (!_attr) return false; - - return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs); - } - - PUGI__FN bool xml_attribute::set_value(const char_t* rhs) - { - if (!_attr) return false; - - return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); - } - - PUGI__FN bool xml_attribute::set_value(int rhs) - { - if (!_attr) return false; - - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); - } - - PUGI__FN bool xml_attribute::set_value(unsigned int rhs) - { - if (!_attr) return false; - - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); - } - - PUGI__FN bool xml_attribute::set_value(double rhs) - { - if (!_attr) return false; - - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); - } - - PUGI__FN bool xml_attribute::set_value(float rhs) - { - if (!_attr) return false; - - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); - } - - PUGI__FN bool xml_attribute::set_value(bool rhs) - { - if (!_attr) return false; - - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); - } - -#ifdef PUGIXML_HAS_LONG_LONG - PUGI__FN bool xml_attribute::set_value(long long rhs) - { - if (!_attr) return false; - - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); - } - - PUGI__FN bool xml_attribute::set_value(unsigned long long rhs) - { - if (!_attr) return false; - - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); - } -#endif - -#ifdef __BORLANDC__ - PUGI__FN bool operator&&(const xml_attribute& lhs, bool rhs) - { - return (bool)lhs && rhs; - } - - PUGI__FN bool operator||(const xml_attribute& lhs, bool rhs) - { - return (bool)lhs || rhs; - } -#endif - - PUGI__FN xml_node::xml_node(): _root(0) - { - } - - PUGI__FN xml_node::xml_node(xml_node_struct* p): _root(p) - { - } - - PUGI__FN static void unspecified_bool_xml_node(xml_node***) - { - } - - PUGI__FN xml_node::operator xml_node::unspecified_bool_type() const - { - return _root ? unspecified_bool_xml_node : 0; - } - - PUGI__FN bool xml_node::operator!() const - { - return !_root; - } - - PUGI__FN xml_node::iterator xml_node::begin() const - { - return iterator(_root ? _root->first_child : 0, _root); - } - - PUGI__FN xml_node::iterator xml_node::end() const - { - return iterator(0, _root); - } - - PUGI__FN xml_node::attribute_iterator xml_node::attributes_begin() const - { - return attribute_iterator(_root ? _root->first_attribute : 0, _root); - } - - PUGI__FN xml_node::attribute_iterator xml_node::attributes_end() const - { - return attribute_iterator(0, _root); - } - - PUGI__FN xml_object_range xml_node::children() const - { - return xml_object_range(begin(), end()); - } - - PUGI__FN xml_object_range xml_node::children(const char_t* name_) const - { - return xml_object_range(xml_named_node_iterator(child(name_)._root, _root, name_), xml_named_node_iterator(0, _root, name_)); - } - - PUGI__FN xml_object_range xml_node::attributes() const - { - return xml_object_range(attributes_begin(), attributes_end()); - } - - PUGI__FN bool xml_node::operator==(const xml_node& r) const - { - return (_root == r._root); - } - - PUGI__FN bool xml_node::operator!=(const xml_node& r) const - { - return (_root != r._root); - } - - PUGI__FN bool xml_node::operator<(const xml_node& r) const - { - return (_root < r._root); - } - - PUGI__FN bool xml_node::operator>(const xml_node& r) const - { - return (_root > r._root); - } - - PUGI__FN bool xml_node::operator<=(const xml_node& r) const - { - return (_root <= r._root); - } - - PUGI__FN bool xml_node::operator>=(const xml_node& r) const - { - return (_root >= r._root); - } - - PUGI__FN bool xml_node::empty() const - { - return !_root; - } - - PUGI__FN const char_t* xml_node::name() const - { - return (_root && _root->name) ? _root->name : PUGIXML_TEXT(""); - } - - PUGI__FN xml_node_type xml_node::type() const - { - return _root ? PUGI__NODETYPE(_root) : node_null; - } - - PUGI__FN const char_t* xml_node::value() const - { - return (_root && _root->value) ? _root->value : PUGIXML_TEXT(""); - } - - PUGI__FN xml_node xml_node::child(const char_t* name_) const - { - if (!_root) return xml_node(); - - for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) - if (i->name && impl::strequal(name_, i->name)) return xml_node(i); - - return xml_node(); - } - - PUGI__FN xml_attribute xml_node::attribute(const char_t* name_) const - { - if (!_root) return xml_attribute(); - - for (xml_attribute_struct* i = _root->first_attribute; i; i = i->next_attribute) - if (i->name && impl::strequal(name_, i->name)) - return xml_attribute(i); - - return xml_attribute(); - } - - PUGI__FN xml_node xml_node::next_sibling(const char_t* name_) const - { - if (!_root) return xml_node(); - - for (xml_node_struct* i = _root->next_sibling; i; i = i->next_sibling) - if (i->name && impl::strequal(name_, i->name)) return xml_node(i); - - return xml_node(); - } - - PUGI__FN xml_node xml_node::next_sibling() const - { - return _root ? xml_node(_root->next_sibling) : xml_node(); - } - - PUGI__FN xml_node xml_node::previous_sibling(const char_t* name_) const - { - if (!_root) return xml_node(); - - for (xml_node_struct* i = _root->prev_sibling_c; i->next_sibling; i = i->prev_sibling_c) - if (i->name && impl::strequal(name_, i->name)) return xml_node(i); - - return xml_node(); - } - - PUGI__FN xml_node xml_node::previous_sibling() const - { - if (!_root) return xml_node(); - - if (_root->prev_sibling_c->next_sibling) return xml_node(_root->prev_sibling_c); - else return xml_node(); - } - - PUGI__FN xml_node xml_node::parent() const - { - return _root ? xml_node(_root->parent) : xml_node(); - } - - PUGI__FN xml_node xml_node::root() const - { - return _root ? xml_node(&impl::get_document(_root)) : xml_node(); - } - - PUGI__FN xml_text xml_node::text() const - { - return xml_text(_root); - } - - PUGI__FN const char_t* xml_node::child_value() const - { - if (!_root) return PUGIXML_TEXT(""); - - for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) - if (i->value && impl::is_text_node(i)) - return i->value; - - return PUGIXML_TEXT(""); - } - - PUGI__FN const char_t* xml_node::child_value(const char_t* name_) const - { - return child(name_).child_value(); - } - - PUGI__FN xml_attribute xml_node::first_attribute() const - { - return _root ? xml_attribute(_root->first_attribute) : xml_attribute(); - } - - PUGI__FN xml_attribute xml_node::last_attribute() const - { - return _root && _root->first_attribute ? xml_attribute(_root->first_attribute->prev_attribute_c) : xml_attribute(); - } - - PUGI__FN xml_node xml_node::first_child() const - { - return _root ? xml_node(_root->first_child) : xml_node(); - } - - PUGI__FN xml_node xml_node::last_child() const - { - return _root && _root->first_child ? xml_node(_root->first_child->prev_sibling_c) : xml_node(); - } - - PUGI__FN bool xml_node::set_name(const char_t* rhs) - { - switch (type()) - { - case node_pi: - case node_declaration: - case node_element: - return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs); - - default: - return false; - } - } - - PUGI__FN bool xml_node::set_value(const char_t* rhs) - { - switch (type()) - { - case node_pi: - case node_cdata: - case node_pcdata: - case node_comment: - case node_doctype: - return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs); - - default: - return false; - } - } - - PUGI__FN xml_attribute xml_node::append_attribute(const char_t* name_) - { - if (!impl::allow_insert_attribute(type())) return xml_attribute(); - - xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root))); - if (!a) return xml_attribute(); - - impl::append_attribute(a._attr, _root); - - a.set_name(name_); - - return a; - } - - PUGI__FN xml_attribute xml_node::prepend_attribute(const char_t* name_) - { - if (!impl::allow_insert_attribute(type())) return xml_attribute(); - - xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root))); - if (!a) return xml_attribute(); - - impl::prepend_attribute(a._attr, _root); - - a.set_name(name_); - - return a; - } - - PUGI__FN xml_attribute xml_node::insert_attribute_after(const char_t* name_, const xml_attribute& attr) - { - if (!impl::allow_insert_attribute(type())) return xml_attribute(); - if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); - - xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root))); - if (!a) return xml_attribute(); - - impl::insert_attribute_after(a._attr, attr._attr, _root); - - a.set_name(name_); - - return a; - } - - PUGI__FN xml_attribute xml_node::insert_attribute_before(const char_t* name_, const xml_attribute& attr) - { - if (!impl::allow_insert_attribute(type())) return xml_attribute(); - if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); - - xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root))); - if (!a) return xml_attribute(); - - impl::insert_attribute_before(a._attr, attr._attr, _root); - - a.set_name(name_); - - return a; - } - - PUGI__FN xml_attribute xml_node::append_copy(const xml_attribute& proto) - { - if (!proto) return xml_attribute(); - - xml_attribute result = append_attribute(proto.name()); - result.set_value(proto.value()); - - return result; - } - - PUGI__FN xml_attribute xml_node::prepend_copy(const xml_attribute& proto) - { - if (!proto) return xml_attribute(); - - xml_attribute result = prepend_attribute(proto.name()); - result.set_value(proto.value()); - - return result; - } - - PUGI__FN xml_attribute xml_node::insert_copy_after(const xml_attribute& proto, const xml_attribute& attr) - { - if (!proto) return xml_attribute(); - - xml_attribute result = insert_attribute_after(proto.name(), attr); - result.set_value(proto.value()); - - return result; - } - - PUGI__FN xml_attribute xml_node::insert_copy_before(const xml_attribute& proto, const xml_attribute& attr) - { - if (!proto) return xml_attribute(); - - xml_attribute result = insert_attribute_before(proto.name(), attr); - result.set_value(proto.value()); - - return result; - } - - PUGI__FN xml_node xml_node::append_child(xml_node_type type_) - { - if (!impl::allow_insert_child(type(), type_)) return xml_node(); - - xml_node n(impl::allocate_node(impl::get_allocator(_root), type_)); - if (!n) return xml_node(); - - impl::append_node(n._root, _root); - - if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); - - return n; - } - - PUGI__FN xml_node xml_node::prepend_child(xml_node_type type_) - { - if (!impl::allow_insert_child(type(), type_)) return xml_node(); - - xml_node n(impl::allocate_node(impl::get_allocator(_root), type_)); - if (!n) return xml_node(); - - impl::prepend_node(n._root, _root); - - if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); - - return n; - } - - PUGI__FN xml_node xml_node::insert_child_before(xml_node_type type_, const xml_node& node) - { - if (!impl::allow_insert_child(type(), type_)) return xml_node(); - if (!node._root || node._root->parent != _root) return xml_node(); - - xml_node n(impl::allocate_node(impl::get_allocator(_root), type_)); - if (!n) return xml_node(); - - impl::insert_node_before(n._root, node._root); - - if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); - - return n; - } - - PUGI__FN xml_node xml_node::insert_child_after(xml_node_type type_, const xml_node& node) - { - if (!impl::allow_insert_child(type(), type_)) return xml_node(); - if (!node._root || node._root->parent != _root) return xml_node(); - - xml_node n(impl::allocate_node(impl::get_allocator(_root), type_)); - if (!n) return xml_node(); - - impl::insert_node_after(n._root, node._root); - - if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); - - return n; - } - - PUGI__FN xml_node xml_node::append_child(const char_t* name_) - { - xml_node result = append_child(node_element); - - result.set_name(name_); - - return result; - } - - PUGI__FN xml_node xml_node::prepend_child(const char_t* name_) - { - xml_node result = prepend_child(node_element); - - result.set_name(name_); - - return result; - } - - PUGI__FN xml_node xml_node::insert_child_after(const char_t* name_, const xml_node& node) - { - xml_node result = insert_child_after(node_element, node); - - result.set_name(name_); - - return result; - } - - PUGI__FN xml_node xml_node::insert_child_before(const char_t* name_, const xml_node& node) - { - xml_node result = insert_child_before(node_element, node); - - result.set_name(name_); - - return result; - } - - PUGI__FN xml_node xml_node::append_copy(const xml_node& proto) - { - xml_node_type type_ = proto.type(); - if (!impl::allow_insert_child(type(), type_)) return xml_node(); - - xml_node n(impl::allocate_node(impl::get_allocator(_root), type_)); - if (!n) return xml_node(); - - impl::append_node(n._root, _root); - impl::node_copy_tree(n._root, proto._root); - - return n; - } - - PUGI__FN xml_node xml_node::prepend_copy(const xml_node& proto) - { - xml_node_type type_ = proto.type(); - if (!impl::allow_insert_child(type(), type_)) return xml_node(); - - xml_node n(impl::allocate_node(impl::get_allocator(_root), type_)); - if (!n) return xml_node(); - - impl::prepend_node(n._root, _root); - impl::node_copy_tree(n._root, proto._root); - - return n; - } - - PUGI__FN xml_node xml_node::insert_copy_after(const xml_node& proto, const xml_node& node) - { - xml_node_type type_ = proto.type(); - if (!impl::allow_insert_child(type(), type_)) return xml_node(); - if (!node._root || node._root->parent != _root) return xml_node(); - - xml_node n(impl::allocate_node(impl::get_allocator(_root), type_)); - if (!n) return xml_node(); - - impl::insert_node_after(n._root, node._root); - impl::node_copy_tree(n._root, proto._root); - - return n; - } - - PUGI__FN xml_node xml_node::insert_copy_before(const xml_node& proto, const xml_node& node) - { - xml_node_type type_ = proto.type(); - if (!impl::allow_insert_child(type(), type_)) return xml_node(); - if (!node._root || node._root->parent != _root) return xml_node(); - - xml_node n(impl::allocate_node(impl::get_allocator(_root), type_)); - if (!n) return xml_node(); - - impl::insert_node_before(n._root, node._root); - impl::node_copy_tree(n._root, proto._root); - - return n; - } - - PUGI__FN xml_node xml_node::append_move(const xml_node& moved) - { - if (!impl::allow_move(*this, moved)) return xml_node(); - - // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers - impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask; - - impl::remove_node(moved._root); - impl::append_node(moved._root, _root); - - return moved; - } - - PUGI__FN xml_node xml_node::prepend_move(const xml_node& moved) - { - if (!impl::allow_move(*this, moved)) return xml_node(); - - // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers - impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask; - - impl::remove_node(moved._root); - impl::prepend_node(moved._root, _root); - - return moved; - } - - PUGI__FN xml_node xml_node::insert_move_after(const xml_node& moved, const xml_node& node) - { - if (!impl::allow_move(*this, moved)) return xml_node(); - if (!node._root || node._root->parent != _root) return xml_node(); - if (moved._root == node._root) return xml_node(); - - // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers - impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask; - - impl::remove_node(moved._root); - impl::insert_node_after(moved._root, node._root); - - return moved; - } - - PUGI__FN xml_node xml_node::insert_move_before(const xml_node& moved, const xml_node& node) - { - if (!impl::allow_move(*this, moved)) return xml_node(); - if (!node._root || node._root->parent != _root) return xml_node(); - if (moved._root == node._root) return xml_node(); - - // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers - impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask; - - impl::remove_node(moved._root); - impl::insert_node_before(moved._root, node._root); - - return moved; - } - - PUGI__FN bool xml_node::remove_attribute(const char_t* name_) - { - return remove_attribute(attribute(name_)); - } - - PUGI__FN bool xml_node::remove_attribute(const xml_attribute& a) - { - if (!_root || !a._attr) return false; - if (!impl::is_attribute_of(a._attr, _root)) return false; - - impl::remove_attribute(a._attr, _root); - impl::destroy_attribute(a._attr, impl::get_allocator(_root)); - - return true; - } - - PUGI__FN bool xml_node::remove_child(const char_t* name_) - { - return remove_child(child(name_)); - } - - PUGI__FN bool xml_node::remove_child(const xml_node& n) - { - if (!_root || !n._root || n._root->parent != _root) return false; - - impl::remove_node(n._root); - impl::destroy_node(n._root, impl::get_allocator(_root)); - - return true; - } - - PUGI__FN xml_parse_result xml_node::append_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding) - { - // append_buffer is only valid for elements/documents - if (!impl::allow_insert_child(type(), node_element)) return impl::make_parse_result(status_append_invalid_root); - - // get document node - impl::xml_document_struct* doc = &impl::get_document(_root); - - // disable document_buffer_order optimization since in a document with multiple buffers comparing buffer pointers does not make sense - doc->header |= impl::xml_memory_page_contents_shared_mask; - - // get extra buffer element (we'll store the document fragment buffer there so that we can deallocate it later) - impl::xml_memory_page* page = 0; - impl::xml_extra_buffer* extra = static_cast(doc->allocate_memory(sizeof(impl::xml_extra_buffer), page)); - (void)page; - - if (!extra) return impl::make_parse_result(status_out_of_memory); - - // save name; name of the root has to be NULL before parsing - otherwise closing node mismatches will not be detected at the top level - char_t* rootname = _root->name; - _root->name = 0; - - // parse - char_t* buffer = 0; - xml_parse_result res = impl::load_buffer_impl(doc, _root, const_cast(contents), size, options, encoding, false, false, &buffer); - - // restore name - _root->name = rootname; - - // add extra buffer to the list - extra->buffer = buffer; - extra->next = doc->extra_buffers; - doc->extra_buffers = extra; - - return res; - } - - PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* name_, const char_t* attr_name, const char_t* attr_value) const - { - if (!_root) return xml_node(); - - for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) - if (i->name && impl::strequal(name_, i->name)) - { - for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute) - if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value : PUGIXML_TEXT(""))) - return xml_node(i); - } - - return xml_node(); - } - - PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const - { - if (!_root) return xml_node(); - - for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) - for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute) - if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value : PUGIXML_TEXT(""))) - return xml_node(i); - - return xml_node(); - } - -#ifndef PUGIXML_NO_STL - PUGI__FN string_t xml_node::path(char_t delimiter) const - { - xml_node cursor = *this; // Make a copy. - - string_t result = cursor.name(); - - while (cursor.parent()) - { - cursor = cursor.parent(); - - string_t temp = cursor.name(); - temp += delimiter; - temp += result; - result.swap(temp); - } - - return result; - } -#endif - - PUGI__FN xml_node xml_node::first_element_by_path(const char_t* path_, char_t delimiter) const - { - xml_node found = *this; // Current search context. - - if (!_root || !path_ || !path_[0]) return found; - - if (path_[0] == delimiter) - { - // Absolute path; e.g. '/foo/bar' - found = found.root(); - ++path_; - } - - const char_t* path_segment = path_; - - while (*path_segment == delimiter) ++path_segment; - - const char_t* path_segment_end = path_segment; - - while (*path_segment_end && *path_segment_end != delimiter) ++path_segment_end; - - if (path_segment == path_segment_end) return found; - - const char_t* next_segment = path_segment_end; - - while (*next_segment == delimiter) ++next_segment; - - if (*path_segment == '.' && path_segment + 1 == path_segment_end) - return found.first_element_by_path(next_segment, delimiter); - else if (*path_segment == '.' && *(path_segment+1) == '.' && path_segment + 2 == path_segment_end) - return found.parent().first_element_by_path(next_segment, delimiter); - else - { - for (xml_node_struct* j = found._root->first_child; j; j = j->next_sibling) - { - if (j->name && impl::strequalrange(j->name, path_segment, static_cast(path_segment_end - path_segment))) - { - xml_node subsearch = xml_node(j).first_element_by_path(next_segment, delimiter); - - if (subsearch) return subsearch; - } - } - - return xml_node(); - } - } - - PUGI__FN bool xml_node::traverse(xml_tree_walker& walker) - { - walker._depth = -1; - - xml_node arg_begin = *this; - if (!walker.begin(arg_begin)) return false; - - xml_node cur = first_child(); - - if (cur) - { - ++walker._depth; - - do - { - xml_node arg_for_each = cur; - if (!walker.for_each(arg_for_each)) - return false; - - if (cur.first_child()) - { - ++walker._depth; - cur = cur.first_child(); - } - else if (cur.next_sibling()) - cur = cur.next_sibling(); - else - { - // Borland C++ workaround - while (!cur.next_sibling() && cur != *this && !cur.parent().empty()) - { - --walker._depth; - cur = cur.parent(); - } - - if (cur != *this) - cur = cur.next_sibling(); - } - } - while (cur && cur != *this); - } - - assert(walker._depth == -1); - - xml_node arg_end = *this; - return walker.end(arg_end); - } - - PUGI__FN size_t xml_node::hash_value() const - { - return static_cast(reinterpret_cast(_root) / sizeof(xml_node_struct)); - } - - PUGI__FN xml_node_struct* xml_node::internal_object() const - { - return _root; - } - - PUGI__FN void xml_node::print(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const - { - if (!_root) return; - - impl::xml_buffered_writer buffered_writer(writer, encoding); - - impl::node_output(buffered_writer, _root, indent, flags, depth); - } - -#ifndef PUGIXML_NO_STL - PUGI__FN void xml_node::print(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const - { - xml_writer_stream writer(stream); - - print(writer, indent, flags, encoding, depth); - } - - PUGI__FN void xml_node::print(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, unsigned int depth) const - { - xml_writer_stream writer(stream); - - print(writer, indent, flags, encoding_wchar, depth); - } -#endif - - PUGI__FN ptrdiff_t xml_node::offset_debug() const - { - if (!_root) return -1; - - impl::xml_document_struct& doc = impl::get_document(_root); - - // we can determine the offset reliably only if there is exactly once parse buffer - if (!doc.buffer || doc.extra_buffers) return -1; - - switch (type()) - { - case node_document: - return 0; - - case node_element: - case node_declaration: - case node_pi: - return _root->name && (_root->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0 ? _root->name - doc.buffer : -1; - - case node_pcdata: - case node_cdata: - case node_comment: - case node_doctype: - return _root->value && (_root->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0 ? _root->value - doc.buffer : -1; - - default: - return -1; - } - } - -#ifdef __BORLANDC__ - PUGI__FN bool operator&&(const xml_node& lhs, bool rhs) - { - return (bool)lhs && rhs; - } - - PUGI__FN bool operator||(const xml_node& lhs, bool rhs) - { - return (bool)lhs || rhs; - } -#endif - - PUGI__FN xml_text::xml_text(xml_node_struct* root): _root(root) - { - } - - PUGI__FN xml_node_struct* xml_text::_data() const - { - if (!_root || impl::is_text_node(_root)) return _root; - - for (xml_node_struct* node = _root->first_child; node; node = node->next_sibling) - if (impl::is_text_node(node)) - return node; - - return 0; - } - - PUGI__FN xml_node_struct* xml_text::_data_new() - { - xml_node_struct* d = _data(); - if (d) return d; - - return xml_node(_root).append_child(node_pcdata).internal_object(); - } - - PUGI__FN xml_text::xml_text(): _root(0) - { - } - - PUGI__FN static void unspecified_bool_xml_text(xml_text***) - { - } - - PUGI__FN xml_text::operator xml_text::unspecified_bool_type() const - { - return _data() ? unspecified_bool_xml_text : 0; - } - - PUGI__FN bool xml_text::operator!() const - { - return !_data(); - } - - PUGI__FN bool xml_text::empty() const - { - return _data() == 0; - } - - PUGI__FN const char_t* xml_text::get() const - { - xml_node_struct* d = _data(); - - return (d && d->value) ? d->value : PUGIXML_TEXT(""); - } - - PUGI__FN const char_t* xml_text::as_string(const char_t* def) const - { - xml_node_struct* d = _data(); - - return (d && d->value) ? d->value : def; - } - - PUGI__FN int xml_text::as_int(int def) const - { - xml_node_struct* d = _data(); - - return impl::get_value_int(d ? d->value : 0, def); - } - - PUGI__FN unsigned int xml_text::as_uint(unsigned int def) const - { - xml_node_struct* d = _data(); - - return impl::get_value_uint(d ? d->value : 0, def); - } - - PUGI__FN double xml_text::as_double(double def) const - { - xml_node_struct* d = _data(); - - return impl::get_value_double(d ? d->value : 0, def); - } - - PUGI__FN float xml_text::as_float(float def) const - { - xml_node_struct* d = _data(); - - return impl::get_value_float(d ? d->value : 0, def); - } - - PUGI__FN bool xml_text::as_bool(bool def) const - { - xml_node_struct* d = _data(); - - return impl::get_value_bool(d ? d->value : 0, def); - } - -#ifdef PUGIXML_HAS_LONG_LONG - PUGI__FN long long xml_text::as_llong(long long def) const - { - xml_node_struct* d = _data(); - - return impl::get_value_llong(d ? d->value : 0, def); - } - - PUGI__FN unsigned long long xml_text::as_ullong(unsigned long long def) const - { - xml_node_struct* d = _data(); - - return impl::get_value_ullong(d ? d->value : 0, def); - } -#endif - - PUGI__FN bool xml_text::set(const char_t* rhs) - { - xml_node_struct* dn = _data_new(); - - return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; - } - - PUGI__FN bool xml_text::set(int rhs) - { - xml_node_struct* dn = _data_new(); - - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; - } - - PUGI__FN bool xml_text::set(unsigned int rhs) - { - xml_node_struct* dn = _data_new(); - - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; - } - - PUGI__FN bool xml_text::set(float rhs) - { - xml_node_struct* dn = _data_new(); - - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; - } - - PUGI__FN bool xml_text::set(double rhs) - { - xml_node_struct* dn = _data_new(); - - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; - } - - PUGI__FN bool xml_text::set(bool rhs) - { - xml_node_struct* dn = _data_new(); - - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; - } - -#ifdef PUGIXML_HAS_LONG_LONG - PUGI__FN bool xml_text::set(long long rhs) - { - xml_node_struct* dn = _data_new(); - - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; - } - - PUGI__FN bool xml_text::set(unsigned long long rhs) - { - xml_node_struct* dn = _data_new(); - - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; - } -#endif - - PUGI__FN xml_text& xml_text::operator=(const char_t* rhs) - { - set(rhs); - return *this; - } - - PUGI__FN xml_text& xml_text::operator=(int rhs) - { - set(rhs); - return *this; - } - - PUGI__FN xml_text& xml_text::operator=(unsigned int rhs) - { - set(rhs); - return *this; - } - - PUGI__FN xml_text& xml_text::operator=(double rhs) - { - set(rhs); - return *this; - } - - PUGI__FN xml_text& xml_text::operator=(float rhs) - { - set(rhs); - return *this; - } - - PUGI__FN xml_text& xml_text::operator=(bool rhs) - { - set(rhs); - return *this; - } - -#ifdef PUGIXML_HAS_LONG_LONG - PUGI__FN xml_text& xml_text::operator=(long long rhs) - { - set(rhs); - return *this; - } - - PUGI__FN xml_text& xml_text::operator=(unsigned long long rhs) - { - set(rhs); - return *this; - } -#endif - - PUGI__FN xml_node xml_text::data() const - { - return xml_node(_data()); - } - -#ifdef __BORLANDC__ - PUGI__FN bool operator&&(const xml_text& lhs, bool rhs) - { - return (bool)lhs && rhs; - } - - PUGI__FN bool operator||(const xml_text& lhs, bool rhs) - { - return (bool)lhs || rhs; - } -#endif - - PUGI__FN xml_node_iterator::xml_node_iterator() - { - } - - PUGI__FN xml_node_iterator::xml_node_iterator(const xml_node& node): _wrap(node), _parent(node.parent()) - { - } - - PUGI__FN xml_node_iterator::xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent) - { - } - - PUGI__FN bool xml_node_iterator::operator==(const xml_node_iterator& rhs) const - { - return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root; - } - - PUGI__FN bool xml_node_iterator::operator!=(const xml_node_iterator& rhs) const - { - return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root; - } - - PUGI__FN xml_node& xml_node_iterator::operator*() const - { - assert(_wrap._root); - return _wrap; - } - - PUGI__FN xml_node* xml_node_iterator::operator->() const - { - assert(_wrap._root); - return const_cast(&_wrap); // BCC32 workaround - } - - PUGI__FN const xml_node_iterator& xml_node_iterator::operator++() - { - assert(_wrap._root); - _wrap._root = _wrap._root->next_sibling; - return *this; - } - - PUGI__FN xml_node_iterator xml_node_iterator::operator++(int) - { - xml_node_iterator temp = *this; - ++*this; - return temp; - } - - PUGI__FN const xml_node_iterator& xml_node_iterator::operator--() - { - _wrap = _wrap._root ? _wrap.previous_sibling() : _parent.last_child(); - return *this; - } - - PUGI__FN xml_node_iterator xml_node_iterator::operator--(int) - { - xml_node_iterator temp = *this; - --*this; - return temp; - } - - PUGI__FN xml_attribute_iterator::xml_attribute_iterator() - { - } - - PUGI__FN xml_attribute_iterator::xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent): _wrap(attr), _parent(parent) - { - } - - PUGI__FN xml_attribute_iterator::xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent) - { - } - - PUGI__FN bool xml_attribute_iterator::operator==(const xml_attribute_iterator& rhs) const - { - return _wrap._attr == rhs._wrap._attr && _parent._root == rhs._parent._root; - } - - PUGI__FN bool xml_attribute_iterator::operator!=(const xml_attribute_iterator& rhs) const - { - return _wrap._attr != rhs._wrap._attr || _parent._root != rhs._parent._root; - } - - PUGI__FN xml_attribute& xml_attribute_iterator::operator*() const - { - assert(_wrap._attr); - return _wrap; - } - - PUGI__FN xml_attribute* xml_attribute_iterator::operator->() const - { - assert(_wrap._attr); - return const_cast(&_wrap); // BCC32 workaround - } - - PUGI__FN const xml_attribute_iterator& xml_attribute_iterator::operator++() - { - assert(_wrap._attr); - _wrap._attr = _wrap._attr->next_attribute; - return *this; - } - - PUGI__FN xml_attribute_iterator xml_attribute_iterator::operator++(int) - { - xml_attribute_iterator temp = *this; - ++*this; - return temp; - } - - PUGI__FN const xml_attribute_iterator& xml_attribute_iterator::operator--() - { - _wrap = _wrap._attr ? _wrap.previous_attribute() : _parent.last_attribute(); - return *this; - } - - PUGI__FN xml_attribute_iterator xml_attribute_iterator::operator--(int) - { - xml_attribute_iterator temp = *this; - --*this; - return temp; - } - - PUGI__FN xml_named_node_iterator::xml_named_node_iterator(): _name(0) - { - } - - PUGI__FN xml_named_node_iterator::xml_named_node_iterator(const xml_node& node, const char_t* name): _wrap(node), _parent(node.parent()), _name(name) - { - } - - PUGI__FN xml_named_node_iterator::xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name): _wrap(ref), _parent(parent), _name(name) - { - } - - PUGI__FN bool xml_named_node_iterator::operator==(const xml_named_node_iterator& rhs) const - { - return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root; - } - - PUGI__FN bool xml_named_node_iterator::operator!=(const xml_named_node_iterator& rhs) const - { - return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root; - } - - PUGI__FN xml_node& xml_named_node_iterator::operator*() const - { - assert(_wrap._root); - return _wrap; - } - - PUGI__FN xml_node* xml_named_node_iterator::operator->() const - { - assert(_wrap._root); - return const_cast(&_wrap); // BCC32 workaround - } - - PUGI__FN const xml_named_node_iterator& xml_named_node_iterator::operator++() - { - assert(_wrap._root); - _wrap = _wrap.next_sibling(_name); - return *this; - } - - PUGI__FN xml_named_node_iterator xml_named_node_iterator::operator++(int) - { - xml_named_node_iterator temp = *this; - ++*this; - return temp; - } - - PUGI__FN const xml_named_node_iterator& xml_named_node_iterator::operator--() - { - if (_wrap._root) - _wrap = _wrap.previous_sibling(_name); - else - { - _wrap = _parent.last_child(); - - if (!impl::strequal(_wrap.name(), _name)) - _wrap = _wrap.previous_sibling(_name); - } - - return *this; - } - - PUGI__FN xml_named_node_iterator xml_named_node_iterator::operator--(int) - { - xml_named_node_iterator temp = *this; - --*this; - return temp; - } - - PUGI__FN xml_parse_result::xml_parse_result(): status(status_internal_error), offset(0), encoding(encoding_auto) - { - } - - PUGI__FN xml_parse_result::operator bool() const - { - return status == status_ok; - } - - PUGI__FN const char* xml_parse_result::description() const - { - switch (status) - { - case status_ok: return "No error"; - - case status_file_not_found: return "File was not found"; - case status_io_error: return "Error reading from file/stream"; - case status_out_of_memory: return "Could not allocate memory"; - case status_internal_error: return "Internal error occurred"; - - case status_unrecognized_tag: return "Could not determine tag type"; - - case status_bad_pi: return "Error parsing document declaration/processing instruction"; - case status_bad_comment: return "Error parsing comment"; - case status_bad_cdata: return "Error parsing CDATA section"; - case status_bad_doctype: return "Error parsing document type declaration"; - case status_bad_pcdata: return "Error parsing PCDATA section"; - case status_bad_start_element: return "Error parsing start element tag"; - case status_bad_attribute: return "Error parsing element attribute"; - case status_bad_end_element: return "Error parsing end element tag"; - case status_end_element_mismatch: return "Start-end tags mismatch"; - - case status_append_invalid_root: return "Unable to append nodes: root is not an element or document"; - - case status_no_document_element: return "No document element found"; - - default: return "Unknown error"; - } - } - - PUGI__FN xml_document::xml_document(): _buffer(0) - { - create(); - } - - PUGI__FN xml_document::~xml_document() - { - destroy(); - } - - PUGI__FN void xml_document::reset() - { - destroy(); - create(); - } - - PUGI__FN void xml_document::reset(const xml_document& proto) - { - reset(); - - for (xml_node cur = proto.first_child(); cur; cur = cur.next_sibling()) - append_copy(cur); - } - - PUGI__FN void xml_document::create() - { - assert(!_root); - - // initialize sentinel page - PUGI__STATIC_ASSERT(sizeof(impl::xml_memory_page) + sizeof(impl::xml_document_struct) + impl::xml_memory_page_alignment - sizeof(void*) <= sizeof(_memory)); - - // align upwards to page boundary - void* page_memory = reinterpret_cast((reinterpret_cast(_memory) + (impl::xml_memory_page_alignment - 1)) & ~(impl::xml_memory_page_alignment - 1)); - - // prepare page structure - impl::xml_memory_page* page = impl::xml_memory_page::construct(page_memory); - assert(page); - - page->busy_size = impl::xml_memory_page_size; - - // allocate new root - _root = new (reinterpret_cast(page) + sizeof(impl::xml_memory_page)) impl::xml_document_struct(page); - _root->prev_sibling_c = _root; - - // setup sentinel page - page->allocator = static_cast(_root); - - // verify the document allocation - assert(reinterpret_cast(_root) + sizeof(impl::xml_document_struct) <= _memory + sizeof(_memory)); - } - - PUGI__FN void xml_document::destroy() - { - assert(_root); - - // destroy static storage - if (_buffer) - { - impl::xml_memory::deallocate(_buffer); - _buffer = 0; - } - - // destroy extra buffers (note: no need to destroy linked list nodes, they're allocated using document allocator) - for (impl::xml_extra_buffer* extra = static_cast(_root)->extra_buffers; extra; extra = extra->next) - { - if (extra->buffer) impl::xml_memory::deallocate(extra->buffer); - } - - // destroy dynamic storage, leave sentinel page (it's in static memory) - impl::xml_memory_page* root_page = reinterpret_cast(_root->header & impl::xml_memory_page_pointer_mask); - assert(root_page && !root_page->prev); - assert(reinterpret_cast(root_page) >= _memory && reinterpret_cast(root_page) < _memory + sizeof(_memory)); - - for (impl::xml_memory_page* page = root_page->next; page; ) - { - impl::xml_memory_page* next = page->next; - - impl::xml_allocator::deallocate_page(page); - - page = next; - } - - _root = 0; - } - -#ifndef PUGIXML_NO_STL - PUGI__FN xml_parse_result xml_document::load(std::basic_istream >& stream, unsigned int options, xml_encoding encoding) - { - reset(); - - return impl::load_stream_impl(*this, stream, options, encoding); - } - - PUGI__FN xml_parse_result xml_document::load(std::basic_istream >& stream, unsigned int options) - { - reset(); - - return impl::load_stream_impl(*this, stream, options, encoding_wchar); - } -#endif - - PUGI__FN xml_parse_result xml_document::load_string(const char_t* contents, unsigned int options) - { - // Force native encoding (skip autodetection) - #ifdef PUGIXML_WCHAR_MODE - xml_encoding encoding = encoding_wchar; - #else - xml_encoding encoding = encoding_utf8; - #endif - - return load_buffer(contents, impl::strlength(contents) * sizeof(char_t), options, encoding); - } - - PUGI__FN xml_parse_result xml_document::load(const char_t* contents, unsigned int options) - { - return load_string(contents, options); - } - - PUGI__FN xml_parse_result xml_document::load_file(const char* path_, unsigned int options, xml_encoding encoding) - { - reset(); - - FILE* file = fopen(path_, "rb"); - - return impl::load_file_impl(*this, file, options, encoding); - } - - PUGI__FN xml_parse_result xml_document::load_file(const wchar_t* path_, unsigned int options, xml_encoding encoding) - { - reset(); - - FILE* file = impl::open_file_wide(path_, L"rb"); - - return impl::load_file_impl(*this, file, options, encoding); - } - - PUGI__FN xml_parse_result xml_document::load_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding) - { - reset(); - - return impl::load_buffer_impl(static_cast(_root), _root, const_cast(contents), size, options, encoding, false, false, &_buffer); - } - - PUGI__FN xml_parse_result xml_document::load_buffer_inplace(void* contents, size_t size, unsigned int options, xml_encoding encoding) - { - reset(); - - return impl::load_buffer_impl(static_cast(_root), _root, contents, size, options, encoding, true, false, &_buffer); - } - - PUGI__FN xml_parse_result xml_document::load_buffer_inplace_own(void* contents, size_t size, unsigned int options, xml_encoding encoding) - { - reset(); - - return impl::load_buffer_impl(static_cast(_root), _root, contents, size, options, encoding, true, true, &_buffer); - } - - PUGI__FN void xml_document::save(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding) const - { - impl::xml_buffered_writer buffered_writer(writer, encoding); - - if ((flags & format_write_bom) && encoding != encoding_latin1) - { - // BOM always represents the codepoint U+FEFF, so just write it in native encoding - #ifdef PUGIXML_WCHAR_MODE - unsigned int bom = 0xfeff; - buffered_writer.write(static_cast(bom)); - #else - buffered_writer.write('\xef', '\xbb', '\xbf'); - #endif - } - - if (!(flags & format_no_declaration) && !impl::has_declaration(_root)) - { - buffered_writer.write_string(PUGIXML_TEXT("'); - if (!(flags & format_raw)) buffered_writer.write('\n'); - } - - impl::node_output(buffered_writer, _root, indent, flags, 0); - } - -#ifndef PUGIXML_NO_STL - PUGI__FN void xml_document::save(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding) const - { - xml_writer_stream writer(stream); - - save(writer, indent, flags, encoding); - } - - PUGI__FN void xml_document::save(std::basic_ostream >& stream, const char_t* indent, unsigned int flags) const - { - xml_writer_stream writer(stream); - - save(writer, indent, flags, encoding_wchar); - } -#endif - - PUGI__FN bool xml_document::save_file(const char* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const - { - FILE* file = fopen(path_, (flags & format_save_file_text) ? "w" : "wb"); - return impl::save_file_impl(*this, file, indent, flags, encoding); - } - - PUGI__FN bool xml_document::save_file(const wchar_t* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const - { - FILE* file = impl::open_file_wide(path_, (flags & format_save_file_text) ? L"w" : L"wb"); - return impl::save_file_impl(*this, file, indent, flags, encoding); - } - - PUGI__FN xml_node xml_document::document_element() const - { - assert(_root); - - for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) - if (PUGI__NODETYPE(i) == node_element) - return xml_node(i); - - return xml_node(); - } - -#ifndef PUGIXML_NO_STL - PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const wchar_t* str) - { - assert(str); - - return impl::as_utf8_impl(str, impl::strlength_wide(str)); - } - - PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const std::basic_string& str) - { - return impl::as_utf8_impl(str.c_str(), str.size()); - } - - PUGI__FN std::basic_string PUGIXML_FUNCTION as_wide(const char* str) - { - assert(str); - - return impl::as_wide_impl(str, strlen(str)); - } - - PUGI__FN std::basic_string PUGIXML_FUNCTION as_wide(const std::string& str) - { - return impl::as_wide_impl(str.c_str(), str.size()); - } -#endif - - PUGI__FN void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate) - { - impl::xml_memory::allocate = allocate; - impl::xml_memory::deallocate = deallocate; - } - - PUGI__FN allocation_function PUGIXML_FUNCTION get_memory_allocation_function() - { - return impl::xml_memory::allocate; - } - - PUGI__FN deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function() - { - return impl::xml_memory::deallocate; - } -} - -#if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC)) -namespace std -{ - // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier) - PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_node_iterator&) - { - return std::bidirectional_iterator_tag(); - } - - PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_attribute_iterator&) - { - return std::bidirectional_iterator_tag(); - } - - PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_named_node_iterator&) - { - return std::bidirectional_iterator_tag(); - } -} -#endif - -#if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC) -namespace std -{ - // Workarounds for (non-standard) iterator category detection - PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_node_iterator&) - { - return std::bidirectional_iterator_tag(); - } - - PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_attribute_iterator&) - { - return std::bidirectional_iterator_tag(); - } - - PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_named_node_iterator&) - { - return std::bidirectional_iterator_tag(); - } -} -#endif - -#ifndef PUGIXML_NO_XPATH -// STL replacements -PUGI__NS_BEGIN - struct equal_to - { - template bool operator()(const T& lhs, const T& rhs) const - { - return lhs == rhs; - } - }; - - struct not_equal_to - { - template bool operator()(const T& lhs, const T& rhs) const - { - return lhs != rhs; - } - }; - - struct less - { - template bool operator()(const T& lhs, const T& rhs) const - { - return lhs < rhs; - } - }; - - struct less_equal - { - template bool operator()(const T& lhs, const T& rhs) const - { - return lhs <= rhs; - } - }; - - template void swap(T& lhs, T& rhs) - { - T temp = lhs; - lhs = rhs; - rhs = temp; - } - - template I min_element(I begin, I end, const Pred& pred) - { - I result = begin; - - for (I it = begin + 1; it != end; ++it) - if (pred(*it, *result)) - result = it; - - return result; - } - - template void reverse(I begin, I end) - { - while (end - begin > 1) swap(*begin++, *--end); - } - - template I unique(I begin, I end) - { - // fast skip head - while (end - begin > 1 && *begin != *(begin + 1)) begin++; - - if (begin == end) return begin; - - // last written element - I write = begin++; - - // merge unique elements - while (begin != end) - { - if (*begin != *write) - *++write = *begin++; - else - begin++; - } - - // past-the-end (write points to live element) - return write + 1; - } - - template void copy_backwards(I begin, I end, I target) - { - while (begin != end) *--target = *--end; - } - - template void insertion_sort(I begin, I end, const Pred& pred, T*) - { - assert(begin != end); - - for (I it = begin + 1; it != end; ++it) - { - T val = *it; - - if (pred(val, *begin)) - { - // move to front - copy_backwards(begin, it, it + 1); - *begin = val; - } - else - { - I hole = it; - - // move hole backwards - while (pred(val, *(hole - 1))) - { - *hole = *(hole - 1); - hole--; - } - - // fill hole with element - *hole = val; - } - } - } - - // std variant for elements with == - template void partition(I begin, I middle, I end, const Pred& pred, I* out_eqbeg, I* out_eqend) - { - I eqbeg = middle, eqend = middle + 1; - - // expand equal range - while (eqbeg != begin && *(eqbeg - 1) == *eqbeg) --eqbeg; - while (eqend != end && *eqend == *eqbeg) ++eqend; - - // process outer elements - I ltend = eqbeg, gtbeg = eqend; - - for (;;) - { - // find the element from the right side that belongs to the left one - for (; gtbeg != end; ++gtbeg) - if (!pred(*eqbeg, *gtbeg)) - { - if (*gtbeg == *eqbeg) swap(*gtbeg, *eqend++); - else break; - } - - // find the element from the left side that belongs to the right one - for (; ltend != begin; --ltend) - if (!pred(*(ltend - 1), *eqbeg)) - { - if (*eqbeg == *(ltend - 1)) swap(*(ltend - 1), *--eqbeg); - else break; - } - - // scanned all elements - if (gtbeg == end && ltend == begin) - { - *out_eqbeg = eqbeg; - *out_eqend = eqend; - return; - } - - // make room for elements by moving equal area - if (gtbeg == end) - { - if (--ltend != --eqbeg) swap(*ltend, *eqbeg); - swap(*eqbeg, *--eqend); - } - else if (ltend == begin) - { - if (eqend != gtbeg) swap(*eqbeg, *eqend); - ++eqend; - swap(*gtbeg++, *eqbeg++); - } - else swap(*gtbeg++, *--ltend); - } - } - - template void median3(I first, I middle, I last, const Pred& pred) - { - if (pred(*middle, *first)) swap(*middle, *first); - if (pred(*last, *middle)) swap(*last, *middle); - if (pred(*middle, *first)) swap(*middle, *first); - } - - template void median(I first, I middle, I last, const Pred& pred) - { - if (last - first <= 40) - { - // median of three for small chunks - median3(first, middle, last, pred); - } - else - { - // median of nine - size_t step = (last - first + 1) / 8; - - median3(first, first + step, first + 2 * step, pred); - median3(middle - step, middle, middle + step, pred); - median3(last - 2 * step, last - step, last, pred); - median3(first + step, middle, last - step, pred); - } - } - - template void sort(I begin, I end, const Pred& pred) - { - // sort large chunks - while (end - begin > 32) - { - // find median element - I middle = begin + (end - begin) / 2; - median(begin, middle, end - 1, pred); - - // partition in three chunks (< = >) - I eqbeg, eqend; - partition(begin, middle, end, pred, &eqbeg, &eqend); - - // loop on larger half - if (eqbeg - begin > end - eqend) - { - sort(eqend, end, pred); - end = eqbeg; - } - else - { - sort(begin, eqbeg, pred); - begin = eqend; - } - } - - // insertion sort small chunk - if (begin != end) insertion_sort(begin, end, pred, &*begin); - } -PUGI__NS_END - -// Allocator used for AST and evaluation stacks -PUGI__NS_BEGIN - struct xpath_memory_block - { - xpath_memory_block* next; - size_t capacity; - - char data[ - #ifdef PUGIXML_MEMORY_XPATH_PAGE_SIZE - PUGIXML_MEMORY_XPATH_PAGE_SIZE - #else - 4096 - #endif - ]; - }; - - class xpath_allocator - { - xpath_memory_block* _root; - size_t _root_size; - - public: - #ifdef PUGIXML_NO_EXCEPTIONS - jmp_buf* error_handler; - #endif - - xpath_allocator(xpath_memory_block* root, size_t root_size = 0): _root(root), _root_size(root_size) - { - #ifdef PUGIXML_NO_EXCEPTIONS - error_handler = 0; - #endif - } - - void* allocate_nothrow(size_t size) - { - // align size so that we're able to store pointers in subsequent blocks - size = (size + sizeof(void*) - 1) & ~(sizeof(void*) - 1); - - if (_root_size + size <= _root->capacity) - { - void* buf = _root->data + _root_size; - _root_size += size; - return buf; - } - else - { - // make sure we have at least 1/4th of the page free after allocation to satisfy subsequent allocation requests - size_t block_capacity_base = sizeof(_root->data); - size_t block_capacity_req = size + block_capacity_base / 4; - size_t block_capacity = (block_capacity_base > block_capacity_req) ? block_capacity_base : block_capacity_req; - - size_t block_size = block_capacity + offsetof(xpath_memory_block, data); - - xpath_memory_block* block = static_cast(xml_memory::allocate(block_size)); - if (!block) return 0; - - block->next = _root; - block->capacity = block_capacity; - - _root = block; - _root_size = size; - - return block->data; - } - } - - void* allocate(size_t size) - { - void* result = allocate_nothrow(size); - - if (!result) - { - #ifdef PUGIXML_NO_EXCEPTIONS - assert(error_handler); - longjmp(*error_handler, 1); - #else - throw std::bad_alloc(); - #endif - } - - return result; - } - - void* reallocate(void* ptr, size_t old_size, size_t new_size) - { - // align size so that we're able to store pointers in subsequent blocks - old_size = (old_size + sizeof(void*) - 1) & ~(sizeof(void*) - 1); - new_size = (new_size + sizeof(void*) - 1) & ~(sizeof(void*) - 1); - - // we can only reallocate the last object - assert(ptr == 0 || static_cast(ptr) + old_size == _root->data + _root_size); - - // adjust root size so that we have not allocated the object at all - bool only_object = (_root_size == old_size); - - if (ptr) _root_size -= old_size; - - // allocate a new version (this will obviously reuse the memory if possible) - void* result = allocate(new_size); - assert(result); - - // we have a new block - if (result != ptr && ptr) - { - // copy old data - assert(new_size >= old_size); - memcpy(result, ptr, old_size); - - // free the previous page if it had no other objects - if (only_object) - { - assert(_root->data == result); - assert(_root->next); - - xpath_memory_block* next = _root->next->next; - - if (next) - { - // deallocate the whole page, unless it was the first one - xml_memory::deallocate(_root->next); - _root->next = next; - } - } - } - - return result; - } - - void revert(const xpath_allocator& state) - { - // free all new pages - xpath_memory_block* cur = _root; - - while (cur != state._root) - { - xpath_memory_block* next = cur->next; - - xml_memory::deallocate(cur); - - cur = next; - } - - // restore state - _root = state._root; - _root_size = state._root_size; - } - - void release() - { - xpath_memory_block* cur = _root; - assert(cur); - - while (cur->next) - { - xpath_memory_block* next = cur->next; - - xml_memory::deallocate(cur); - - cur = next; - } - } - }; - - struct xpath_allocator_capture - { - xpath_allocator_capture(xpath_allocator* alloc): _target(alloc), _state(*alloc) - { - } - - ~xpath_allocator_capture() - { - _target->revert(_state); - } - - xpath_allocator* _target; - xpath_allocator _state; - }; - - struct xpath_stack - { - xpath_allocator* result; - xpath_allocator* temp; - }; - - struct xpath_stack_data - { - xpath_memory_block blocks[2]; - xpath_allocator result; - xpath_allocator temp; - xpath_stack stack; - - #ifdef PUGIXML_NO_EXCEPTIONS - jmp_buf error_handler; - #endif - - xpath_stack_data(): result(blocks + 0), temp(blocks + 1) - { - blocks[0].next = blocks[1].next = 0; - blocks[0].capacity = blocks[1].capacity = sizeof(blocks[0].data); - - stack.result = &result; - stack.temp = &temp; - - #ifdef PUGIXML_NO_EXCEPTIONS - result.error_handler = temp.error_handler = &error_handler; - #endif - } - - ~xpath_stack_data() - { - result.release(); - temp.release(); - } - }; -PUGI__NS_END - -// String class -PUGI__NS_BEGIN - class xpath_string - { - const char_t* _buffer; - bool _uses_heap; - size_t _length_heap; - - static char_t* duplicate_string(const char_t* string, size_t length, xpath_allocator* alloc) - { - char_t* result = static_cast(alloc->allocate((length + 1) * sizeof(char_t))); - assert(result); - - memcpy(result, string, length * sizeof(char_t)); - result[length] = 0; - - return result; - } - - xpath_string(const char_t* buffer, bool uses_heap_, size_t length_heap): _buffer(buffer), _uses_heap(uses_heap_), _length_heap(length_heap) - { - } - - public: - static xpath_string from_const(const char_t* str) - { - return xpath_string(str, false, 0); - } - - static xpath_string from_heap_preallocated(const char_t* begin, const char_t* end) - { - assert(begin <= end && *end == 0); - - return xpath_string(begin, true, static_cast(end - begin)); - } - - static xpath_string from_heap(const char_t* begin, const char_t* end, xpath_allocator* alloc) - { - assert(begin <= end); - - size_t length = static_cast(end - begin); - - return length == 0 ? xpath_string() : xpath_string(duplicate_string(begin, length, alloc), true, length); - } - - xpath_string(): _buffer(PUGIXML_TEXT("")), _uses_heap(false), _length_heap(0) - { - } - - void append(const xpath_string& o, xpath_allocator* alloc) - { - // skip empty sources - if (!*o._buffer) return; - - // fast append for constant empty target and constant source - if (!*_buffer && !_uses_heap && !o._uses_heap) - { - _buffer = o._buffer; - } - else - { - // need to make heap copy - size_t target_length = length(); - size_t source_length = o.length(); - size_t result_length = target_length + source_length; - - // allocate new buffer - char_t* result = static_cast(alloc->reallocate(_uses_heap ? const_cast(_buffer) : 0, (target_length + 1) * sizeof(char_t), (result_length + 1) * sizeof(char_t))); - assert(result); - - // append first string to the new buffer in case there was no reallocation - if (!_uses_heap) memcpy(result, _buffer, target_length * sizeof(char_t)); - - // append second string to the new buffer - memcpy(result + target_length, o._buffer, source_length * sizeof(char_t)); - result[result_length] = 0; - - // finalize - _buffer = result; - _uses_heap = true; - _length_heap = result_length; - } - } - - const char_t* c_str() const - { - return _buffer; - } - - size_t length() const - { - return _uses_heap ? _length_heap : strlength(_buffer); - } - - char_t* data(xpath_allocator* alloc) - { - // make private heap copy - if (!_uses_heap) - { - size_t length_ = strlength(_buffer); - - _buffer = duplicate_string(_buffer, length_, alloc); - _uses_heap = true; - _length_heap = length_; - } - - return const_cast(_buffer); - } - - bool empty() const - { - return *_buffer == 0; - } - - bool operator==(const xpath_string& o) const - { - return strequal(_buffer, o._buffer); - } - - bool operator!=(const xpath_string& o) const - { - return !strequal(_buffer, o._buffer); - } - - bool uses_heap() const - { - return _uses_heap; - } - }; -PUGI__NS_END - -PUGI__NS_BEGIN - PUGI__FN bool starts_with(const char_t* string, const char_t* pattern) - { - while (*pattern && *string == *pattern) - { - string++; - pattern++; - } - - return *pattern == 0; - } - - PUGI__FN const char_t* find_char(const char_t* s, char_t c) - { - #ifdef PUGIXML_WCHAR_MODE - return wcschr(s, c); - #else - return strchr(s, c); - #endif - } - - PUGI__FN const char_t* find_substring(const char_t* s, const char_t* p) - { - #ifdef PUGIXML_WCHAR_MODE - // MSVC6 wcsstr bug workaround (if s is empty it always returns 0) - return (*p == 0) ? s : wcsstr(s, p); - #else - return strstr(s, p); - #endif - } - - // Converts symbol to lower case, if it is an ASCII one - PUGI__FN char_t tolower_ascii(char_t ch) - { - return static_cast(ch - 'A') < 26 ? static_cast(ch | ' ') : ch; - } - - PUGI__FN xpath_string string_value(const xpath_node& na, xpath_allocator* alloc) - { - if (na.attribute()) - return xpath_string::from_const(na.attribute().value()); - else - { - xml_node n = na.node(); - - switch (n.type()) - { - case node_pcdata: - case node_cdata: - case node_comment: - case node_pi: - return xpath_string::from_const(n.value()); - - case node_document: - case node_element: - { - xpath_string result; - - xml_node cur = n.first_child(); - - while (cur && cur != n) - { - if (cur.type() == node_pcdata || cur.type() == node_cdata) - result.append(xpath_string::from_const(cur.value()), alloc); - - if (cur.first_child()) - cur = cur.first_child(); - else if (cur.next_sibling()) - cur = cur.next_sibling(); - else - { - while (!cur.next_sibling() && cur != n) - cur = cur.parent(); - - if (cur != n) cur = cur.next_sibling(); - } - } - - return result; - } - - default: - return xpath_string(); - } - } - } - - PUGI__FN bool node_is_before_sibling(xml_node_struct* ln, xml_node_struct* rn) - { - assert(ln->parent == rn->parent); - - // there is no common ancestor (the shared parent is null), nodes are from different documents - if (!ln->parent) return ln < rn; - - // determine sibling order - xml_node_struct* ls = ln; - xml_node_struct* rs = rn; - - while (ls && rs) - { - if (ls == rn) return true; - if (rs == ln) return false; - - ls = ls->next_sibling; - rs = rs->next_sibling; - } - - // if rn sibling chain ended ln must be before rn - return !rs; - } - - PUGI__FN bool node_is_before(xml_node_struct* ln, xml_node_struct* rn) - { - // find common ancestor at the same depth, if any - xml_node_struct* lp = ln; - xml_node_struct* rp = rn; - - while (lp && rp && lp->parent != rp->parent) - { - lp = lp->parent; - rp = rp->parent; - } - - // parents are the same! - if (lp && rp) return node_is_before_sibling(lp, rp); - - // nodes are at different depths, need to normalize heights - bool left_higher = !lp; - - while (lp) - { - lp = lp->parent; - ln = ln->parent; - } - - while (rp) - { - rp = rp->parent; - rn = rn->parent; - } - - // one node is the ancestor of the other - if (ln == rn) return left_higher; - - // find common ancestor... again - while (ln->parent != rn->parent) - { - ln = ln->parent; - rn = rn->parent; - } - - return node_is_before_sibling(ln, rn); - } - - PUGI__FN bool node_is_ancestor(xml_node_struct* parent, xml_node_struct* node) - { - while (node && node != parent) node = node->parent; - - return parent && node == parent; - } - - PUGI__FN const void* document_buffer_order(const xpath_node& xnode) - { - xml_node_struct* node = xnode.node().internal_object(); - - if (node) - { - if ((get_document(node).header & xml_memory_page_contents_shared_mask) == 0) - { - if (node->name && (node->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0) return node->name; - if (node->value && (node->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0) return node->value; - } - - return 0; - } - - xml_attribute_struct* attr = xnode.attribute().internal_object(); - - if (attr) - { - if ((get_document(attr).header & xml_memory_page_contents_shared_mask) == 0) - { - if ((attr->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0) return attr->name; - if ((attr->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0) return attr->value; - } - - return 0; - } - - return 0; - } - - struct document_order_comparator - { - bool operator()(const xpath_node& lhs, const xpath_node& rhs) const - { - // optimized document order based check - const void* lo = document_buffer_order(lhs); - const void* ro = document_buffer_order(rhs); - - if (lo && ro) return lo < ro; - - // slow comparison - xml_node ln = lhs.node(), rn = rhs.node(); - - // compare attributes - if (lhs.attribute() && rhs.attribute()) - { - // shared parent - if (lhs.parent() == rhs.parent()) - { - // determine sibling order - for (xml_attribute a = lhs.attribute(); a; a = a.next_attribute()) - if (a == rhs.attribute()) - return true; - - return false; - } - - // compare attribute parents - ln = lhs.parent(); - rn = rhs.parent(); - } - else if (lhs.attribute()) - { - // attributes go after the parent element - if (lhs.parent() == rhs.node()) return false; - - ln = lhs.parent(); - } - else if (rhs.attribute()) - { - // attributes go after the parent element - if (rhs.parent() == lhs.node()) return true; - - rn = rhs.parent(); - } - - if (ln == rn) return false; - - if (!ln || !rn) return ln < rn; - - return node_is_before(ln.internal_object(), rn.internal_object()); - } - }; - - struct duplicate_comparator - { - bool operator()(const xpath_node& lhs, const xpath_node& rhs) const - { - if (lhs.attribute()) return rhs.attribute() ? lhs.attribute() < rhs.attribute() : true; - else return rhs.attribute() ? false : lhs.node() < rhs.node(); - } - }; - - PUGI__FN double gen_nan() - { - #if defined(__STDC_IEC_559__) || ((FLT_RADIX - 0 == 2) && (FLT_MAX_EXP - 0 == 128) && (FLT_MANT_DIG - 0 == 24)) - union { float f; uint32_t i; } u[sizeof(float) == sizeof(uint32_t) ? 1 : -1]; - u[0].i = 0x7fc00000; - return u[0].f; - #else - // fallback - const volatile double zero = 0.0; - return zero / zero; - #endif - } - - PUGI__FN bool is_nan(double value) - { - #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) - return !!_isnan(value); - #elif defined(fpclassify) && defined(FP_NAN) - return fpclassify(value) == FP_NAN; - #else - // fallback - const volatile double v = value; - return v != v; - #endif - } - - PUGI__FN const char_t* convert_number_to_string_special(double value) - { - #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) - if (_finite(value)) return (value == 0) ? PUGIXML_TEXT("0") : 0; - if (_isnan(value)) return PUGIXML_TEXT("NaN"); - return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); - #elif defined(fpclassify) && defined(FP_NAN) && defined(FP_INFINITE) && defined(FP_ZERO) - switch (fpclassify(value)) - { - case FP_NAN: - return PUGIXML_TEXT("NaN"); - - case FP_INFINITE: - return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); - - case FP_ZERO: - return PUGIXML_TEXT("0"); - - default: - return 0; - } - #else - // fallback - const volatile double v = value; - - if (v == 0) return PUGIXML_TEXT("0"); - if (v != v) return PUGIXML_TEXT("NaN"); - if (v * 2 == v) return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); - return 0; - #endif - } - - PUGI__FN bool convert_number_to_boolean(double value) - { - return (value != 0 && !is_nan(value)); - } - - PUGI__FN void truncate_zeros(char* begin, char* end) - { - while (begin != end && end[-1] == '0') end--; - - *end = 0; - } - - // gets mantissa digits in the form of 0.xxxxx with 0. implied and the exponent -#if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE) - PUGI__FN void convert_number_to_mantissa_exponent(double value, char* buffer, size_t buffer_size, char** out_mantissa, int* out_exponent) - { - // get base values - int sign, exponent; - _ecvt_s(buffer, buffer_size, value, DBL_DIG + 1, &exponent, &sign); - - // truncate redundant zeros - truncate_zeros(buffer, buffer + strlen(buffer)); - - // fill results - *out_mantissa = buffer; - *out_exponent = exponent; - } -#else - PUGI__FN void convert_number_to_mantissa_exponent(double value, char* buffer, size_t buffer_size, char** out_mantissa, int* out_exponent) - { - // get a scientific notation value with IEEE DBL_DIG decimals - sprintf(buffer, "%.*e", DBL_DIG, value); - assert(strlen(buffer) < buffer_size); - (void)!buffer_size; - - // get the exponent (possibly negative) - char* exponent_string = strchr(buffer, 'e'); - assert(exponent_string); - - int exponent = atoi(exponent_string + 1); - - // extract mantissa string: skip sign - char* mantissa = buffer[0] == '-' ? buffer + 1 : buffer; - assert(mantissa[0] != '0' && mantissa[1] == '.'); - - // divide mantissa by 10 to eliminate integer part - mantissa[1] = mantissa[0]; - mantissa++; - exponent++; - - // remove extra mantissa digits and zero-terminate mantissa - truncate_zeros(mantissa, exponent_string); - - // fill results - *out_mantissa = mantissa; - *out_exponent = exponent; - } -#endif - - PUGI__FN xpath_string convert_number_to_string(double value, xpath_allocator* alloc) - { - // try special number conversion - const char_t* special = convert_number_to_string_special(value); - if (special) return xpath_string::from_const(special); - - // get mantissa + exponent form - char mantissa_buffer[32]; - - char* mantissa; - int exponent; - convert_number_to_mantissa_exponent(value, mantissa_buffer, sizeof(mantissa_buffer), &mantissa, &exponent); - - // allocate a buffer of suitable length for the number - size_t result_size = strlen(mantissa_buffer) + (exponent > 0 ? exponent : -exponent) + 4; - char_t* result = static_cast(alloc->allocate(sizeof(char_t) * result_size)); - assert(result); - - // make the number! - char_t* s = result; - - // sign - if (value < 0) *s++ = '-'; - - // integer part - if (exponent <= 0) - { - *s++ = '0'; - } - else - { - while (exponent > 0) - { - assert(*mantissa == 0 || static_cast(static_cast(*mantissa) - '0') <= 9); - *s++ = *mantissa ? *mantissa++ : '0'; - exponent--; - } - } - - // fractional part - if (*mantissa) - { - // decimal point - *s++ = '.'; - - // extra zeroes from negative exponent - while (exponent < 0) - { - *s++ = '0'; - exponent++; - } - - // extra mantissa digits - while (*mantissa) - { - assert(static_cast(*mantissa - '0') <= 9); - *s++ = *mantissa++; - } - } - - // zero-terminate - assert(s < result + result_size); - *s = 0; - - return xpath_string::from_heap_preallocated(result, s); - } - - PUGI__FN bool check_string_to_number_format(const char_t* string) - { - // parse leading whitespace - while (PUGI__IS_CHARTYPE(*string, ct_space)) ++string; - - // parse sign - if (*string == '-') ++string; - - if (!*string) return false; - - // if there is no integer part, there should be a decimal part with at least one digit - if (!PUGI__IS_CHARTYPEX(string[0], ctx_digit) && (string[0] != '.' || !PUGI__IS_CHARTYPEX(string[1], ctx_digit))) return false; - - // parse integer part - while (PUGI__IS_CHARTYPEX(*string, ctx_digit)) ++string; - - // parse decimal part - if (*string == '.') - { - ++string; - - while (PUGI__IS_CHARTYPEX(*string, ctx_digit)) ++string; - } - - // parse trailing whitespace - while (PUGI__IS_CHARTYPE(*string, ct_space)) ++string; - - return *string == 0; - } - - PUGI__FN double convert_string_to_number(const char_t* string) - { - // check string format - if (!check_string_to_number_format(string)) return gen_nan(); - - // parse string - #ifdef PUGIXML_WCHAR_MODE - return wcstod(string, 0); - #else - return atof(string); - #endif - } - - PUGI__FN bool convert_string_to_number_scratch(char_t (&buffer)[32], const char_t* begin, const char_t* end, double* out_result) - { - size_t length = static_cast(end - begin); - char_t* scratch = buffer; - - if (length >= sizeof(buffer) / sizeof(buffer[0])) - { - // need to make dummy on-heap copy - scratch = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!scratch) return false; - } - - // copy string to zero-terminated buffer and perform conversion - memcpy(scratch, begin, length * sizeof(char_t)); - scratch[length] = 0; - - *out_result = convert_string_to_number(scratch); - - // free dummy buffer - if (scratch != buffer) xml_memory::deallocate(scratch); - - return true; - } - - PUGI__FN double round_nearest(double value) - { - return floor(value + 0.5); - } - - PUGI__FN double round_nearest_nzero(double value) - { - // same as round_nearest, but returns -0 for [-0.5, -0] - // ceil is used to differentiate between +0 and -0 (we return -0 for [-0.5, -0] and +0 for +0) - return (value >= -0.5 && value <= 0) ? ceil(value) : floor(value + 0.5); - } - - PUGI__FN const char_t* qualified_name(const xpath_node& node) - { - return node.attribute() ? node.attribute().name() : node.node().name(); - } - - PUGI__FN const char_t* local_name(const xpath_node& node) - { - const char_t* name = qualified_name(node); - const char_t* p = find_char(name, ':'); - - return p ? p + 1 : name; - } - - struct namespace_uri_predicate - { - const char_t* prefix; - size_t prefix_length; - - namespace_uri_predicate(const char_t* name) - { - const char_t* pos = find_char(name, ':'); - - prefix = pos ? name : 0; - prefix_length = pos ? static_cast(pos - name) : 0; - } - - bool operator()(xml_attribute a) const - { - const char_t* name = a.name(); - - if (!starts_with(name, PUGIXML_TEXT("xmlns"))) return false; - - return prefix ? name[5] == ':' && strequalrange(name + 6, prefix, prefix_length) : name[5] == 0; - } - }; - - PUGI__FN const char_t* namespace_uri(xml_node node) - { - namespace_uri_predicate pred = node.name(); - - xml_node p = node; - - while (p) - { - xml_attribute a = p.find_attribute(pred); - - if (a) return a.value(); - - p = p.parent(); - } - - return PUGIXML_TEXT(""); - } - - PUGI__FN const char_t* namespace_uri(xml_attribute attr, xml_node parent) - { - namespace_uri_predicate pred = attr.name(); - - // Default namespace does not apply to attributes - if (!pred.prefix) return PUGIXML_TEXT(""); - - xml_node p = parent; - - while (p) - { - xml_attribute a = p.find_attribute(pred); - - if (a) return a.value(); - - p = p.parent(); - } - - return PUGIXML_TEXT(""); - } - - PUGI__FN const char_t* namespace_uri(const xpath_node& node) - { - return node.attribute() ? namespace_uri(node.attribute(), node.parent()) : namespace_uri(node.node()); - } - - PUGI__FN char_t* normalize_space(char_t* buffer) - { - char_t* write = buffer; - - for (char_t* it = buffer; *it; ) - { - char_t ch = *it++; - - if (PUGI__IS_CHARTYPE(ch, ct_space)) - { - // replace whitespace sequence with single space - while (PUGI__IS_CHARTYPE(*it, ct_space)) it++; - - // avoid leading spaces - if (write != buffer) *write++ = ' '; - } - else *write++ = ch; - } - - // remove trailing space - if (write != buffer && PUGI__IS_CHARTYPE(write[-1], ct_space)) write--; - - // zero-terminate - *write = 0; - - return write; - } - - PUGI__FN char_t* translate(char_t* buffer, const char_t* from, const char_t* to, size_t to_length) - { - char_t* write = buffer; - - while (*buffer) - { - PUGI__DMC_VOLATILE char_t ch = *buffer++; - - const char_t* pos = find_char(from, ch); - - if (!pos) - *write++ = ch; // do not process - else if (static_cast(pos - from) < to_length) - *write++ = to[pos - from]; // replace - } - - // zero-terminate - *write = 0; - - return write; - } - - PUGI__FN unsigned char* translate_table_generate(xpath_allocator* alloc, const char_t* from, const char_t* to) - { - unsigned char table[128] = {0}; - - while (*from) - { - unsigned int fc = static_cast(*from); - unsigned int tc = static_cast(*to); - - if (fc >= 128 || tc >= 128) - return 0; - - // code=128 means "skip character" - if (!table[fc]) - table[fc] = static_cast(tc ? tc : 128); - - from++; - if (tc) to++; - } - - for (int i = 0; i < 128; ++i) - if (!table[i]) - table[i] = static_cast(i); - - void* result = alloc->allocate_nothrow(sizeof(table)); - - if (result) - { - memcpy(result, table, sizeof(table)); - } - - return static_cast(result); - } - - PUGI__FN char_t* translate_table(char_t* buffer, const unsigned char* table) - { - char_t* write = buffer; - - while (*buffer) - { - char_t ch = *buffer++; - unsigned int index = static_cast(ch); - - if (index < 128) - { - unsigned char code = table[index]; - - // code=128 means "skip character" (table size is 128 so 128 can be a special value) - // this code skips these characters without extra branches - *write = static_cast(code); - write += 1 - (code >> 7); - } - else - { - *write++ = ch; - } - } - - // zero-terminate - *write = 0; - - return write; - } - - inline bool is_xpath_attribute(const char_t* name) - { - return !(starts_with(name, PUGIXML_TEXT("xmlns")) && (name[5] == 0 || name[5] == ':')); - } - - struct xpath_variable_boolean: xpath_variable - { - xpath_variable_boolean(): value(false) - { - } - - bool value; - char_t name[1]; - }; - - struct xpath_variable_number: xpath_variable - { - xpath_variable_number(): value(0) - { - } - - double value; - char_t name[1]; - }; - - struct xpath_variable_string: xpath_variable - { - xpath_variable_string(): value(0) - { - } - - ~xpath_variable_string() - { - if (value) xml_memory::deallocate(value); - } - - char_t* value; - char_t name[1]; - }; - - struct xpath_variable_node_set: xpath_variable - { - xpath_node_set value; - char_t name[1]; - }; - - static const xpath_node_set dummy_node_set; - - PUGI__FN unsigned int hash_string(const char_t* str) - { - // Jenkins one-at-a-time hash (http://en.wikipedia.org/wiki/Jenkins_hash_function#one-at-a-time) - unsigned int result = 0; - - while (*str) - { - result += static_cast(*str++); - result += result << 10; - result ^= result >> 6; - } - - result += result << 3; - result ^= result >> 11; - result += result << 15; - - return result; - } - - template PUGI__FN T* new_xpath_variable(const char_t* name) - { - size_t length = strlength(name); - if (length == 0) return 0; // empty variable names are invalid - - // $$ we can't use offsetof(T, name) because T is non-POD, so we just allocate additional length characters - void* memory = xml_memory::allocate(sizeof(T) + length * sizeof(char_t)); - if (!memory) return 0; - - T* result = new (memory) T(); - - memcpy(result->name, name, (length + 1) * sizeof(char_t)); - - return result; - } - - PUGI__FN xpath_variable* new_xpath_variable(xpath_value_type type, const char_t* name) - { - switch (type) - { - case xpath_type_node_set: - return new_xpath_variable(name); - - case xpath_type_number: - return new_xpath_variable(name); - - case xpath_type_string: - return new_xpath_variable(name); - - case xpath_type_boolean: - return new_xpath_variable(name); - - default: - return 0; - } - } - - template PUGI__FN void delete_xpath_variable(T* var) - { - var->~T(); - xml_memory::deallocate(var); - } - - PUGI__FN void delete_xpath_variable(xpath_value_type type, xpath_variable* var) - { - switch (type) - { - case xpath_type_node_set: - delete_xpath_variable(static_cast(var)); - break; - - case xpath_type_number: - delete_xpath_variable(static_cast(var)); - break; - - case xpath_type_string: - delete_xpath_variable(static_cast(var)); - break; - - case xpath_type_boolean: - delete_xpath_variable(static_cast(var)); - break; - - default: - assert(!"Invalid variable type"); - } - } - - PUGI__FN xpath_variable* get_variable_scratch(char_t (&buffer)[32], xpath_variable_set* set, const char_t* begin, const char_t* end) - { - size_t length = static_cast(end - begin); - char_t* scratch = buffer; - - if (length >= sizeof(buffer) / sizeof(buffer[0])) - { - // need to make dummy on-heap copy - scratch = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); - if (!scratch) return 0; - } - - // copy string to zero-terminated buffer and perform lookup - memcpy(scratch, begin, length * sizeof(char_t)); - scratch[length] = 0; - - xpath_variable* result = set->get(scratch); - - // free dummy buffer - if (scratch != buffer) xml_memory::deallocate(scratch); - - return result; - } -PUGI__NS_END - -// Internal node set class -PUGI__NS_BEGIN - PUGI__FN xpath_node_set::type_t xpath_get_order(const xpath_node* begin, const xpath_node* end) - { - if (end - begin < 2) - return xpath_node_set::type_sorted; - - document_order_comparator cmp; - - bool first = cmp(begin[0], begin[1]); - - for (const xpath_node* it = begin + 1; it + 1 < end; ++it) - if (cmp(it[0], it[1]) != first) - return xpath_node_set::type_unsorted; - - return first ? xpath_node_set::type_sorted : xpath_node_set::type_sorted_reverse; - } - - PUGI__FN xpath_node_set::type_t xpath_sort(xpath_node* begin, xpath_node* end, xpath_node_set::type_t type, bool rev) - { - xpath_node_set::type_t order = rev ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted; - - if (type == xpath_node_set::type_unsorted) - { - xpath_node_set::type_t sorted = xpath_get_order(begin, end); - - if (sorted == xpath_node_set::type_unsorted) - { - sort(begin, end, document_order_comparator()); - - type = xpath_node_set::type_sorted; - } - else - type = sorted; - } - - if (type != order) reverse(begin, end); - - return order; - } - - PUGI__FN xpath_node xpath_first(const xpath_node* begin, const xpath_node* end, xpath_node_set::type_t type) - { - if (begin == end) return xpath_node(); - - switch (type) - { - case xpath_node_set::type_sorted: - return *begin; - - case xpath_node_set::type_sorted_reverse: - return *(end - 1); - - case xpath_node_set::type_unsorted: - return *min_element(begin, end, document_order_comparator()); - - default: - assert(!"Invalid node set type"); - return xpath_node(); - } - } - - class xpath_node_set_raw - { - xpath_node_set::type_t _type; - - xpath_node* _begin; - xpath_node* _end; - xpath_node* _eos; - - public: - xpath_node_set_raw(): _type(xpath_node_set::type_unsorted), _begin(0), _end(0), _eos(0) - { - } - - xpath_node* begin() const - { - return _begin; - } - - xpath_node* end() const - { - return _end; - } - - bool empty() const - { - return _begin == _end; - } - - size_t size() const - { - return static_cast(_end - _begin); - } - - xpath_node first() const - { - return xpath_first(_begin, _end, _type); - } - - void push_back_grow(const xpath_node& node, xpath_allocator* alloc); - - void push_back(const xpath_node& node, xpath_allocator* alloc) - { - if (_end != _eos) - *_end++ = node; - else - push_back_grow(node, alloc); - } - - void append(const xpath_node* begin_, const xpath_node* end_, xpath_allocator* alloc) - { - if (begin_ == end_) return; - - size_t size_ = static_cast(_end - _begin); - size_t capacity = static_cast(_eos - _begin); - size_t count = static_cast(end_ - begin_); - - if (size_ + count > capacity) - { - // reallocate the old array or allocate a new one - xpath_node* data = static_cast(alloc->reallocate(_begin, capacity * sizeof(xpath_node), (size_ + count) * sizeof(xpath_node))); - assert(data); - - // finalize - _begin = data; - _end = data + size_; - _eos = data + size_ + count; - } - - memcpy(_end, begin_, count * sizeof(xpath_node)); - _end += count; - } - - void sort_do() - { - _type = xpath_sort(_begin, _end, _type, false); - } - - void truncate(xpath_node* pos) - { - assert(_begin <= pos && pos <= _end); - - _end = pos; - } - - void remove_duplicates() - { - if (_type == xpath_node_set::type_unsorted) - sort(_begin, _end, duplicate_comparator()); - - _end = unique(_begin, _end); - } - - xpath_node_set::type_t type() const - { - return _type; - } - - void set_type(xpath_node_set::type_t value) - { - _type = value; - } - }; - - PUGI__FN_NO_INLINE void xpath_node_set_raw::push_back_grow(const xpath_node& node, xpath_allocator* alloc) - { - size_t capacity = static_cast(_eos - _begin); - - // get new capacity (1.5x rule) - size_t new_capacity = capacity + capacity / 2 + 1; - - // reallocate the old array or allocate a new one - xpath_node* data = static_cast(alloc->reallocate(_begin, capacity * sizeof(xpath_node), new_capacity * sizeof(xpath_node))); - assert(data); - - // finalize - _begin = data; - _end = data + capacity; - _eos = data + new_capacity; - - // push - *_end++ = node; - } -PUGI__NS_END - -PUGI__NS_BEGIN - struct xpath_context - { - xpath_node n; - size_t position, size; - - xpath_context(const xpath_node& n_, size_t position_, size_t size_): n(n_), position(position_), size(size_) - { - } - }; - - enum lexeme_t - { - lex_none = 0, - lex_equal, - lex_not_equal, - lex_less, - lex_greater, - lex_less_or_equal, - lex_greater_or_equal, - lex_plus, - lex_minus, - lex_multiply, - lex_union, - lex_var_ref, - lex_open_brace, - lex_close_brace, - lex_quoted_string, - lex_number, - lex_slash, - lex_double_slash, - lex_open_square_brace, - lex_close_square_brace, - lex_string, - lex_comma, - lex_axis_attribute, - lex_dot, - lex_double_dot, - lex_double_colon, - lex_eof - }; - - struct xpath_lexer_string - { - const char_t* begin; - const char_t* end; - - xpath_lexer_string(): begin(0), end(0) - { - } - - bool operator==(const char_t* other) const - { - size_t length = static_cast(end - begin); - - return strequalrange(other, begin, length); - } - }; - - class xpath_lexer - { - const char_t* _cur; - const char_t* _cur_lexeme_pos; - xpath_lexer_string _cur_lexeme_contents; - - lexeme_t _cur_lexeme; - - public: - explicit xpath_lexer(const char_t* query): _cur(query) - { - next(); - } - - const char_t* state() const - { - return _cur; - } - - void next() - { - const char_t* cur = _cur; - - while (PUGI__IS_CHARTYPE(*cur, ct_space)) ++cur; - - // save lexeme position for error reporting - _cur_lexeme_pos = cur; - - switch (*cur) - { - case 0: - _cur_lexeme = lex_eof; - break; - - case '>': - if (*(cur+1) == '=') - { - cur += 2; - _cur_lexeme = lex_greater_or_equal; - } - else - { - cur += 1; - _cur_lexeme = lex_greater; - } - break; - - case '<': - if (*(cur+1) == '=') - { - cur += 2; - _cur_lexeme = lex_less_or_equal; - } - else - { - cur += 1; - _cur_lexeme = lex_less; - } - break; - - case '!': - if (*(cur+1) == '=') - { - cur += 2; - _cur_lexeme = lex_not_equal; - } - else - { - _cur_lexeme = lex_none; - } - break; - - case '=': - cur += 1; - _cur_lexeme = lex_equal; - - break; - - case '+': - cur += 1; - _cur_lexeme = lex_plus; - - break; - - case '-': - cur += 1; - _cur_lexeme = lex_minus; - - break; - - case '*': - cur += 1; - _cur_lexeme = lex_multiply; - - break; - - case '|': - cur += 1; - _cur_lexeme = lex_union; - - break; - - case '$': - cur += 1; - - if (PUGI__IS_CHARTYPEX(*cur, ctx_start_symbol)) - { - _cur_lexeme_contents.begin = cur; - - while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; - - if (cur[0] == ':' && PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // qname - { - cur++; // : - - while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; - } - - _cur_lexeme_contents.end = cur; - - _cur_lexeme = lex_var_ref; - } - else - { - _cur_lexeme = lex_none; - } - - break; - - case '(': - cur += 1; - _cur_lexeme = lex_open_brace; - - break; - - case ')': - cur += 1; - _cur_lexeme = lex_close_brace; - - break; - - case '[': - cur += 1; - _cur_lexeme = lex_open_square_brace; - - break; - - case ']': - cur += 1; - _cur_lexeme = lex_close_square_brace; - - break; - - case ',': - cur += 1; - _cur_lexeme = lex_comma; - - break; - - case '/': - if (*(cur+1) == '/') - { - cur += 2; - _cur_lexeme = lex_double_slash; - } - else - { - cur += 1; - _cur_lexeme = lex_slash; - } - break; - - case '.': - if (*(cur+1) == '.') - { - cur += 2; - _cur_lexeme = lex_double_dot; - } - else if (PUGI__IS_CHARTYPEX(*(cur+1), ctx_digit)) - { - _cur_lexeme_contents.begin = cur; // . - - ++cur; - - while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++; - - _cur_lexeme_contents.end = cur; - - _cur_lexeme = lex_number; - } - else - { - cur += 1; - _cur_lexeme = lex_dot; - } - break; - - case '@': - cur += 1; - _cur_lexeme = lex_axis_attribute; - - break; - - case '"': - case '\'': - { - char_t terminator = *cur; - - ++cur; - - _cur_lexeme_contents.begin = cur; - while (*cur && *cur != terminator) cur++; - _cur_lexeme_contents.end = cur; - - if (!*cur) - _cur_lexeme = lex_none; - else - { - cur += 1; - _cur_lexeme = lex_quoted_string; - } - - break; - } - - case ':': - if (*(cur+1) == ':') - { - cur += 2; - _cur_lexeme = lex_double_colon; - } - else - { - _cur_lexeme = lex_none; - } - break; - - default: - if (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) - { - _cur_lexeme_contents.begin = cur; - - while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++; - - if (*cur == '.') - { - cur++; - - while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++; - } - - _cur_lexeme_contents.end = cur; - - _cur_lexeme = lex_number; - } - else if (PUGI__IS_CHARTYPEX(*cur, ctx_start_symbol)) - { - _cur_lexeme_contents.begin = cur; - - while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; - - if (cur[0] == ':') - { - if (cur[1] == '*') // namespace test ncname:* - { - cur += 2; // :* - } - else if (PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // namespace test qname - { - cur++; // : - - while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; - } - } - - _cur_lexeme_contents.end = cur; - - _cur_lexeme = lex_string; - } - else - { - _cur_lexeme = lex_none; - } - } - - _cur = cur; - } - - lexeme_t current() const - { - return _cur_lexeme; - } - - const char_t* current_pos() const - { - return _cur_lexeme_pos; - } - - const xpath_lexer_string& contents() const - { - assert(_cur_lexeme == lex_var_ref || _cur_lexeme == lex_number || _cur_lexeme == lex_string || _cur_lexeme == lex_quoted_string); - - return _cur_lexeme_contents; - } - }; - - enum ast_type_t - { - ast_unknown, - ast_op_or, // left or right - ast_op_and, // left and right - ast_op_equal, // left = right - ast_op_not_equal, // left != right - ast_op_less, // left < right - ast_op_greater, // left > right - ast_op_less_or_equal, // left <= right - ast_op_greater_or_equal, // left >= right - ast_op_add, // left + right - ast_op_subtract, // left - right - ast_op_multiply, // left * right - ast_op_divide, // left / right - ast_op_mod, // left % right - ast_op_negate, // left - right - ast_op_union, // left | right - ast_predicate, // apply predicate to set; next points to next predicate - ast_filter, // select * from left where right - ast_string_constant, // string constant - ast_number_constant, // number constant - ast_variable, // variable - ast_func_last, // last() - ast_func_position, // position() - ast_func_count, // count(left) - ast_func_id, // id(left) - ast_func_local_name_0, // local-name() - ast_func_local_name_1, // local-name(left) - ast_func_namespace_uri_0, // namespace-uri() - ast_func_namespace_uri_1, // namespace-uri(left) - ast_func_name_0, // name() - ast_func_name_1, // name(left) - ast_func_string_0, // string() - ast_func_string_1, // string(left) - ast_func_concat, // concat(left, right, siblings) - ast_func_starts_with, // starts_with(left, right) - ast_func_contains, // contains(left, right) - ast_func_substring_before, // substring-before(left, right) - ast_func_substring_after, // substring-after(left, right) - ast_func_substring_2, // substring(left, right) - ast_func_substring_3, // substring(left, right, third) - ast_func_string_length_0, // string-length() - ast_func_string_length_1, // string-length(left) - ast_func_normalize_space_0, // normalize-space() - ast_func_normalize_space_1, // normalize-space(left) - ast_func_translate, // translate(left, right, third) - ast_func_boolean, // boolean(left) - ast_func_not, // not(left) - ast_func_true, // true() - ast_func_false, // false() - ast_func_lang, // lang(left) - ast_func_number_0, // number() - ast_func_number_1, // number(left) - ast_func_sum, // sum(left) - ast_func_floor, // floor(left) - ast_func_ceiling, // ceiling(left) - ast_func_round, // round(left) - ast_step, // process set left with step - ast_step_root, // select root node - - ast_opt_translate_table, // translate(left, right, third) where right/third are constants - ast_opt_compare_attribute // @name = 'string' - }; - - enum axis_t - { - axis_ancestor, - axis_ancestor_or_self, - axis_attribute, - axis_child, - axis_descendant, - axis_descendant_or_self, - axis_following, - axis_following_sibling, - axis_namespace, - axis_parent, - axis_preceding, - axis_preceding_sibling, - axis_self - }; - - enum nodetest_t - { - nodetest_none, - nodetest_name, - nodetest_type_node, - nodetest_type_comment, - nodetest_type_pi, - nodetest_type_text, - nodetest_pi, - nodetest_all, - nodetest_all_in_namespace - }; - - enum predicate_t - { - predicate_default, - predicate_posinv, - predicate_constant, - predicate_constant_one - }; - - enum nodeset_eval_t - { - nodeset_eval_all, - nodeset_eval_any, - nodeset_eval_first - }; - - template struct axis_to_type - { - static const axis_t axis; - }; - - template const axis_t axis_to_type::axis = N; - - class xpath_ast_node - { - private: - // node type - char _type; - char _rettype; - - // for ast_step - char _axis; - - // for ast_step/ast_predicate/ast_filter - char _test; - - // tree node structure - xpath_ast_node* _left; - xpath_ast_node* _right; - xpath_ast_node* _next; - - union - { - // value for ast_string_constant - const char_t* string; - // value for ast_number_constant - double number; - // variable for ast_variable - xpath_variable* variable; - // node test for ast_step (node name/namespace/node type/pi target) - const char_t* nodetest; - // table for ast_opt_translate_table - const unsigned char* table; - } _data; - - xpath_ast_node(const xpath_ast_node&); - xpath_ast_node& operator=(const xpath_ast_node&); - - template static bool compare_eq(xpath_ast_node* lhs, xpath_ast_node* rhs, const xpath_context& c, const xpath_stack& stack, const Comp& comp) - { - xpath_value_type lt = lhs->rettype(), rt = rhs->rettype(); - - if (lt != xpath_type_node_set && rt != xpath_type_node_set) - { - if (lt == xpath_type_boolean || rt == xpath_type_boolean) - return comp(lhs->eval_boolean(c, stack), rhs->eval_boolean(c, stack)); - else if (lt == xpath_type_number || rt == xpath_type_number) - return comp(lhs->eval_number(c, stack), rhs->eval_number(c, stack)); - else if (lt == xpath_type_string || rt == xpath_type_string) - { - xpath_allocator_capture cr(stack.result); - - xpath_string ls = lhs->eval_string(c, stack); - xpath_string rs = rhs->eval_string(c, stack); - - return comp(ls, rs); - } - } - else if (lt == xpath_type_node_set && rt == xpath_type_node_set) - { - xpath_allocator_capture cr(stack.result); - - xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all); - xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); - - for (const xpath_node* li = ls.begin(); li != ls.end(); ++li) - for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) - { - xpath_allocator_capture cri(stack.result); - - if (comp(string_value(*li, stack.result), string_value(*ri, stack.result))) - return true; - } - - return false; - } - else - { - if (lt == xpath_type_node_set) - { - swap(lhs, rhs); - swap(lt, rt); - } - - if (lt == xpath_type_boolean) - return comp(lhs->eval_boolean(c, stack), rhs->eval_boolean(c, stack)); - else if (lt == xpath_type_number) - { - xpath_allocator_capture cr(stack.result); - - double l = lhs->eval_number(c, stack); - xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); - - for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) - { - xpath_allocator_capture cri(stack.result); - - if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) - return true; - } - - return false; - } - else if (lt == xpath_type_string) - { - xpath_allocator_capture cr(stack.result); - - xpath_string l = lhs->eval_string(c, stack); - xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); - - for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) - { - xpath_allocator_capture cri(stack.result); - - if (comp(l, string_value(*ri, stack.result))) - return true; - } - - return false; - } - } - - assert(!"Wrong types"); - return false; - } - - static bool eval_once(xpath_node_set::type_t type, nodeset_eval_t eval) - { - return type == xpath_node_set::type_sorted ? eval != nodeset_eval_all : eval == nodeset_eval_any; - } - - template static bool compare_rel(xpath_ast_node* lhs, xpath_ast_node* rhs, const xpath_context& c, const xpath_stack& stack, const Comp& comp) - { - xpath_value_type lt = lhs->rettype(), rt = rhs->rettype(); - - if (lt != xpath_type_node_set && rt != xpath_type_node_set) - return comp(lhs->eval_number(c, stack), rhs->eval_number(c, stack)); - else if (lt == xpath_type_node_set && rt == xpath_type_node_set) - { - xpath_allocator_capture cr(stack.result); - - xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all); - xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); - - for (const xpath_node* li = ls.begin(); li != ls.end(); ++li) - { - xpath_allocator_capture cri(stack.result); - - double l = convert_string_to_number(string_value(*li, stack.result).c_str()); - - for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) - { - xpath_allocator_capture crii(stack.result); - - if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) - return true; - } - } - - return false; - } - else if (lt != xpath_type_node_set && rt == xpath_type_node_set) - { - xpath_allocator_capture cr(stack.result); - - double l = lhs->eval_number(c, stack); - xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); - - for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) - { - xpath_allocator_capture cri(stack.result); - - if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) - return true; - } - - return false; - } - else if (lt == xpath_type_node_set && rt != xpath_type_node_set) - { - xpath_allocator_capture cr(stack.result); - - xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all); - double r = rhs->eval_number(c, stack); - - for (const xpath_node* li = ls.begin(); li != ls.end(); ++li) - { - xpath_allocator_capture cri(stack.result); - - if (comp(convert_string_to_number(string_value(*li, stack.result).c_str()), r)) - return true; - } - - return false; - } - else - { - assert(!"Wrong types"); - return false; - } - } - - static void apply_predicate_boolean(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack, bool once) - { - assert(ns.size() >= first); - assert(expr->rettype() != xpath_type_number); - - size_t i = 1; - size_t size = ns.size() - first; - - xpath_node* last = ns.begin() + first; - - // remove_if... or well, sort of - for (xpath_node* it = last; it != ns.end(); ++it, ++i) - { - xpath_context c(*it, i, size); - - if (expr->eval_boolean(c, stack)) - { - *last++ = *it; - - if (once) break; - } - } - - ns.truncate(last); - } - - static void apply_predicate_number(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack, bool once) - { - assert(ns.size() >= first); - assert(expr->rettype() == xpath_type_number); - - size_t i = 1; - size_t size = ns.size() - first; - - xpath_node* last = ns.begin() + first; - - // remove_if... or well, sort of - for (xpath_node* it = last; it != ns.end(); ++it, ++i) - { - xpath_context c(*it, i, size); - - if (expr->eval_number(c, stack) == i) - { - *last++ = *it; - - if (once) break; - } - } - - ns.truncate(last); - } - - static void apply_predicate_number_const(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack) - { - assert(ns.size() >= first); - assert(expr->rettype() == xpath_type_number); - - size_t size = ns.size() - first; - - xpath_node* last = ns.begin() + first; - - xpath_context c(xpath_node(), 1, size); - - double er = expr->eval_number(c, stack); - - if (er >= 1.0 && er <= size) - { - size_t eri = static_cast(er); - - if (er == eri) - { - xpath_node r = last[eri - 1]; - - *last++ = r; - } - } - - ns.truncate(last); - } - - void apply_predicate(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack, bool once) - { - if (ns.size() == first) return; - - assert(_type == ast_filter || _type == ast_predicate); - - if (_test == predicate_constant || _test == predicate_constant_one) - apply_predicate_number_const(ns, first, _right, stack); - else if (_right->rettype() == xpath_type_number) - apply_predicate_number(ns, first, _right, stack, once); - else - apply_predicate_boolean(ns, first, _right, stack, once); - } - - void apply_predicates(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack, nodeset_eval_t eval) - { - if (ns.size() == first) return; - - bool last_once = eval_once(ns.type(), eval); - - for (xpath_ast_node* pred = _right; pred; pred = pred->_next) - pred->apply_predicate(ns, first, stack, !pred->_next && last_once); - } - - bool step_push(xpath_node_set_raw& ns, xml_attribute_struct* a, xml_node_struct* parent, xpath_allocator* alloc) - { - assert(a); - - const char_t* name = a->name ? a->name : PUGIXML_TEXT(""); - - switch (_test) - { - case nodetest_name: - if (strequal(name, _data.nodetest) && is_xpath_attribute(name)) - { - ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc); - return true; - } - break; - - case nodetest_type_node: - case nodetest_all: - if (is_xpath_attribute(name)) - { - ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc); - return true; - } - break; - - case nodetest_all_in_namespace: - if (starts_with(name, _data.nodetest) && is_xpath_attribute(name)) - { - ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc); - return true; - } - break; - - default: - ; - } - - return false; - } - - bool step_push(xpath_node_set_raw& ns, xml_node_struct* n, xpath_allocator* alloc) - { - assert(n); - - xml_node_type type = PUGI__NODETYPE(n); - - switch (_test) - { - case nodetest_name: - if (type == node_element && n->name && strequal(n->name, _data.nodetest)) - { - ns.push_back(xml_node(n), alloc); - return true; - } - break; - - case nodetest_type_node: - ns.push_back(xml_node(n), alloc); - return true; - - case nodetest_type_comment: - if (type == node_comment) - { - ns.push_back(xml_node(n), alloc); - return true; - } - break; - - case nodetest_type_text: - if (type == node_pcdata || type == node_cdata) - { - ns.push_back(xml_node(n), alloc); - return true; - } - break; - - case nodetest_type_pi: - if (type == node_pi) - { - ns.push_back(xml_node(n), alloc); - return true; - } - break; - - case nodetest_pi: - if (type == node_pi && n->name && strequal(n->name, _data.nodetest)) - { - ns.push_back(xml_node(n), alloc); - return true; - } - break; - - case nodetest_all: - if (type == node_element) - { - ns.push_back(xml_node(n), alloc); - return true; - } - break; - - case nodetest_all_in_namespace: - if (type == node_element && n->name && starts_with(n->name, _data.nodetest)) - { - ns.push_back(xml_node(n), alloc); - return true; - } - break; - - default: - assert(!"Unknown axis"); - } - - return false; - } - - template void step_fill(xpath_node_set_raw& ns, xml_node_struct* n, xpath_allocator* alloc, bool once, T) - { - const axis_t axis = T::axis; - - switch (axis) - { - case axis_attribute: - { - for (xml_attribute_struct* a = n->first_attribute; a; a = a->next_attribute) - if (step_push(ns, a, n, alloc) & once) - return; - - break; - } - - case axis_child: - { - for (xml_node_struct* c = n->first_child; c; c = c->next_sibling) - if (step_push(ns, c, alloc) & once) - return; - - break; - } - - case axis_descendant: - case axis_descendant_or_self: - { - if (axis == axis_descendant_or_self) - if (step_push(ns, n, alloc) & once) - return; - - xml_node_struct* cur = n->first_child; - - while (cur) - { - if (step_push(ns, cur, alloc) & once) - return; - - if (cur->first_child) - cur = cur->first_child; - else - { - while (!cur->next_sibling) - { - cur = cur->parent; - - if (cur == n) return; - } - - cur = cur->next_sibling; - } - } - - break; - } - - case axis_following_sibling: - { - for (xml_node_struct* c = n->next_sibling; c; c = c->next_sibling) - if (step_push(ns, c, alloc) & once) - return; - - break; - } - - case axis_preceding_sibling: - { - for (xml_node_struct* c = n->prev_sibling_c; c->next_sibling; c = c->prev_sibling_c) - if (step_push(ns, c, alloc) & once) - return; - - break; - } - - case axis_following: - { - xml_node_struct* cur = n; - - // exit from this node so that we don't include descendants - while (!cur->next_sibling) - { - cur = cur->parent; - - if (!cur) return; - } - - cur = cur->next_sibling; - - while (cur) - { - if (step_push(ns, cur, alloc) & once) - return; - - if (cur->first_child) - cur = cur->first_child; - else - { - while (!cur->next_sibling) - { - cur = cur->parent; - - if (!cur) return; - } - - cur = cur->next_sibling; - } - } - - break; - } - - case axis_preceding: - { - xml_node_struct* cur = n; - - // exit from this node so that we don't include descendants - while (!cur->prev_sibling_c->next_sibling) - { - cur = cur->parent; - - if (!cur) return; - } - - cur = cur->prev_sibling_c; - - while (cur) - { - if (cur->first_child) - cur = cur->first_child->prev_sibling_c; - else - { - // leaf node, can't be ancestor - if (step_push(ns, cur, alloc) & once) - return; - - while (!cur->prev_sibling_c->next_sibling) - { - cur = cur->parent; - - if (!cur) return; - - if (!node_is_ancestor(cur, n)) - if (step_push(ns, cur, alloc) & once) - return; - } - - cur = cur->prev_sibling_c; - } - } - - break; - } - - case axis_ancestor: - case axis_ancestor_or_self: - { - if (axis == axis_ancestor_or_self) - if (step_push(ns, n, alloc) & once) - return; - - xml_node_struct* cur = n->parent; - - while (cur) - { - if (step_push(ns, cur, alloc) & once) - return; - - cur = cur->parent; - } - - break; - } - - case axis_self: - { - step_push(ns, n, alloc); - - break; - } - - case axis_parent: - { - if (n->parent) - step_push(ns, n->parent, alloc); - - break; - } - - default: - assert(!"Unimplemented axis"); - } - } - - template void step_fill(xpath_node_set_raw& ns, xml_attribute_struct* a, xml_node_struct* p, xpath_allocator* alloc, bool once, T v) - { - const axis_t axis = T::axis; - - switch (axis) - { - case axis_ancestor: - case axis_ancestor_or_self: - { - if (axis == axis_ancestor_or_self && _test == nodetest_type_node) // reject attributes based on principal node type test - if (step_push(ns, a, p, alloc) & once) - return; - - xml_node_struct* cur = p; - - while (cur) - { - if (step_push(ns, cur, alloc) & once) - return; - - cur = cur->parent; - } - - break; - } - - case axis_descendant_or_self: - case axis_self: - { - if (_test == nodetest_type_node) // reject attributes based on principal node type test - step_push(ns, a, p, alloc); - - break; - } - - case axis_following: - { - xml_node_struct* cur = p; - - while (cur) - { - if (cur->first_child) - cur = cur->first_child; - else - { - while (!cur->next_sibling) - { - cur = cur->parent; - - if (!cur) return; - } - - cur = cur->next_sibling; - } - - if (step_push(ns, cur, alloc) & once) - return; - } - - break; - } - - case axis_parent: - { - step_push(ns, p, alloc); - - break; - } - - case axis_preceding: - { - // preceding:: axis does not include attribute nodes and attribute ancestors (they are the same as parent's ancestors), so we can reuse node preceding - step_fill(ns, p, alloc, once, v); - break; - } - - default: - assert(!"Unimplemented axis"); - } - } - - template void step_fill(xpath_node_set_raw& ns, const xpath_node& xn, xpath_allocator* alloc, bool once, T v) - { - const axis_t axis = T::axis; - const bool axis_has_attributes = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_descendant_or_self || axis == axis_following || axis == axis_parent || axis == axis_preceding || axis == axis_self); - - if (xn.node()) - step_fill(ns, xn.node().internal_object(), alloc, once, v); - else if (axis_has_attributes && xn.attribute() && xn.parent()) - step_fill(ns, xn.attribute().internal_object(), xn.parent().internal_object(), alloc, once, v); - } - - template xpath_node_set_raw step_do(const xpath_context& c, const xpath_stack& stack, nodeset_eval_t eval, T v) - { - const axis_t axis = T::axis; - const bool axis_reverse = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_preceding || axis == axis_preceding_sibling); - const xpath_node_set::type_t axis_type = axis_reverse ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted; - - bool once = - (axis == axis_attribute && _test == nodetest_name) || - (!_right && eval_once(axis_type, eval)) || - (_right && !_right->_next && _right->_test == predicate_constant_one); - - xpath_node_set_raw ns; - ns.set_type(axis_type); - - if (_left) - { - xpath_node_set_raw s = _left->eval_node_set(c, stack, nodeset_eval_all); - - // self axis preserves the original order - if (axis == axis_self) ns.set_type(s.type()); - - for (const xpath_node* it = s.begin(); it != s.end(); ++it) - { - size_t size = ns.size(); - - // in general, all axes generate elements in a particular order, but there is no order guarantee if axis is applied to two nodes - if (axis != axis_self && size != 0) ns.set_type(xpath_node_set::type_unsorted); - - step_fill(ns, *it, stack.result, once, v); - if (_right) apply_predicates(ns, size, stack, eval); - } - } - else - { - step_fill(ns, c.n, stack.result, once, v); - if (_right) apply_predicates(ns, 0, stack, eval); - } - - // child, attribute and self axes always generate unique set of nodes - // for other axis, if the set stayed sorted, it stayed unique because the traversal algorithms do not visit the same node twice - if (axis != axis_child && axis != axis_attribute && axis != axis_self && ns.type() == xpath_node_set::type_unsorted) - ns.remove_duplicates(); - - return ns; - } - - public: - xpath_ast_node(ast_type_t type, xpath_value_type rettype_, const char_t* value): - _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0) - { - assert(type == ast_string_constant); - _data.string = value; - } - - xpath_ast_node(ast_type_t type, xpath_value_type rettype_, double value): - _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0) - { - assert(type == ast_number_constant); - _data.number = value; - } - - xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_variable* value): - _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0) - { - assert(type == ast_variable); - _data.variable = value; - } - - xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_ast_node* left = 0, xpath_ast_node* right = 0): - _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(left), _right(right), _next(0) - { - } - - xpath_ast_node(ast_type_t type, xpath_ast_node* left, axis_t axis, nodetest_t test, const char_t* contents): - _type(static_cast(type)), _rettype(xpath_type_node_set), _axis(static_cast(axis)), _test(static_cast(test)), _left(left), _right(0), _next(0) - { - assert(type == ast_step); - _data.nodetest = contents; - } - - xpath_ast_node(ast_type_t type, xpath_ast_node* left, xpath_ast_node* right, predicate_t test): - _type(static_cast(type)), _rettype(xpath_type_node_set), _axis(0), _test(static_cast(test)), _left(left), _right(right), _next(0) - { - assert(type == ast_filter || type == ast_predicate); - } - - void set_next(xpath_ast_node* value) - { - _next = value; - } - - void set_right(xpath_ast_node* value) - { - _right = value; - } - - bool eval_boolean(const xpath_context& c, const xpath_stack& stack) - { - switch (_type) - { - case ast_op_or: - return _left->eval_boolean(c, stack) || _right->eval_boolean(c, stack); - - case ast_op_and: - return _left->eval_boolean(c, stack) && _right->eval_boolean(c, stack); - - case ast_op_equal: - return compare_eq(_left, _right, c, stack, equal_to()); - - case ast_op_not_equal: - return compare_eq(_left, _right, c, stack, not_equal_to()); - - case ast_op_less: - return compare_rel(_left, _right, c, stack, less()); - - case ast_op_greater: - return compare_rel(_right, _left, c, stack, less()); - - case ast_op_less_or_equal: - return compare_rel(_left, _right, c, stack, less_equal()); - - case ast_op_greater_or_equal: - return compare_rel(_right, _left, c, stack, less_equal()); - - case ast_func_starts_with: - { - xpath_allocator_capture cr(stack.result); - - xpath_string lr = _left->eval_string(c, stack); - xpath_string rr = _right->eval_string(c, stack); - - return starts_with(lr.c_str(), rr.c_str()); - } - - case ast_func_contains: - { - xpath_allocator_capture cr(stack.result); - - xpath_string lr = _left->eval_string(c, stack); - xpath_string rr = _right->eval_string(c, stack); - - return find_substring(lr.c_str(), rr.c_str()) != 0; - } - - case ast_func_boolean: - return _left->eval_boolean(c, stack); - - case ast_func_not: - return !_left->eval_boolean(c, stack); - - case ast_func_true: - return true; - - case ast_func_false: - return false; - - case ast_func_lang: - { - if (c.n.attribute()) return false; - - xpath_allocator_capture cr(stack.result); - - xpath_string lang = _left->eval_string(c, stack); - - for (xml_node n = c.n.node(); n; n = n.parent()) - { - xml_attribute a = n.attribute(PUGIXML_TEXT("xml:lang")); - - if (a) - { - const char_t* value = a.value(); - - // strnicmp / strncasecmp is not portable - for (const char_t* lit = lang.c_str(); *lit; ++lit) - { - if (tolower_ascii(*lit) != tolower_ascii(*value)) return false; - ++value; - } - - return *value == 0 || *value == '-'; - } - } - - return false; - } - - case ast_opt_compare_attribute: - { - const char_t* value = (_right->_type == ast_string_constant) ? _right->_data.string : _right->_data.variable->get_string(); - - xml_attribute attr = c.n.node().attribute(_left->_data.nodetest); - - return attr && strequal(attr.value(), value) && is_xpath_attribute(attr.name()); - } - - case ast_variable: - { - assert(_rettype == _data.variable->type()); - - if (_rettype == xpath_type_boolean) - return _data.variable->get_boolean(); - - // fallthrough to type conversion - } - - default: - { - switch (_rettype) - { - case xpath_type_number: - return convert_number_to_boolean(eval_number(c, stack)); - - case xpath_type_string: - { - xpath_allocator_capture cr(stack.result); - - return !eval_string(c, stack).empty(); - } - - case xpath_type_node_set: - { - xpath_allocator_capture cr(stack.result); - - return !eval_node_set(c, stack, nodeset_eval_any).empty(); - } - - default: - assert(!"Wrong expression for return type boolean"); - return false; - } - } - } - } - - double eval_number(const xpath_context& c, const xpath_stack& stack) - { - switch (_type) - { - case ast_op_add: - return _left->eval_number(c, stack) + _right->eval_number(c, stack); - - case ast_op_subtract: - return _left->eval_number(c, stack) - _right->eval_number(c, stack); - - case ast_op_multiply: - return _left->eval_number(c, stack) * _right->eval_number(c, stack); - - case ast_op_divide: - return _left->eval_number(c, stack) / _right->eval_number(c, stack); - - case ast_op_mod: - return fmod(_left->eval_number(c, stack), _right->eval_number(c, stack)); - - case ast_op_negate: - return -_left->eval_number(c, stack); - - case ast_number_constant: - return _data.number; - - case ast_func_last: - return static_cast(c.size); - - case ast_func_position: - return static_cast(c.position); - - case ast_func_count: - { - xpath_allocator_capture cr(stack.result); - - return static_cast(_left->eval_node_set(c, stack, nodeset_eval_all).size()); - } - - case ast_func_string_length_0: - { - xpath_allocator_capture cr(stack.result); - - return static_cast(string_value(c.n, stack.result).length()); - } - - case ast_func_string_length_1: - { - xpath_allocator_capture cr(stack.result); - - return static_cast(_left->eval_string(c, stack).length()); - } - - case ast_func_number_0: - { - xpath_allocator_capture cr(stack.result); - - return convert_string_to_number(string_value(c.n, stack.result).c_str()); - } - - case ast_func_number_1: - return _left->eval_number(c, stack); - - case ast_func_sum: - { - xpath_allocator_capture cr(stack.result); - - double r = 0; - - xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_all); - - for (const xpath_node* it = ns.begin(); it != ns.end(); ++it) - { - xpath_allocator_capture cri(stack.result); - - r += convert_string_to_number(string_value(*it, stack.result).c_str()); - } - - return r; - } - - case ast_func_floor: - { - double r = _left->eval_number(c, stack); - - return r == r ? floor(r) : r; - } - - case ast_func_ceiling: - { - double r = _left->eval_number(c, stack); - - return r == r ? ceil(r) : r; - } - - case ast_func_round: - return round_nearest_nzero(_left->eval_number(c, stack)); - - case ast_variable: - { - assert(_rettype == _data.variable->type()); - - if (_rettype == xpath_type_number) - return _data.variable->get_number(); - - // fallthrough to type conversion - } - - default: - { - switch (_rettype) - { - case xpath_type_boolean: - return eval_boolean(c, stack) ? 1 : 0; - - case xpath_type_string: - { - xpath_allocator_capture cr(stack.result); - - return convert_string_to_number(eval_string(c, stack).c_str()); - } - - case xpath_type_node_set: - { - xpath_allocator_capture cr(stack.result); - - return convert_string_to_number(eval_string(c, stack).c_str()); - } - - default: - assert(!"Wrong expression for return type number"); - return 0; - } - - } - } - } - - xpath_string eval_string_concat(const xpath_context& c, const xpath_stack& stack) - { - assert(_type == ast_func_concat); - - xpath_allocator_capture ct(stack.temp); - - // count the string number - size_t count = 1; - for (xpath_ast_node* nc = _right; nc; nc = nc->_next) count++; - - // gather all strings - xpath_string static_buffer[4]; - xpath_string* buffer = static_buffer; - - // allocate on-heap for large concats - if (count > sizeof(static_buffer) / sizeof(static_buffer[0])) - { - buffer = static_cast(stack.temp->allocate(count * sizeof(xpath_string))); - assert(buffer); - } - - // evaluate all strings to temporary stack - xpath_stack swapped_stack = {stack.temp, stack.result}; - - buffer[0] = _left->eval_string(c, swapped_stack); - - size_t pos = 1; - for (xpath_ast_node* n = _right; n; n = n->_next, ++pos) buffer[pos] = n->eval_string(c, swapped_stack); - assert(pos == count); - - // get total length - size_t length = 0; - for (size_t i = 0; i < count; ++i) length += buffer[i].length(); - - // create final string - char_t* result = static_cast(stack.result->allocate((length + 1) * sizeof(char_t))); - assert(result); - - char_t* ri = result; - - for (size_t j = 0; j < count; ++j) - for (const char_t* bi = buffer[j].c_str(); *bi; ++bi) - *ri++ = *bi; - - *ri = 0; - - return xpath_string::from_heap_preallocated(result, ri); - } - - xpath_string eval_string(const xpath_context& c, const xpath_stack& stack) - { - switch (_type) - { - case ast_string_constant: - return xpath_string::from_const(_data.string); - - case ast_func_local_name_0: - { - xpath_node na = c.n; - - return xpath_string::from_const(local_name(na)); - } - - case ast_func_local_name_1: - { - xpath_allocator_capture cr(stack.result); - - xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first); - xpath_node na = ns.first(); - - return xpath_string::from_const(local_name(na)); - } - - case ast_func_name_0: - { - xpath_node na = c.n; - - return xpath_string::from_const(qualified_name(na)); - } - - case ast_func_name_1: - { - xpath_allocator_capture cr(stack.result); - - xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first); - xpath_node na = ns.first(); - - return xpath_string::from_const(qualified_name(na)); - } - - case ast_func_namespace_uri_0: - { - xpath_node na = c.n; - - return xpath_string::from_const(namespace_uri(na)); - } - - case ast_func_namespace_uri_1: - { - xpath_allocator_capture cr(stack.result); - - xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first); - xpath_node na = ns.first(); - - return xpath_string::from_const(namespace_uri(na)); - } - - case ast_func_string_0: - return string_value(c.n, stack.result); - - case ast_func_string_1: - return _left->eval_string(c, stack); - - case ast_func_concat: - return eval_string_concat(c, stack); - - case ast_func_substring_before: - { - xpath_allocator_capture cr(stack.temp); - - xpath_stack swapped_stack = {stack.temp, stack.result}; - - xpath_string s = _left->eval_string(c, swapped_stack); - xpath_string p = _right->eval_string(c, swapped_stack); - - const char_t* pos = find_substring(s.c_str(), p.c_str()); - - return pos ? xpath_string::from_heap(s.c_str(), pos, stack.result) : xpath_string(); - } - - case ast_func_substring_after: - { - xpath_allocator_capture cr(stack.temp); - - xpath_stack swapped_stack = {stack.temp, stack.result}; - - xpath_string s = _left->eval_string(c, swapped_stack); - xpath_string p = _right->eval_string(c, swapped_stack); - - const char_t* pos = find_substring(s.c_str(), p.c_str()); - if (!pos) return xpath_string(); - - const char_t* rbegin = pos + p.length(); - const char_t* rend = s.c_str() + s.length(); - - return s.uses_heap() ? xpath_string::from_heap(rbegin, rend, stack.result) : xpath_string::from_const(rbegin); - } - - case ast_func_substring_2: - { - xpath_allocator_capture cr(stack.temp); - - xpath_stack swapped_stack = {stack.temp, stack.result}; - - xpath_string s = _left->eval_string(c, swapped_stack); - size_t s_length = s.length(); - - double first = round_nearest(_right->eval_number(c, stack)); - - if (is_nan(first)) return xpath_string(); // NaN - else if (first >= s_length + 1) return xpath_string(); - - size_t pos = first < 1 ? 1 : static_cast(first); - assert(1 <= pos && pos <= s_length + 1); - - const char_t* rbegin = s.c_str() + (pos - 1); - const char_t* rend = s.c_str() + s.length(); - - return s.uses_heap() ? xpath_string::from_heap(rbegin, rend, stack.result) : xpath_string::from_const(rbegin); - } - - case ast_func_substring_3: - { - xpath_allocator_capture cr(stack.temp); - - xpath_stack swapped_stack = {stack.temp, stack.result}; - - xpath_string s = _left->eval_string(c, swapped_stack); - size_t s_length = s.length(); - - double first = round_nearest(_right->eval_number(c, stack)); - double last = first + round_nearest(_right->_next->eval_number(c, stack)); - - if (is_nan(first) || is_nan(last)) return xpath_string(); - else if (first >= s_length + 1) return xpath_string(); - else if (first >= last) return xpath_string(); - else if (last < 1) return xpath_string(); - - size_t pos = first < 1 ? 1 : static_cast(first); - size_t end = last >= s_length + 1 ? s_length + 1 : static_cast(last); - - assert(1 <= pos && pos <= end && end <= s_length + 1); - const char_t* rbegin = s.c_str() + (pos - 1); - const char_t* rend = s.c_str() + (end - 1); - - return (end == s_length + 1 && !s.uses_heap()) ? xpath_string::from_const(rbegin) : xpath_string::from_heap(rbegin, rend, stack.result); - } - - case ast_func_normalize_space_0: - { - xpath_string s = string_value(c.n, stack.result); - - char_t* begin = s.data(stack.result); - char_t* end = normalize_space(begin); - - return xpath_string::from_heap_preallocated(begin, end); - } - - case ast_func_normalize_space_1: - { - xpath_string s = _left->eval_string(c, stack); - - char_t* begin = s.data(stack.result); - char_t* end = normalize_space(begin); - - return xpath_string::from_heap_preallocated(begin, end); - } - - case ast_func_translate: - { - xpath_allocator_capture cr(stack.temp); - - xpath_stack swapped_stack = {stack.temp, stack.result}; - - xpath_string s = _left->eval_string(c, stack); - xpath_string from = _right->eval_string(c, swapped_stack); - xpath_string to = _right->_next->eval_string(c, swapped_stack); - - char_t* begin = s.data(stack.result); - char_t* end = translate(begin, from.c_str(), to.c_str(), to.length()); - - return xpath_string::from_heap_preallocated(begin, end); - } - - case ast_opt_translate_table: - { - xpath_string s = _left->eval_string(c, stack); - - char_t* begin = s.data(stack.result); - char_t* end = translate_table(begin, _data.table); - - return xpath_string::from_heap_preallocated(begin, end); - } - - case ast_variable: - { - assert(_rettype == _data.variable->type()); - - if (_rettype == xpath_type_string) - return xpath_string::from_const(_data.variable->get_string()); - - // fallthrough to type conversion - } - - default: - { - switch (_rettype) - { - case xpath_type_boolean: - return xpath_string::from_const(eval_boolean(c, stack) ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false")); - - case xpath_type_number: - return convert_number_to_string(eval_number(c, stack), stack.result); - - case xpath_type_node_set: - { - xpath_allocator_capture cr(stack.temp); - - xpath_stack swapped_stack = {stack.temp, stack.result}; - - xpath_node_set_raw ns = eval_node_set(c, swapped_stack, nodeset_eval_first); - return ns.empty() ? xpath_string() : string_value(ns.first(), stack.result); - } - - default: - assert(!"Wrong expression for return type string"); - return xpath_string(); - } - } - } - } - - xpath_node_set_raw eval_node_set(const xpath_context& c, const xpath_stack& stack, nodeset_eval_t eval) - { - switch (_type) - { - case ast_op_union: - { - xpath_allocator_capture cr(stack.temp); - - xpath_stack swapped_stack = {stack.temp, stack.result}; - - xpath_node_set_raw ls = _left->eval_node_set(c, swapped_stack, eval); - xpath_node_set_raw rs = _right->eval_node_set(c, stack, eval); - - // we can optimize merging two sorted sets, but this is a very rare operation, so don't bother - rs.set_type(xpath_node_set::type_unsorted); - - rs.append(ls.begin(), ls.end(), stack.result); - rs.remove_duplicates(); - - return rs; - } - - case ast_filter: - { - xpath_node_set_raw set = _left->eval_node_set(c, stack, _test == predicate_constant_one ? nodeset_eval_first : nodeset_eval_all); - - // either expression is a number or it contains position() call; sort by document order - if (_test != predicate_posinv) set.sort_do(); - - bool once = eval_once(set.type(), eval); - - apply_predicate(set, 0, stack, once); - - return set; - } - - case ast_func_id: - return xpath_node_set_raw(); - - case ast_step: - { - switch (_axis) - { - case axis_ancestor: - return step_do(c, stack, eval, axis_to_type()); - - case axis_ancestor_or_self: - return step_do(c, stack, eval, axis_to_type()); - - case axis_attribute: - return step_do(c, stack, eval, axis_to_type()); - - case axis_child: - return step_do(c, stack, eval, axis_to_type()); - - case axis_descendant: - return step_do(c, stack, eval, axis_to_type()); - - case axis_descendant_or_self: - return step_do(c, stack, eval, axis_to_type()); - - case axis_following: - return step_do(c, stack, eval, axis_to_type()); - - case axis_following_sibling: - return step_do(c, stack, eval, axis_to_type()); - - case axis_namespace: - // namespaced axis is not supported - return xpath_node_set_raw(); - - case axis_parent: - return step_do(c, stack, eval, axis_to_type()); - - case axis_preceding: - return step_do(c, stack, eval, axis_to_type()); - - case axis_preceding_sibling: - return step_do(c, stack, eval, axis_to_type()); - - case axis_self: - return step_do(c, stack, eval, axis_to_type()); - - default: - assert(!"Unknown axis"); - return xpath_node_set_raw(); - } - } - - case ast_step_root: - { - assert(!_right); // root step can't have any predicates - - xpath_node_set_raw ns; - - ns.set_type(xpath_node_set::type_sorted); - - if (c.n.node()) ns.push_back(c.n.node().root(), stack.result); - else if (c.n.attribute()) ns.push_back(c.n.parent().root(), stack.result); - - return ns; - } - - case ast_variable: - { - assert(_rettype == _data.variable->type()); - - if (_rettype == xpath_type_node_set) - { - const xpath_node_set& s = _data.variable->get_node_set(); - - xpath_node_set_raw ns; - - ns.set_type(s.type()); - ns.append(s.begin(), s.end(), stack.result); - - return ns; - } - - // fallthrough to type conversion - } - - default: - assert(!"Wrong expression for return type node set"); - return xpath_node_set_raw(); - } - } - - void optimize(xpath_allocator* alloc) - { - if (_left) _left->optimize(alloc); - if (_right) _right->optimize(alloc); - if (_next) _next->optimize(alloc); - - // Rewrite [position()=expr] with [expr] - // Note that this step has to go before classification to recognize [position()=1] - if ((_type == ast_filter || _type == ast_predicate) && - _right->_type == ast_op_equal && _right->_left->_type == ast_func_position && _right->_right->_rettype == xpath_type_number) - { - _right = _right->_right; - } - - // Classify filter/predicate ops to perform various optimizations during evaluation - if (_type == ast_filter || _type == ast_predicate) - { - assert(_test == predicate_default); - - if (_right->_type == ast_number_constant && _right->_data.number == 1.0) - _test = predicate_constant_one; - else if (_right->_rettype == xpath_type_number && (_right->_type == ast_number_constant || _right->_type == ast_variable || _right->_type == ast_func_last)) - _test = predicate_constant; - else if (_right->_rettype != xpath_type_number && _right->is_posinv_expr()) - _test = predicate_posinv; - } - - // Rewrite descendant-or-self::node()/child::foo with descendant::foo - // The former is a full form of //foo, the latter is much faster since it executes the node test immediately - // Do a similar kind of rewrite for self/descendant/descendant-or-self axes - // Note that we only rewrite positionally invariant steps (//foo[1] != /descendant::foo[1]) - if (_type == ast_step && (_axis == axis_child || _axis == axis_self || _axis == axis_descendant || _axis == axis_descendant_or_self) && _left && - _left->_type == ast_step && _left->_axis == axis_descendant_or_self && _left->_test == nodetest_type_node && !_left->_right && - is_posinv_step()) - { - if (_axis == axis_child || _axis == axis_descendant) - _axis = axis_descendant; - else - _axis = axis_descendant_or_self; - - _left = _left->_left; - } - - // Use optimized lookup table implementation for translate() with constant arguments - if (_type == ast_func_translate && _right->_type == ast_string_constant && _right->_next->_type == ast_string_constant) - { - unsigned char* table = translate_table_generate(alloc, _right->_data.string, _right->_next->_data.string); - - if (table) - { - _type = ast_opt_translate_table; - _data.table = table; - } - } - - // Use optimized path for @attr = 'value' or @attr = $value - if (_type == ast_op_equal && - _left->_type == ast_step && _left->_axis == axis_attribute && _left->_test == nodetest_name && !_left->_left && !_left->_right && - (_right->_type == ast_string_constant || (_right->_type == ast_variable && _right->_rettype == xpath_type_string))) - { - _type = ast_opt_compare_attribute; - } - } - - bool is_posinv_expr() const - { - switch (_type) - { - case ast_func_position: - case ast_func_last: - return false; - - case ast_string_constant: - case ast_number_constant: - case ast_variable: - return true; - - case ast_step: - case ast_step_root: - return true; - - case ast_predicate: - case ast_filter: - return true; - - default: - if (_left && !_left->is_posinv_expr()) return false; - - for (xpath_ast_node* n = _right; n; n = n->_next) - if (!n->is_posinv_expr()) return false; - - return true; - } - } - - bool is_posinv_step() const - { - assert(_type == ast_step); - - for (xpath_ast_node* n = _right; n; n = n->_next) - { - assert(n->_type == ast_predicate); - - if (n->_test != predicate_posinv) - return false; - } - - return true; - } - - xpath_value_type rettype() const - { - return static_cast(_rettype); - } - }; - - struct xpath_parser - { - xpath_allocator* _alloc; - xpath_lexer _lexer; - - const char_t* _query; - xpath_variable_set* _variables; - - xpath_parse_result* _result; - - char_t _scratch[32]; - - #ifdef PUGIXML_NO_EXCEPTIONS - jmp_buf _error_handler; - #endif - - void throw_error(const char* message) - { - _result->error = message; - _result->offset = _lexer.current_pos() - _query; - - #ifdef PUGIXML_NO_EXCEPTIONS - longjmp(_error_handler, 1); - #else - throw xpath_exception(*_result); - #endif - } - - void throw_error_oom() - { - #ifdef PUGIXML_NO_EXCEPTIONS - throw_error("Out of memory"); - #else - throw std::bad_alloc(); - #endif - } - - void* alloc_node() - { - void* result = _alloc->allocate_nothrow(sizeof(xpath_ast_node)); - - if (!result) throw_error_oom(); - - return result; - } - - const char_t* alloc_string(const xpath_lexer_string& value) - { - if (value.begin) - { - size_t length = static_cast(value.end - value.begin); - - char_t* c = static_cast(_alloc->allocate_nothrow((length + 1) * sizeof(char_t))); - if (!c) throw_error_oom(); - assert(c); // workaround for clang static analysis - - memcpy(c, value.begin, length * sizeof(char_t)); - c[length] = 0; - - return c; - } - else return 0; - } - - xpath_ast_node* parse_function_helper(ast_type_t type0, ast_type_t type1, size_t argc, xpath_ast_node* args[2]) - { - assert(argc <= 1); - - if (argc == 1 && args[0]->rettype() != xpath_type_node_set) throw_error("Function has to be applied to node set"); - - return new (alloc_node()) xpath_ast_node(argc == 0 ? type0 : type1, xpath_type_string, args[0]); - } - - xpath_ast_node* parse_function(const xpath_lexer_string& name, size_t argc, xpath_ast_node* args[2]) - { - switch (name.begin[0]) - { - case 'b': - if (name == PUGIXML_TEXT("boolean") && argc == 1) - return new (alloc_node()) xpath_ast_node(ast_func_boolean, xpath_type_boolean, args[0]); - - break; - - case 'c': - if (name == PUGIXML_TEXT("count") && argc == 1) - { - if (args[0]->rettype() != xpath_type_node_set) throw_error("Function has to be applied to node set"); - return new (alloc_node()) xpath_ast_node(ast_func_count, xpath_type_number, args[0]); - } - else if (name == PUGIXML_TEXT("contains") && argc == 2) - return new (alloc_node()) xpath_ast_node(ast_func_contains, xpath_type_boolean, args[0], args[1]); - else if (name == PUGIXML_TEXT("concat") && argc >= 2) - return new (alloc_node()) xpath_ast_node(ast_func_concat, xpath_type_string, args[0], args[1]); - else if (name == PUGIXML_TEXT("ceiling") && argc == 1) - return new (alloc_node()) xpath_ast_node(ast_func_ceiling, xpath_type_number, args[0]); - - break; - - case 'f': - if (name == PUGIXML_TEXT("false") && argc == 0) - return new (alloc_node()) xpath_ast_node(ast_func_false, xpath_type_boolean); - else if (name == PUGIXML_TEXT("floor") && argc == 1) - return new (alloc_node()) xpath_ast_node(ast_func_floor, xpath_type_number, args[0]); - - break; - - case 'i': - if (name == PUGIXML_TEXT("id") && argc == 1) - return new (alloc_node()) xpath_ast_node(ast_func_id, xpath_type_node_set, args[0]); - - break; - - case 'l': - if (name == PUGIXML_TEXT("last") && argc == 0) - return new (alloc_node()) xpath_ast_node(ast_func_last, xpath_type_number); - else if (name == PUGIXML_TEXT("lang") && argc == 1) - return new (alloc_node()) xpath_ast_node(ast_func_lang, xpath_type_boolean, args[0]); - else if (name == PUGIXML_TEXT("local-name") && argc <= 1) - return parse_function_helper(ast_func_local_name_0, ast_func_local_name_1, argc, args); - - break; - - case 'n': - if (name == PUGIXML_TEXT("name") && argc <= 1) - return parse_function_helper(ast_func_name_0, ast_func_name_1, argc, args); - else if (name == PUGIXML_TEXT("namespace-uri") && argc <= 1) - return parse_function_helper(ast_func_namespace_uri_0, ast_func_namespace_uri_1, argc, args); - else if (name == PUGIXML_TEXT("normalize-space") && argc <= 1) - return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_normalize_space_0 : ast_func_normalize_space_1, xpath_type_string, args[0], args[1]); - else if (name == PUGIXML_TEXT("not") && argc == 1) - return new (alloc_node()) xpath_ast_node(ast_func_not, xpath_type_boolean, args[0]); - else if (name == PUGIXML_TEXT("number") && argc <= 1) - return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_number_0 : ast_func_number_1, xpath_type_number, args[0]); - - break; - - case 'p': - if (name == PUGIXML_TEXT("position") && argc == 0) - return new (alloc_node()) xpath_ast_node(ast_func_position, xpath_type_number); - - break; - - case 'r': - if (name == PUGIXML_TEXT("round") && argc == 1) - return new (alloc_node()) xpath_ast_node(ast_func_round, xpath_type_number, args[0]); - - break; - - case 's': - if (name == PUGIXML_TEXT("string") && argc <= 1) - return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_string_0 : ast_func_string_1, xpath_type_string, args[0]); - else if (name == PUGIXML_TEXT("string-length") && argc <= 1) - return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_string_length_0 : ast_func_string_length_1, xpath_type_number, args[0]); - else if (name == PUGIXML_TEXT("starts-with") && argc == 2) - return new (alloc_node()) xpath_ast_node(ast_func_starts_with, xpath_type_boolean, args[0], args[1]); - else if (name == PUGIXML_TEXT("substring-before") && argc == 2) - return new (alloc_node()) xpath_ast_node(ast_func_substring_before, xpath_type_string, args[0], args[1]); - else if (name == PUGIXML_TEXT("substring-after") && argc == 2) - return new (alloc_node()) xpath_ast_node(ast_func_substring_after, xpath_type_string, args[0], args[1]); - else if (name == PUGIXML_TEXT("substring") && (argc == 2 || argc == 3)) - return new (alloc_node()) xpath_ast_node(argc == 2 ? ast_func_substring_2 : ast_func_substring_3, xpath_type_string, args[0], args[1]); - else if (name == PUGIXML_TEXT("sum") && argc == 1) - { - if (args[0]->rettype() != xpath_type_node_set) throw_error("Function has to be applied to node set"); - return new (alloc_node()) xpath_ast_node(ast_func_sum, xpath_type_number, args[0]); - } - - break; - - case 't': - if (name == PUGIXML_TEXT("translate") && argc == 3) - return new (alloc_node()) xpath_ast_node(ast_func_translate, xpath_type_string, args[0], args[1]); - else if (name == PUGIXML_TEXT("true") && argc == 0) - return new (alloc_node()) xpath_ast_node(ast_func_true, xpath_type_boolean); - - break; - - default: - break; - } - - throw_error("Unrecognized function or wrong parameter count"); - - return 0; - } - - axis_t parse_axis_name(const xpath_lexer_string& name, bool& specified) - { - specified = true; - - switch (name.begin[0]) - { - case 'a': - if (name == PUGIXML_TEXT("ancestor")) - return axis_ancestor; - else if (name == PUGIXML_TEXT("ancestor-or-self")) - return axis_ancestor_or_self; - else if (name == PUGIXML_TEXT("attribute")) - return axis_attribute; - - break; - - case 'c': - if (name == PUGIXML_TEXT("child")) - return axis_child; - - break; - - case 'd': - if (name == PUGIXML_TEXT("descendant")) - return axis_descendant; - else if (name == PUGIXML_TEXT("descendant-or-self")) - return axis_descendant_or_self; - - break; - - case 'f': - if (name == PUGIXML_TEXT("following")) - return axis_following; - else if (name == PUGIXML_TEXT("following-sibling")) - return axis_following_sibling; - - break; - - case 'n': - if (name == PUGIXML_TEXT("namespace")) - return axis_namespace; - - break; - - case 'p': - if (name == PUGIXML_TEXT("parent")) - return axis_parent; - else if (name == PUGIXML_TEXT("preceding")) - return axis_preceding; - else if (name == PUGIXML_TEXT("preceding-sibling")) - return axis_preceding_sibling; - - break; - - case 's': - if (name == PUGIXML_TEXT("self")) - return axis_self; - - break; - - default: - break; - } - - specified = false; - return axis_child; - } - - nodetest_t parse_node_test_type(const xpath_lexer_string& name) - { - switch (name.begin[0]) - { - case 'c': - if (name == PUGIXML_TEXT("comment")) - return nodetest_type_comment; - - break; - - case 'n': - if (name == PUGIXML_TEXT("node")) - return nodetest_type_node; - - break; - - case 'p': - if (name == PUGIXML_TEXT("processing-instruction")) - return nodetest_type_pi; - - break; - - case 't': - if (name == PUGIXML_TEXT("text")) - return nodetest_type_text; - - break; - - default: - break; - } - - return nodetest_none; - } - - // PrimaryExpr ::= VariableReference | '(' Expr ')' | Literal | Number | FunctionCall - xpath_ast_node* parse_primary_expression() - { - switch (_lexer.current()) - { - case lex_var_ref: - { - xpath_lexer_string name = _lexer.contents(); - - if (!_variables) - throw_error("Unknown variable: variable set is not provided"); - - xpath_variable* var = get_variable_scratch(_scratch, _variables, name.begin, name.end); - - if (!var) - throw_error("Unknown variable: variable set does not contain the given name"); - - _lexer.next(); - - return new (alloc_node()) xpath_ast_node(ast_variable, var->type(), var); - } - - case lex_open_brace: - { - _lexer.next(); - - xpath_ast_node* n = parse_expression(); - - if (_lexer.current() != lex_close_brace) - throw_error("Unmatched braces"); - - _lexer.next(); - - return n; - } - - case lex_quoted_string: - { - const char_t* value = alloc_string(_lexer.contents()); - - xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_string_constant, xpath_type_string, value); - _lexer.next(); - - return n; - } - - case lex_number: - { - double value = 0; - - if (!convert_string_to_number_scratch(_scratch, _lexer.contents().begin, _lexer.contents().end, &value)) - throw_error_oom(); - - xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_number_constant, xpath_type_number, value); - _lexer.next(); - - return n; - } - - case lex_string: - { - xpath_ast_node* args[2] = {0}; - size_t argc = 0; - - xpath_lexer_string function = _lexer.contents(); - _lexer.next(); - - xpath_ast_node* last_arg = 0; - - if (_lexer.current() != lex_open_brace) - throw_error("Unrecognized function call"); - _lexer.next(); - - if (_lexer.current() != lex_close_brace) - args[argc++] = parse_expression(); - - while (_lexer.current() != lex_close_brace) - { - if (_lexer.current() != lex_comma) - throw_error("No comma between function arguments"); - _lexer.next(); - - xpath_ast_node* n = parse_expression(); - - if (argc < 2) args[argc] = n; - else last_arg->set_next(n); - - argc++; - last_arg = n; - } - - _lexer.next(); - - return parse_function(function, argc, args); - } - - default: - throw_error("Unrecognizable primary expression"); - - return 0; - } - } - - // FilterExpr ::= PrimaryExpr | FilterExpr Predicate - // Predicate ::= '[' PredicateExpr ']' - // PredicateExpr ::= Expr - xpath_ast_node* parse_filter_expression() - { - xpath_ast_node* n = parse_primary_expression(); - - while (_lexer.current() == lex_open_square_brace) - { - _lexer.next(); - - xpath_ast_node* expr = parse_expression(); - - if (n->rettype() != xpath_type_node_set) throw_error("Predicate has to be applied to node set"); - - n = new (alloc_node()) xpath_ast_node(ast_filter, n, expr, predicate_default); - - if (_lexer.current() != lex_close_square_brace) - throw_error("Unmatched square brace"); - - _lexer.next(); - } - - return n; - } - - // Step ::= AxisSpecifier NodeTest Predicate* | AbbreviatedStep - // AxisSpecifier ::= AxisName '::' | '@'? - // NodeTest ::= NameTest | NodeType '(' ')' | 'processing-instruction' '(' Literal ')' - // NameTest ::= '*' | NCName ':' '*' | QName - // AbbreviatedStep ::= '.' | '..' - xpath_ast_node* parse_step(xpath_ast_node* set) - { - if (set && set->rettype() != xpath_type_node_set) - throw_error("Step has to be applied to node set"); - - bool axis_specified = false; - axis_t axis = axis_child; // implied child axis - - if (_lexer.current() == lex_axis_attribute) - { - axis = axis_attribute; - axis_specified = true; - - _lexer.next(); - } - else if (_lexer.current() == lex_dot) - { - _lexer.next(); - - return new (alloc_node()) xpath_ast_node(ast_step, set, axis_self, nodetest_type_node, 0); - } - else if (_lexer.current() == lex_double_dot) - { - _lexer.next(); - - return new (alloc_node()) xpath_ast_node(ast_step, set, axis_parent, nodetest_type_node, 0); - } - - nodetest_t nt_type = nodetest_none; - xpath_lexer_string nt_name; - - if (_lexer.current() == lex_string) - { - // node name test - nt_name = _lexer.contents(); - _lexer.next(); - - // was it an axis name? - if (_lexer.current() == lex_double_colon) - { - // parse axis name - if (axis_specified) throw_error("Two axis specifiers in one step"); - - axis = parse_axis_name(nt_name, axis_specified); - - if (!axis_specified) throw_error("Unknown axis"); - - // read actual node test - _lexer.next(); - - if (_lexer.current() == lex_multiply) - { - nt_type = nodetest_all; - nt_name = xpath_lexer_string(); - _lexer.next(); - } - else if (_lexer.current() == lex_string) - { - nt_name = _lexer.contents(); - _lexer.next(); - } - else throw_error("Unrecognized node test"); - } - - if (nt_type == nodetest_none) - { - // node type test or processing-instruction - if (_lexer.current() == lex_open_brace) - { - _lexer.next(); - - if (_lexer.current() == lex_close_brace) - { - _lexer.next(); - - nt_type = parse_node_test_type(nt_name); - - if (nt_type == nodetest_none) throw_error("Unrecognized node type"); - - nt_name = xpath_lexer_string(); - } - else if (nt_name == PUGIXML_TEXT("processing-instruction")) - { - if (_lexer.current() != lex_quoted_string) - throw_error("Only literals are allowed as arguments to processing-instruction()"); - - nt_type = nodetest_pi; - nt_name = _lexer.contents(); - _lexer.next(); - - if (_lexer.current() != lex_close_brace) - throw_error("Unmatched brace near processing-instruction()"); - _lexer.next(); - } - else - throw_error("Unmatched brace near node type test"); - - } - // QName or NCName:* - else - { - if (nt_name.end - nt_name.begin > 2 && nt_name.end[-2] == ':' && nt_name.end[-1] == '*') // NCName:* - { - nt_name.end--; // erase * - - nt_type = nodetest_all_in_namespace; - } - else nt_type = nodetest_name; - } - } - } - else if (_lexer.current() == lex_multiply) - { - nt_type = nodetest_all; - _lexer.next(); - } - else throw_error("Unrecognized node test"); - - xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step, set, axis, nt_type, alloc_string(nt_name)); - - xpath_ast_node* last = 0; - - while (_lexer.current() == lex_open_square_brace) - { - _lexer.next(); - - xpath_ast_node* expr = parse_expression(); - - xpath_ast_node* pred = new (alloc_node()) xpath_ast_node(ast_predicate, 0, expr, predicate_default); - - if (_lexer.current() != lex_close_square_brace) - throw_error("Unmatched square brace"); - _lexer.next(); - - if (last) last->set_next(pred); - else n->set_right(pred); - - last = pred; - } - - return n; - } - - // RelativeLocationPath ::= Step | RelativeLocationPath '/' Step | RelativeLocationPath '//' Step - xpath_ast_node* parse_relative_location_path(xpath_ast_node* set) - { - xpath_ast_node* n = parse_step(set); - - while (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash) - { - lexeme_t l = _lexer.current(); - _lexer.next(); - - if (l == lex_double_slash) - n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); - - n = parse_step(n); - } - - return n; - } - - // LocationPath ::= RelativeLocationPath | AbsoluteLocationPath - // AbsoluteLocationPath ::= '/' RelativeLocationPath? | '//' RelativeLocationPath - xpath_ast_node* parse_location_path() - { - if (_lexer.current() == lex_slash) - { - _lexer.next(); - - xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step_root, xpath_type_node_set); - - // relative location path can start from axis_attribute, dot, double_dot, multiply and string lexemes; any other lexeme means standalone root path - lexeme_t l = _lexer.current(); - - if (l == lex_string || l == lex_axis_attribute || l == lex_dot || l == lex_double_dot || l == lex_multiply) - return parse_relative_location_path(n); - else - return n; - } - else if (_lexer.current() == lex_double_slash) - { - _lexer.next(); - - xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step_root, xpath_type_node_set); - n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); - - return parse_relative_location_path(n); - } - - // else clause moved outside of if because of bogus warning 'control may reach end of non-void function being inlined' in gcc 4.0.1 - return parse_relative_location_path(0); - } - - // PathExpr ::= LocationPath - // | FilterExpr - // | FilterExpr '/' RelativeLocationPath - // | FilterExpr '//' RelativeLocationPath - // UnionExpr ::= PathExpr | UnionExpr '|' PathExpr - // UnaryExpr ::= UnionExpr | '-' UnaryExpr - xpath_ast_node* parse_path_or_unary_expression() - { - // Clarification. - // PathExpr begins with either LocationPath or FilterExpr. - // FilterExpr begins with PrimaryExpr - // PrimaryExpr begins with '$' in case of it being a variable reference, - // '(' in case of it being an expression, string literal, number constant or - // function call. - - if (_lexer.current() == lex_var_ref || _lexer.current() == lex_open_brace || - _lexer.current() == lex_quoted_string || _lexer.current() == lex_number || - _lexer.current() == lex_string) - { - if (_lexer.current() == lex_string) - { - // This is either a function call, or not - if not, we shall proceed with location path - const char_t* state = _lexer.state(); - - while (PUGI__IS_CHARTYPE(*state, ct_space)) ++state; - - if (*state != '(') return parse_location_path(); - - // This looks like a function call; however this still can be a node-test. Check it. - if (parse_node_test_type(_lexer.contents()) != nodetest_none) return parse_location_path(); - } - - xpath_ast_node* n = parse_filter_expression(); - - if (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash) - { - lexeme_t l = _lexer.current(); - _lexer.next(); - - if (l == lex_double_slash) - { - if (n->rettype() != xpath_type_node_set) throw_error("Step has to be applied to node set"); - - n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); - } - - // select from location path - return parse_relative_location_path(n); - } - - return n; - } - else if (_lexer.current() == lex_minus) - { - _lexer.next(); - - // precedence 7+ - only parses union expressions - xpath_ast_node* expr = parse_expression_rec(parse_path_or_unary_expression(), 7); - - return new (alloc_node()) xpath_ast_node(ast_op_negate, xpath_type_number, expr); - } - else - return parse_location_path(); - } - - struct binary_op_t - { - ast_type_t asttype; - xpath_value_type rettype; - int precedence; - - binary_op_t(): asttype(ast_unknown), rettype(xpath_type_none), precedence(0) - { - } - - binary_op_t(ast_type_t asttype_, xpath_value_type rettype_, int precedence_): asttype(asttype_), rettype(rettype_), precedence(precedence_) - { - } - - static binary_op_t parse(xpath_lexer& lexer) - { - switch (lexer.current()) - { - case lex_string: - if (lexer.contents() == PUGIXML_TEXT("or")) - return binary_op_t(ast_op_or, xpath_type_boolean, 1); - else if (lexer.contents() == PUGIXML_TEXT("and")) - return binary_op_t(ast_op_and, xpath_type_boolean, 2); - else if (lexer.contents() == PUGIXML_TEXT("div")) - return binary_op_t(ast_op_divide, xpath_type_number, 6); - else if (lexer.contents() == PUGIXML_TEXT("mod")) - return binary_op_t(ast_op_mod, xpath_type_number, 6); - else - return binary_op_t(); - - case lex_equal: - return binary_op_t(ast_op_equal, xpath_type_boolean, 3); - - case lex_not_equal: - return binary_op_t(ast_op_not_equal, xpath_type_boolean, 3); - - case lex_less: - return binary_op_t(ast_op_less, xpath_type_boolean, 4); - - case lex_greater: - return binary_op_t(ast_op_greater, xpath_type_boolean, 4); - - case lex_less_or_equal: - return binary_op_t(ast_op_less_or_equal, xpath_type_boolean, 4); - - case lex_greater_or_equal: - return binary_op_t(ast_op_greater_or_equal, xpath_type_boolean, 4); - - case lex_plus: - return binary_op_t(ast_op_add, xpath_type_number, 5); - - case lex_minus: - return binary_op_t(ast_op_subtract, xpath_type_number, 5); - - case lex_multiply: - return binary_op_t(ast_op_multiply, xpath_type_number, 6); - - case lex_union: - return binary_op_t(ast_op_union, xpath_type_node_set, 7); - - default: - return binary_op_t(); - } - } - }; - - xpath_ast_node* parse_expression_rec(xpath_ast_node* lhs, int limit) - { - binary_op_t op = binary_op_t::parse(_lexer); - - while (op.asttype != ast_unknown && op.precedence >= limit) - { - _lexer.next(); - - xpath_ast_node* rhs = parse_path_or_unary_expression(); - - binary_op_t nextop = binary_op_t::parse(_lexer); - - while (nextop.asttype != ast_unknown && nextop.precedence > op.precedence) - { - rhs = parse_expression_rec(rhs, nextop.precedence); - - nextop = binary_op_t::parse(_lexer); - } - - if (op.asttype == ast_op_union && (lhs->rettype() != xpath_type_node_set || rhs->rettype() != xpath_type_node_set)) - throw_error("Union operator has to be applied to node sets"); - - lhs = new (alloc_node()) xpath_ast_node(op.asttype, op.rettype, lhs, rhs); - - op = binary_op_t::parse(_lexer); - } - - return lhs; - } - - // Expr ::= OrExpr - // OrExpr ::= AndExpr | OrExpr 'or' AndExpr - // AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr - // EqualityExpr ::= RelationalExpr - // | EqualityExpr '=' RelationalExpr - // | EqualityExpr '!=' RelationalExpr - // RelationalExpr ::= AdditiveExpr - // | RelationalExpr '<' AdditiveExpr - // | RelationalExpr '>' AdditiveExpr - // | RelationalExpr '<=' AdditiveExpr - // | RelationalExpr '>=' AdditiveExpr - // AdditiveExpr ::= MultiplicativeExpr - // | AdditiveExpr '+' MultiplicativeExpr - // | AdditiveExpr '-' MultiplicativeExpr - // MultiplicativeExpr ::= UnaryExpr - // | MultiplicativeExpr '*' UnaryExpr - // | MultiplicativeExpr 'div' UnaryExpr - // | MultiplicativeExpr 'mod' UnaryExpr - xpath_ast_node* parse_expression() - { - return parse_expression_rec(parse_path_or_unary_expression(), 0); - } - - xpath_parser(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result): _alloc(alloc), _lexer(query), _query(query), _variables(variables), _result(result) - { - } - - xpath_ast_node* parse() - { - xpath_ast_node* result = parse_expression(); - - if (_lexer.current() != lex_eof) - { - // there are still unparsed tokens left, error - throw_error("Incorrect query"); - } - - return result; - } - - static xpath_ast_node* parse(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result) - { - xpath_parser parser(query, variables, alloc, result); - - #ifdef PUGIXML_NO_EXCEPTIONS - int error = setjmp(parser._error_handler); - - return (error == 0) ? parser.parse() : 0; - #else - return parser.parse(); - #endif - } - }; - - struct xpath_query_impl - { - static xpath_query_impl* create() - { - void* memory = xml_memory::allocate(sizeof(xpath_query_impl)); - - return new (memory) xpath_query_impl(); - } - - static void destroy(void* ptr) - { - if (!ptr) return; - - // free all allocated pages - static_cast(ptr)->alloc.release(); - - // free allocator memory (with the first page) - xml_memory::deallocate(ptr); - } - - xpath_query_impl(): root(0), alloc(&block) - { - block.next = 0; - block.capacity = sizeof(block.data); - } - - xpath_ast_node* root; - xpath_allocator alloc; - xpath_memory_block block; - }; - - PUGI__FN xpath_string evaluate_string_impl(xpath_query_impl* impl, const xpath_node& n, xpath_stack_data& sd) - { - if (!impl) return xpath_string(); - - #ifdef PUGIXML_NO_EXCEPTIONS - if (setjmp(sd.error_handler)) return xpath_string(); - #endif - - xpath_context c(n, 1, 1); - - return impl->root->eval_string(c, sd.stack); - } - - PUGI__FN impl::xpath_ast_node* evaluate_node_set_prepare(xpath_query_impl* impl) - { - if (!impl) return 0; - - if (impl->root->rettype() != xpath_type_node_set) - { - #ifdef PUGIXML_NO_EXCEPTIONS - return 0; - #else - xpath_parse_result res; - res.error = "Expression does not evaluate to node set"; - - throw xpath_exception(res); - #endif - } - - return impl->root; - } -PUGI__NS_END - -namespace pugi -{ -#ifndef PUGIXML_NO_EXCEPTIONS - PUGI__FN xpath_exception::xpath_exception(const xpath_parse_result& result_): _result(result_) - { - assert(_result.error); - } - - PUGI__FN const char* xpath_exception::what() const throw() - { - return _result.error; - } - - PUGI__FN const xpath_parse_result& xpath_exception::result() const - { - return _result; - } -#endif - - PUGI__FN xpath_node::xpath_node() - { - } - - PUGI__FN xpath_node::xpath_node(const xml_node& node_): _node(node_) - { - } - - PUGI__FN xpath_node::xpath_node(const xml_attribute& attribute_, const xml_node& parent_): _node(attribute_ ? parent_ : xml_node()), _attribute(attribute_) - { - } - - PUGI__FN xml_node xpath_node::node() const - { - return _attribute ? xml_node() : _node; - } - - PUGI__FN xml_attribute xpath_node::attribute() const - { - return _attribute; - } - - PUGI__FN xml_node xpath_node::parent() const - { - return _attribute ? _node : _node.parent(); - } - - PUGI__FN static void unspecified_bool_xpath_node(xpath_node***) - { - } - - PUGI__FN xpath_node::operator xpath_node::unspecified_bool_type() const - { - return (_node || _attribute) ? unspecified_bool_xpath_node : 0; - } - - PUGI__FN bool xpath_node::operator!() const - { - return !(_node || _attribute); - } - - PUGI__FN bool xpath_node::operator==(const xpath_node& n) const - { - return _node == n._node && _attribute == n._attribute; - } - - PUGI__FN bool xpath_node::operator!=(const xpath_node& n) const - { - return _node != n._node || _attribute != n._attribute; - } - -#ifdef __BORLANDC__ - PUGI__FN bool operator&&(const xpath_node& lhs, bool rhs) - { - return (bool)lhs && rhs; - } - - PUGI__FN bool operator||(const xpath_node& lhs, bool rhs) - { - return (bool)lhs || rhs; - } -#endif - - PUGI__FN void xpath_node_set::_assign(const_iterator begin_, const_iterator end_) - { - assert(begin_ <= end_); - - size_t size_ = static_cast(end_ - begin_); - - if (size_ <= 1) - { - // deallocate old buffer - if (_begin != &_storage) impl::xml_memory::deallocate(_begin); - - // use internal buffer - if (begin_ != end_) _storage = *begin_; - - _begin = &_storage; - _end = &_storage + size_; - } - else - { - // make heap copy - xpath_node* storage = static_cast(impl::xml_memory::allocate(size_ * sizeof(xpath_node))); - - if (!storage) - { - #ifdef PUGIXML_NO_EXCEPTIONS - return; - #else - throw std::bad_alloc(); - #endif - } - - memcpy(storage, begin_, size_ * sizeof(xpath_node)); - - // deallocate old buffer - if (_begin != &_storage) impl::xml_memory::deallocate(_begin); - - // finalize - _begin = storage; - _end = storage + size_; - } - } - - PUGI__FN xpath_node_set::xpath_node_set(): _type(type_unsorted), _begin(&_storage), _end(&_storage) - { - } - - PUGI__FN xpath_node_set::xpath_node_set(const_iterator begin_, const_iterator end_, type_t type_): _type(type_), _begin(&_storage), _end(&_storage) - { - _assign(begin_, end_); - } - - PUGI__FN xpath_node_set::~xpath_node_set() - { - if (_begin != &_storage) impl::xml_memory::deallocate(_begin); - } - - PUGI__FN xpath_node_set::xpath_node_set(const xpath_node_set& ns): _type(ns._type), _begin(&_storage), _end(&_storage) - { - _assign(ns._begin, ns._end); - } - - PUGI__FN xpath_node_set& xpath_node_set::operator=(const xpath_node_set& ns) - { - if (this == &ns) return *this; - - _type = ns._type; - _assign(ns._begin, ns._end); - - return *this; - } - - PUGI__FN xpath_node_set::type_t xpath_node_set::type() const - { - return _type; - } - - PUGI__FN size_t xpath_node_set::size() const - { - return _end - _begin; - } - - PUGI__FN bool xpath_node_set::empty() const - { - return _begin == _end; - } - - PUGI__FN const xpath_node& xpath_node_set::operator[](size_t index) const - { - assert(index < size()); - return _begin[index]; - } - - PUGI__FN xpath_node_set::const_iterator xpath_node_set::begin() const - { - return _begin; - } - - PUGI__FN xpath_node_set::const_iterator xpath_node_set::end() const - { - return _end; - } - - PUGI__FN void xpath_node_set::sort(bool reverse) - { - _type = impl::xpath_sort(_begin, _end, _type, reverse); - } - - PUGI__FN xpath_node xpath_node_set::first() const - { - return impl::xpath_first(_begin, _end, _type); - } - - PUGI__FN xpath_parse_result::xpath_parse_result(): error("Internal error"), offset(0) - { - } - - PUGI__FN xpath_parse_result::operator bool() const - { - return error == 0; - } - - PUGI__FN const char* xpath_parse_result::description() const - { - return error ? error : "No error"; - } - - PUGI__FN xpath_variable::xpath_variable(): _type(xpath_type_none), _next(0) - { - } - - PUGI__FN const char_t* xpath_variable::name() const - { - switch (_type) - { - case xpath_type_node_set: - return static_cast(this)->name; - - case xpath_type_number: - return static_cast(this)->name; - - case xpath_type_string: - return static_cast(this)->name; - - case xpath_type_boolean: - return static_cast(this)->name; - - default: - assert(!"Invalid variable type"); - return 0; - } - } - - PUGI__FN xpath_value_type xpath_variable::type() const - { - return _type; - } - - PUGI__FN bool xpath_variable::get_boolean() const - { - return (_type == xpath_type_boolean) ? static_cast(this)->value : false; - } - - PUGI__FN double xpath_variable::get_number() const - { - return (_type == xpath_type_number) ? static_cast(this)->value : impl::gen_nan(); - } - - PUGI__FN const char_t* xpath_variable::get_string() const - { - const char_t* value = (_type == xpath_type_string) ? static_cast(this)->value : 0; - return value ? value : PUGIXML_TEXT(""); - } - - PUGI__FN const xpath_node_set& xpath_variable::get_node_set() const - { - return (_type == xpath_type_node_set) ? static_cast(this)->value : impl::dummy_node_set; - } - - PUGI__FN bool xpath_variable::set(bool value) - { - if (_type != xpath_type_boolean) return false; - - static_cast(this)->value = value; - return true; - } - - PUGI__FN bool xpath_variable::set(double value) - { - if (_type != xpath_type_number) return false; - - static_cast(this)->value = value; - return true; - } - - PUGI__FN bool xpath_variable::set(const char_t* value) - { - if (_type != xpath_type_string) return false; - - impl::xpath_variable_string* var = static_cast(this); - - // duplicate string - size_t size = (impl::strlength(value) + 1) * sizeof(char_t); - - char_t* copy = static_cast(impl::xml_memory::allocate(size)); - if (!copy) return false; - - memcpy(copy, value, size); - - // replace old string - if (var->value) impl::xml_memory::deallocate(var->value); - var->value = copy; - - return true; - } - - PUGI__FN bool xpath_variable::set(const xpath_node_set& value) - { - if (_type != xpath_type_node_set) return false; - - static_cast(this)->value = value; - return true; - } - - PUGI__FN xpath_variable_set::xpath_variable_set() - { - for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) _data[i] = 0; - } - - PUGI__FN xpath_variable_set::~xpath_variable_set() - { - for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) - { - xpath_variable* var = _data[i]; - - while (var) - { - xpath_variable* next = var->_next; - - impl::delete_xpath_variable(var->_type, var); - - var = next; - } - } - } - - PUGI__FN xpath_variable* xpath_variable_set::find(const char_t* name) const - { - const size_t hash_size = sizeof(_data) / sizeof(_data[0]); - size_t hash = impl::hash_string(name) % hash_size; - - // look for existing variable - for (xpath_variable* var = _data[hash]; var; var = var->_next) - if (impl::strequal(var->name(), name)) - return var; - - return 0; - } - - PUGI__FN xpath_variable* xpath_variable_set::add(const char_t* name, xpath_value_type type) - { - const size_t hash_size = sizeof(_data) / sizeof(_data[0]); - size_t hash = impl::hash_string(name) % hash_size; - - // look for existing variable - for (xpath_variable* var = _data[hash]; var; var = var->_next) - if (impl::strequal(var->name(), name)) - return var->type() == type ? var : 0; - - // add new variable - xpath_variable* result = impl::new_xpath_variable(type, name); - - if (result) - { - result->_type = type; - result->_next = _data[hash]; - - _data[hash] = result; - } - - return result; - } - - PUGI__FN bool xpath_variable_set::set(const char_t* name, bool value) - { - xpath_variable* var = add(name, xpath_type_boolean); - return var ? var->set(value) : false; - } - - PUGI__FN bool xpath_variable_set::set(const char_t* name, double value) - { - xpath_variable* var = add(name, xpath_type_number); - return var ? var->set(value) : false; - } - - PUGI__FN bool xpath_variable_set::set(const char_t* name, const char_t* value) - { - xpath_variable* var = add(name, xpath_type_string); - return var ? var->set(value) : false; - } - - PUGI__FN bool xpath_variable_set::set(const char_t* name, const xpath_node_set& value) - { - xpath_variable* var = add(name, xpath_type_node_set); - return var ? var->set(value) : false; - } - - PUGI__FN xpath_variable* xpath_variable_set::get(const char_t* name) - { - return find(name); - } - - PUGI__FN const xpath_variable* xpath_variable_set::get(const char_t* name) const - { - return find(name); - } - - PUGI__FN xpath_query::xpath_query(const char_t* query, xpath_variable_set* variables): _impl(0) - { - impl::xpath_query_impl* qimpl = impl::xpath_query_impl::create(); - - if (!qimpl) - { - #ifdef PUGIXML_NO_EXCEPTIONS - _result.error = "Out of memory"; - #else - throw std::bad_alloc(); - #endif - } - else - { - impl::buffer_holder impl_holder(qimpl, impl::xpath_query_impl::destroy); - - qimpl->root = impl::xpath_parser::parse(query, variables, &qimpl->alloc, &_result); - - if (qimpl->root) - { - qimpl->root->optimize(&qimpl->alloc); - - _impl = static_cast(impl_holder.release()); - _result.error = 0; - } - } - } - - PUGI__FN xpath_query::~xpath_query() - { - impl::xpath_query_impl::destroy(_impl); - } - - PUGI__FN xpath_value_type xpath_query::return_type() const - { - if (!_impl) return xpath_type_none; - - return static_cast(_impl)->root->rettype(); - } - - PUGI__FN bool xpath_query::evaluate_boolean(const xpath_node& n) const - { - if (!_impl) return false; - - impl::xpath_context c(n, 1, 1); - impl::xpath_stack_data sd; - - #ifdef PUGIXML_NO_EXCEPTIONS - if (setjmp(sd.error_handler)) return false; - #endif - - return static_cast(_impl)->root->eval_boolean(c, sd.stack); - } - - PUGI__FN double xpath_query::evaluate_number(const xpath_node& n) const - { - if (!_impl) return impl::gen_nan(); - - impl::xpath_context c(n, 1, 1); - impl::xpath_stack_data sd; - - #ifdef PUGIXML_NO_EXCEPTIONS - if (setjmp(sd.error_handler)) return impl::gen_nan(); - #endif - - return static_cast(_impl)->root->eval_number(c, sd.stack); - } - -#ifndef PUGIXML_NO_STL - PUGI__FN string_t xpath_query::evaluate_string(const xpath_node& n) const - { - impl::xpath_stack_data sd; - - impl::xpath_string r = impl::evaluate_string_impl(static_cast(_impl), n, sd); - - return string_t(r.c_str(), r.length()); - } -#endif - - PUGI__FN size_t xpath_query::evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const - { - impl::xpath_stack_data sd; - - impl::xpath_string r = impl::evaluate_string_impl(static_cast(_impl), n, sd); - - size_t full_size = r.length() + 1; - - if (capacity > 0) - { - size_t size = (full_size < capacity) ? full_size : capacity; - assert(size > 0); - - memcpy(buffer, r.c_str(), (size - 1) * sizeof(char_t)); - buffer[size - 1] = 0; - } - - return full_size; - } - - PUGI__FN xpath_node_set xpath_query::evaluate_node_set(const xpath_node& n) const - { - impl::xpath_ast_node* root = impl::evaluate_node_set_prepare(static_cast(_impl)); - if (!root) return xpath_node_set(); - - impl::xpath_context c(n, 1, 1); - impl::xpath_stack_data sd; - - #ifdef PUGIXML_NO_EXCEPTIONS - if (setjmp(sd.error_handler)) return xpath_node_set(); - #endif - - impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_all); - - return xpath_node_set(r.begin(), r.end(), r.type()); - } - - PUGI__FN xpath_node xpath_query::evaluate_node(const xpath_node& n) const - { - impl::xpath_ast_node* root = impl::evaluate_node_set_prepare(static_cast(_impl)); - if (!root) return xpath_node(); - - impl::xpath_context c(n, 1, 1); - impl::xpath_stack_data sd; - - #ifdef PUGIXML_NO_EXCEPTIONS - if (setjmp(sd.error_handler)) return xpath_node(); - #endif - - impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_first); - - return r.first(); - } - - PUGI__FN const xpath_parse_result& xpath_query::result() const - { - return _result; - } - - PUGI__FN static void unspecified_bool_xpath_query(xpath_query***) - { - } - - PUGI__FN xpath_query::operator xpath_query::unspecified_bool_type() const - { - return _impl ? unspecified_bool_xpath_query : 0; - } - - PUGI__FN bool xpath_query::operator!() const - { - return !_impl; - } - - PUGI__FN xpath_node xml_node::select_node(const char_t* query, xpath_variable_set* variables) const - { - xpath_query q(query, variables); - return select_node(q); - } - - PUGI__FN xpath_node xml_node::select_node(const xpath_query& query) const - { - return query.evaluate_node(*this); - } - - PUGI__FN xpath_node_set xml_node::select_nodes(const char_t* query, xpath_variable_set* variables) const - { - xpath_query q(query, variables); - return select_nodes(q); - } - - PUGI__FN xpath_node_set xml_node::select_nodes(const xpath_query& query) const - { - return query.evaluate_node_set(*this); - } - - PUGI__FN xpath_node xml_node::select_single_node(const char_t* query, xpath_variable_set* variables) const - { - xpath_query q(query, variables); - return select_single_node(q); - } - - PUGI__FN xpath_node xml_node::select_single_node(const xpath_query& query) const - { - return query.evaluate_node(*this); - } -} - -#endif - -#ifdef __BORLANDC__ -# pragma option pop -#endif - -// Intel C++ does not properly keep warning state for function templates, -// so popping warning state at the end of translation unit leads to warnings in the middle. -#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) -# pragma warning(pop) -#endif - -// Undefine all local macros (makes sure we're not leaking macros in header-only mode) -#undef PUGI__NO_INLINE -#undef PUGI__UNLIKELY -#undef PUGI__STATIC_ASSERT -#undef PUGI__DMC_VOLATILE -#undef PUGI__MSVC_CRT_VERSION -#undef PUGI__NS_BEGIN -#undef PUGI__NS_END -#undef PUGI__FN -#undef PUGI__FN_NO_INLINE -#undef PUGI__NODETYPE -#undef PUGI__IS_CHARTYPE_IMPL -#undef PUGI__IS_CHARTYPE -#undef PUGI__IS_CHARTYPEX -#undef PUGI__ENDSWITH -#undef PUGI__SKIPWS -#undef PUGI__OPTSET -#undef PUGI__PUSHNODE -#undef PUGI__POPNODE -#undef PUGI__SCANFOR -#undef PUGI__SCANWHILE -#undef PUGI__SCANWHILE_UNROLL -#undef PUGI__ENDSEG -#undef PUGI__THROW_ERROR -#undef PUGI__CHECK_ERROR - -#endif - -/** - * Copyright (c) 2006-2015 Arseny Kapoulkine - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ diff --git a/3rdparty/pugixml-1.6/src/pugixml.hpp b/3rdparty/pugixml-1.6/src/pugixml.hpp deleted file mode 100644 index d59f8640..00000000 --- a/3rdparty/pugixml-1.6/src/pugixml.hpp +++ /dev/null @@ -1,1366 +0,0 @@ -/** - * pugixml parser - version 1.6 - * -------------------------------------------------------- - * Copyright (C) 2006-2015, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) - * Report bugs and download new versions at http://pugixml.org/ - * - * This library is distributed under the MIT License. See notice at the end - * of this file. - * - * This work is based on the pugxml parser, which is: - * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) - */ - -#ifndef PUGIXML_VERSION -// Define version macro; evaluates to major * 100 + minor so that it's safe to use in less-than comparisons -# define PUGIXML_VERSION 160 -#endif - -// Include user configuration file (this can define various configuration macros) -#include "pugiconfig.hpp" - -#ifndef HEADER_PUGIXML_HPP -#define HEADER_PUGIXML_HPP - -// Include stddef.h for size_t and ptrdiff_t -#include - -// Include exception header for XPath -#if !defined(PUGIXML_NO_XPATH) && !defined(PUGIXML_NO_EXCEPTIONS) -# include -#endif - -// Include STL headers -#ifndef PUGIXML_NO_STL -# include -# include -# include -#endif - -// Macro for deprecated features -#ifndef PUGIXML_DEPRECATED -# if defined(__GNUC__) -# define PUGIXML_DEPRECATED __attribute__((deprecated)) -# elif defined(_MSC_VER) && _MSC_VER >= 1300 -# define PUGIXML_DEPRECATED __declspec(deprecated) -# else -# define PUGIXML_DEPRECATED -# endif -#endif - -// If no API is defined, assume default -#ifndef PUGIXML_API -# define PUGIXML_API -#endif - -// If no API for classes is defined, assume default -#ifndef PUGIXML_CLASS -# define PUGIXML_CLASS PUGIXML_API -#endif - -// If no API for functions is defined, assume default -#ifndef PUGIXML_FUNCTION -# define PUGIXML_FUNCTION PUGIXML_API -#endif - -// If the platform is known to have long long support, enable long long functions -#ifndef PUGIXML_HAS_LONG_LONG -# if defined(__cplusplus) && __cplusplus >= 201103 -# define PUGIXML_HAS_LONG_LONG -# elif defined(_MSC_VER) && _MSC_VER >= 1400 -# define PUGIXML_HAS_LONG_LONG -# endif -#endif - -// Character interface macros -#ifdef PUGIXML_WCHAR_MODE -# define PUGIXML_TEXT(t) L ## t -# define PUGIXML_CHAR wchar_t -#else -# define PUGIXML_TEXT(t) t -# define PUGIXML_CHAR char -#endif - -namespace pugi -{ - // Character type used for all internal storage and operations; depends on PUGIXML_WCHAR_MODE - typedef PUGIXML_CHAR char_t; - -#ifndef PUGIXML_NO_STL - // String type used for operations that work with STL string; depends on PUGIXML_WCHAR_MODE - typedef std::basic_string, std::allocator > string_t; -#endif -} - -// The PugiXML namespace -namespace pugi -{ - // Tree node types - enum xml_node_type - { - node_null, // Empty (null) node handle - node_document, // A document tree's absolute root - node_element, // Element tag, i.e. '' - node_pcdata, // Plain character data, i.e. 'text' - node_cdata, // Character data, i.e. '' - node_comment, // Comment tag, i.e. '' - node_pi, // Processing instruction, i.e. '' - node_declaration, // Document declaration, i.e. '' - node_doctype // Document type declaration, i.e. '' - }; - - // Parsing options - - // Minimal parsing mode (equivalent to turning all other flags off). - // Only elements and PCDATA sections are added to the DOM tree, no text conversions are performed. - const unsigned int parse_minimal = 0x0000; - - // This flag determines if processing instructions (node_pi) are added to the DOM tree. This flag is off by default. - const unsigned int parse_pi = 0x0001; - - // This flag determines if comments (node_comment) are added to the DOM tree. This flag is off by default. - const unsigned int parse_comments = 0x0002; - - // This flag determines if CDATA sections (node_cdata) are added to the DOM tree. This flag is on by default. - const unsigned int parse_cdata = 0x0004; - - // This flag determines if plain character data (node_pcdata) that consist only of whitespace are added to the DOM tree. - // This flag is off by default; turning it on usually results in slower parsing and more memory consumption. - const unsigned int parse_ws_pcdata = 0x0008; - - // This flag determines if character and entity references are expanded during parsing. This flag is on by default. - const unsigned int parse_escapes = 0x0010; - - // This flag determines if EOL characters are normalized (converted to #xA) during parsing. This flag is on by default. - const unsigned int parse_eol = 0x0020; - - // This flag determines if attribute values are normalized using CDATA normalization rules during parsing. This flag is on by default. - const unsigned int parse_wconv_attribute = 0x0040; - - // This flag determines if attribute values are normalized using NMTOKENS normalization rules during parsing. This flag is off by default. - const unsigned int parse_wnorm_attribute = 0x0080; - - // This flag determines if document declaration (node_declaration) is added to the DOM tree. This flag is off by default. - const unsigned int parse_declaration = 0x0100; - - // This flag determines if document type declaration (node_doctype) is added to the DOM tree. This flag is off by default. - const unsigned int parse_doctype = 0x0200; - - // This flag determines if plain character data (node_pcdata) that is the only child of the parent node and that consists only - // of whitespace is added to the DOM tree. - // This flag is off by default; turning it on may result in slower parsing and more memory consumption. - const unsigned int parse_ws_pcdata_single = 0x0400; - - // This flag determines if leading and trailing whitespace is to be removed from plain character data. This flag is off by default. - const unsigned int parse_trim_pcdata = 0x0800; - - // This flag determines if plain character data that does not have a parent node is added to the DOM tree, and if an empty document - // is a valid document. This flag is off by default. - const unsigned int parse_fragment = 0x1000; - - // The default parsing mode. - // Elements, PCDATA and CDATA sections are added to the DOM tree, character/reference entities are expanded, - // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. - const unsigned int parse_default = parse_cdata | parse_escapes | parse_wconv_attribute | parse_eol; - - // The full parsing mode. - // Nodes of all types are added to the DOM tree, character/reference entities are expanded, - // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. - const unsigned int parse_full = parse_default | parse_pi | parse_comments | parse_declaration | parse_doctype; - - // These flags determine the encoding of input data for XML document - enum xml_encoding - { - encoding_auto, // Auto-detect input encoding using BOM or < / class xml_object_range - { - public: - typedef It const_iterator; - typedef It iterator; - - xml_object_range(It b, It e): _begin(b), _end(e) - { - } - - It begin() const { return _begin; } - It end() const { return _end; } - - private: - It _begin, _end; - }; - - // Writer interface for node printing (see xml_node::print) - class PUGIXML_CLASS xml_writer - { - public: - virtual ~xml_writer() {} - - // Write memory chunk into stream/file/whatever - virtual void write(const void* data, size_t size) = 0; - }; - - // xml_writer implementation for FILE* - class PUGIXML_CLASS xml_writer_file: public xml_writer - { - public: - // Construct writer from a FILE* object; void* is used to avoid header dependencies on stdio - xml_writer_file(void* file); - - virtual void write(const void* data, size_t size); - - private: - void* file; - }; - - #ifndef PUGIXML_NO_STL - // xml_writer implementation for streams - class PUGIXML_CLASS xml_writer_stream: public xml_writer - { - public: - // Construct writer from an output stream object - xml_writer_stream(std::basic_ostream >& stream); - xml_writer_stream(std::basic_ostream >& stream); - - virtual void write(const void* data, size_t size); - - private: - std::basic_ostream >* narrow_stream; - std::basic_ostream >* wide_stream; - }; - #endif - - // A light-weight handle for manipulating attributes in DOM tree - class PUGIXML_CLASS xml_attribute - { - friend class xml_attribute_iterator; - friend class xml_node; - - private: - xml_attribute_struct* _attr; - - typedef void (*unspecified_bool_type)(xml_attribute***); - - public: - // Default constructor. Constructs an empty attribute. - xml_attribute(); - - // Constructs attribute from internal pointer - explicit xml_attribute(xml_attribute_struct* attr); - - // Safe bool conversion operator - operator unspecified_bool_type() const; - - // Borland C++ workaround - bool operator!() const; - - // Comparison operators (compares wrapped attribute pointers) - bool operator==(const xml_attribute& r) const; - bool operator!=(const xml_attribute& r) const; - bool operator<(const xml_attribute& r) const; - bool operator>(const xml_attribute& r) const; - bool operator<=(const xml_attribute& r) const; - bool operator>=(const xml_attribute& r) const; - - // Check if attribute is empty - bool empty() const; - - // Get attribute name/value, or "" if attribute is empty - const char_t* name() const; - const char_t* value() const; - - // Get attribute value, or the default value if attribute is empty - const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const; - - // Get attribute value as a number, or the default value if conversion did not succeed or attribute is empty - int as_int(int def = 0) const; - unsigned int as_uint(unsigned int def = 0) const; - double as_double(double def = 0) const; - float as_float(float def = 0) const; - - #ifdef PUGIXML_HAS_LONG_LONG - long long as_llong(long long def = 0) const; - unsigned long long as_ullong(unsigned long long def = 0) const; - #endif - - // Get attribute value as bool (returns true if first character is in '1tTyY' set), or the default value if attribute is empty - bool as_bool(bool def = false) const; - - // Set attribute name/value (returns false if attribute is empty or there is not enough memory) - bool set_name(const char_t* rhs); - bool set_value(const char_t* rhs); - - // Set attribute value with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") - bool set_value(int rhs); - bool set_value(unsigned int rhs); - bool set_value(double rhs); - bool set_value(float rhs); - bool set_value(bool rhs); - - #ifdef PUGIXML_HAS_LONG_LONG - bool set_value(long long rhs); - bool set_value(unsigned long long rhs); - #endif - - // Set attribute value (equivalent to set_value without error checking) - xml_attribute& operator=(const char_t* rhs); - xml_attribute& operator=(int rhs); - xml_attribute& operator=(unsigned int rhs); - xml_attribute& operator=(double rhs); - xml_attribute& operator=(float rhs); - xml_attribute& operator=(bool rhs); - - #ifdef PUGIXML_HAS_LONG_LONG - xml_attribute& operator=(long long rhs); - xml_attribute& operator=(unsigned long long rhs); - #endif - - // Get next/previous attribute in the attribute list of the parent node - xml_attribute next_attribute() const; - xml_attribute previous_attribute() const; - - // Get hash value (unique for handles to the same object) - size_t hash_value() const; - - // Get internal pointer - xml_attribute_struct* internal_object() const; - }; - -#ifdef __BORLANDC__ - // Borland C++ workaround - bool PUGIXML_FUNCTION operator&&(const xml_attribute& lhs, bool rhs); - bool PUGIXML_FUNCTION operator||(const xml_attribute& lhs, bool rhs); -#endif - - // A light-weight handle for manipulating nodes in DOM tree - class PUGIXML_CLASS xml_node - { - friend class xml_attribute_iterator; - friend class xml_node_iterator; - friend class xml_named_node_iterator; - - protected: - xml_node_struct* _root; - - typedef void (*unspecified_bool_type)(xml_node***); - - public: - // Default constructor. Constructs an empty node. - xml_node(); - - // Constructs node from internal pointer - explicit xml_node(xml_node_struct* p); - - // Safe bool conversion operator - operator unspecified_bool_type() const; - - // Borland C++ workaround - bool operator!() const; - - // Comparison operators (compares wrapped node pointers) - bool operator==(const xml_node& r) const; - bool operator!=(const xml_node& r) const; - bool operator<(const xml_node& r) const; - bool operator>(const xml_node& r) const; - bool operator<=(const xml_node& r) const; - bool operator>=(const xml_node& r) const; - - // Check if node is empty. - bool empty() const; - - // Get node type - xml_node_type type() const; - - // Get node name, or "" if node is empty or it has no name - const char_t* name() const; - - // Get node value, or "" if node is empty or it has no value - // Note: For text node.value() does not return "text"! Use child_value() or text() methods to access text inside nodes. - const char_t* value() const; - - // Get attribute list - xml_attribute first_attribute() const; - xml_attribute last_attribute() const; - - // Get children list - xml_node first_child() const; - xml_node last_child() const; - - // Get next/previous sibling in the children list of the parent node - xml_node next_sibling() const; - xml_node previous_sibling() const; - - // Get parent node - xml_node parent() const; - - // Get root of DOM tree this node belongs to - xml_node root() const; - - // Get text object for the current node - xml_text text() const; - - // Get child, attribute or next/previous sibling with the specified name - xml_node child(const char_t* name) const; - xml_attribute attribute(const char_t* name) const; - xml_node next_sibling(const char_t* name) const; - xml_node previous_sibling(const char_t* name) const; - - // Get child value of current node; that is, value of the first child node of type PCDATA/CDATA - const char_t* child_value() const; - - // Get child value of child with specified name. Equivalent to child(name).child_value(). - const char_t* child_value(const char_t* name) const; - - // Set node name/value (returns false if node is empty, there is not enough memory, or node can not have name/value) - bool set_name(const char_t* rhs); - bool set_value(const char_t* rhs); - - // Add attribute with specified name. Returns added attribute, or empty attribute on errors. - xml_attribute append_attribute(const char_t* name); - xml_attribute prepend_attribute(const char_t* name); - xml_attribute insert_attribute_after(const char_t* name, const xml_attribute& attr); - xml_attribute insert_attribute_before(const char_t* name, const xml_attribute& attr); - - // Add a copy of the specified attribute. Returns added attribute, or empty attribute on errors. - xml_attribute append_copy(const xml_attribute& proto); - xml_attribute prepend_copy(const xml_attribute& proto); - xml_attribute insert_copy_after(const xml_attribute& proto, const xml_attribute& attr); - xml_attribute insert_copy_before(const xml_attribute& proto, const xml_attribute& attr); - - // Add child node with specified type. Returns added node, or empty node on errors. - xml_node append_child(xml_node_type type = node_element); - xml_node prepend_child(xml_node_type type = node_element); - xml_node insert_child_after(xml_node_type type, const xml_node& node); - xml_node insert_child_before(xml_node_type type, const xml_node& node); - - // Add child element with specified name. Returns added node, or empty node on errors. - xml_node append_child(const char_t* name); - xml_node prepend_child(const char_t* name); - xml_node insert_child_after(const char_t* name, const xml_node& node); - xml_node insert_child_before(const char_t* name, const xml_node& node); - - // Add a copy of the specified node as a child. Returns added node, or empty node on errors. - xml_node append_copy(const xml_node& proto); - xml_node prepend_copy(const xml_node& proto); - xml_node insert_copy_after(const xml_node& proto, const xml_node& node); - xml_node insert_copy_before(const xml_node& proto, const xml_node& node); - - // Move the specified node to become a child of this node. Returns moved node, or empty node on errors. - xml_node append_move(const xml_node& moved); - xml_node prepend_move(const xml_node& moved); - xml_node insert_move_after(const xml_node& moved, const xml_node& node); - xml_node insert_move_before(const xml_node& moved, const xml_node& node); - - // Remove specified attribute - bool remove_attribute(const xml_attribute& a); - bool remove_attribute(const char_t* name); - - // Remove specified child - bool remove_child(const xml_node& n); - bool remove_child(const char_t* name); - - // Parses buffer as an XML document fragment and appends all nodes as children of the current node. - // Copies/converts the buffer, so it may be deleted or changed after the function returns. - // Note: append_buffer allocates memory that has the lifetime of the owning document; removing the appended nodes does not immediately reclaim that memory. - xml_parse_result append_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); - - // Find attribute using predicate. Returns first attribute for which predicate returned true. - template xml_attribute find_attribute(Predicate pred) const - { - if (!_root) return xml_attribute(); - - for (xml_attribute attrib = first_attribute(); attrib; attrib = attrib.next_attribute()) - if (pred(attrib)) - return attrib; - - return xml_attribute(); - } - - // Find child node using predicate. Returns first child for which predicate returned true. - template xml_node find_child(Predicate pred) const - { - if (!_root) return xml_node(); - - for (xml_node node = first_child(); node; node = node.next_sibling()) - if (pred(node)) - return node; - - return xml_node(); - } - - // Find node from subtree using predicate. Returns first node from subtree (depth-first), for which predicate returned true. - template xml_node find_node(Predicate pred) const - { - if (!_root) return xml_node(); - - xml_node cur = first_child(); - - while (cur._root && cur._root != _root) - { - if (pred(cur)) return cur; - - if (cur.first_child()) cur = cur.first_child(); - else if (cur.next_sibling()) cur = cur.next_sibling(); - else - { - while (!cur.next_sibling() && cur._root != _root) cur = cur.parent(); - - if (cur._root != _root) cur = cur.next_sibling(); - } - } - - return xml_node(); - } - - // Find child node by attribute name/value - xml_node find_child_by_attribute(const char_t* name, const char_t* attr_name, const char_t* attr_value) const; - xml_node find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const; - - #ifndef PUGIXML_NO_STL - // Get the absolute node path from root as a text string. - string_t path(char_t delimiter = '/') const; - #endif - - // Search for a node by path consisting of node names and . or .. elements. - xml_node first_element_by_path(const char_t* path, char_t delimiter = '/') const; - - // Recursively traverse subtree with xml_tree_walker - bool traverse(xml_tree_walker& walker); - - #ifndef PUGIXML_NO_XPATH - // Select single node by evaluating XPath query. Returns first node from the resulting node set. - xpath_node select_node(const char_t* query, xpath_variable_set* variables = 0) const; - xpath_node select_node(const xpath_query& query) const; - - // Select node set by evaluating XPath query - xpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = 0) const; - xpath_node_set select_nodes(const xpath_query& query) const; - - // (deprecated: use select_node instead) Select single node by evaluating XPath query. - xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = 0) const; - xpath_node select_single_node(const xpath_query& query) const; - - #endif - - // Print subtree using a writer object - void print(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; - - #ifndef PUGIXML_NO_STL - // Print subtree to stream - void print(std::basic_ostream >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; - void print(std::basic_ostream >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, unsigned int depth = 0) const; - #endif - - // Child nodes iterators - typedef xml_node_iterator iterator; - - iterator begin() const; - iterator end() const; - - // Attribute iterators - typedef xml_attribute_iterator attribute_iterator; - - attribute_iterator attributes_begin() const; - attribute_iterator attributes_end() const; - - // Range-based for support - xml_object_range children() const; - xml_object_range children(const char_t* name) const; - xml_object_range attributes() const; - - // Get node offset in parsed file/string (in char_t units) for debugging purposes - ptrdiff_t offset_debug() const; - - // Get hash value (unique for handles to the same object) - size_t hash_value() const; - - // Get internal pointer - xml_node_struct* internal_object() const; - }; - -#ifdef __BORLANDC__ - // Borland C++ workaround - bool PUGIXML_FUNCTION operator&&(const xml_node& lhs, bool rhs); - bool PUGIXML_FUNCTION operator||(const xml_node& lhs, bool rhs); -#endif - - // A helper for working with text inside PCDATA nodes - class PUGIXML_CLASS xml_text - { - friend class xml_node; - - xml_node_struct* _root; - - typedef void (*unspecified_bool_type)(xml_text***); - - explicit xml_text(xml_node_struct* root); - - xml_node_struct* _data_new(); - xml_node_struct* _data() const; - - public: - // Default constructor. Constructs an empty object. - xml_text(); - - // Safe bool conversion operator - operator unspecified_bool_type() const; - - // Borland C++ workaround - bool operator!() const; - - // Check if text object is empty - bool empty() const; - - // Get text, or "" if object is empty - const char_t* get() const; - - // Get text, or the default value if object is empty - const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const; - - // Get text as a number, or the default value if conversion did not succeed or object is empty - int as_int(int def = 0) const; - unsigned int as_uint(unsigned int def = 0) const; - double as_double(double def = 0) const; - float as_float(float def = 0) const; - - #ifdef PUGIXML_HAS_LONG_LONG - long long as_llong(long long def = 0) const; - unsigned long long as_ullong(unsigned long long def = 0) const; - #endif - - // Get text as bool (returns true if first character is in '1tTyY' set), or the default value if object is empty - bool as_bool(bool def = false) const; - - // Set text (returns false if object is empty or there is not enough memory) - bool set(const char_t* rhs); - - // Set text with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") - bool set(int rhs); - bool set(unsigned int rhs); - bool set(double rhs); - bool set(float rhs); - bool set(bool rhs); - - #ifdef PUGIXML_HAS_LONG_LONG - bool set(long long rhs); - bool set(unsigned long long rhs); - #endif - - // Set text (equivalent to set without error checking) - xml_text& operator=(const char_t* rhs); - xml_text& operator=(int rhs); - xml_text& operator=(unsigned int rhs); - xml_text& operator=(double rhs); - xml_text& operator=(float rhs); - xml_text& operator=(bool rhs); - - #ifdef PUGIXML_HAS_LONG_LONG - xml_text& operator=(long long rhs); - xml_text& operator=(unsigned long long rhs); - #endif - - // Get the data node (node_pcdata or node_cdata) for this object - xml_node data() const; - }; - -#ifdef __BORLANDC__ - // Borland C++ workaround - bool PUGIXML_FUNCTION operator&&(const xml_text& lhs, bool rhs); - bool PUGIXML_FUNCTION operator||(const xml_text& lhs, bool rhs); -#endif - - // Child node iterator (a bidirectional iterator over a collection of xml_node) - class PUGIXML_CLASS xml_node_iterator - { - friend class xml_node; - - private: - mutable xml_node _wrap; - xml_node _parent; - - xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent); - - public: - // Iterator traits - typedef ptrdiff_t difference_type; - typedef xml_node value_type; - typedef xml_node* pointer; - typedef xml_node& reference; - - #ifndef PUGIXML_NO_STL - typedef std::bidirectional_iterator_tag iterator_category; - #endif - - // Default constructor - xml_node_iterator(); - - // Construct an iterator which points to the specified node - xml_node_iterator(const xml_node& node); - - // Iterator operators - bool operator==(const xml_node_iterator& rhs) const; - bool operator!=(const xml_node_iterator& rhs) const; - - xml_node& operator*() const; - xml_node* operator->() const; - - const xml_node_iterator& operator++(); - xml_node_iterator operator++(int); - - const xml_node_iterator& operator--(); - xml_node_iterator operator--(int); - }; - - // Attribute iterator (a bidirectional iterator over a collection of xml_attribute) - class PUGIXML_CLASS xml_attribute_iterator - { - friend class xml_node; - - private: - mutable xml_attribute _wrap; - xml_node _parent; - - xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent); - - public: - // Iterator traits - typedef ptrdiff_t difference_type; - typedef xml_attribute value_type; - typedef xml_attribute* pointer; - typedef xml_attribute& reference; - - #ifndef PUGIXML_NO_STL - typedef std::bidirectional_iterator_tag iterator_category; - #endif - - // Default constructor - xml_attribute_iterator(); - - // Construct an iterator which points to the specified attribute - xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent); - - // Iterator operators - bool operator==(const xml_attribute_iterator& rhs) const; - bool operator!=(const xml_attribute_iterator& rhs) const; - - xml_attribute& operator*() const; - xml_attribute* operator->() const; - - const xml_attribute_iterator& operator++(); - xml_attribute_iterator operator++(int); - - const xml_attribute_iterator& operator--(); - xml_attribute_iterator operator--(int); - }; - - // Named node range helper - class PUGIXML_CLASS xml_named_node_iterator - { - friend class xml_node; - - public: - // Iterator traits - typedef ptrdiff_t difference_type; - typedef xml_node value_type; - typedef xml_node* pointer; - typedef xml_node& reference; - - #ifndef PUGIXML_NO_STL - typedef std::bidirectional_iterator_tag iterator_category; - #endif - - // Default constructor - xml_named_node_iterator(); - - // Construct an iterator which points to the specified node - xml_named_node_iterator(const xml_node& node, const char_t* name); - - // Iterator operators - bool operator==(const xml_named_node_iterator& rhs) const; - bool operator!=(const xml_named_node_iterator& rhs) const; - - xml_node& operator*() const; - xml_node* operator->() const; - - const xml_named_node_iterator& operator++(); - xml_named_node_iterator operator++(int); - - const xml_named_node_iterator& operator--(); - xml_named_node_iterator operator--(int); - - private: - mutable xml_node _wrap; - xml_node _parent; - const char_t* _name; - - xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name); - }; - - // Abstract tree walker class (see xml_node::traverse) - class PUGIXML_CLASS xml_tree_walker - { - friend class xml_node; - - private: - int _depth; - - protected: - // Get current traversal depth - int depth() const; - - public: - xml_tree_walker(); - virtual ~xml_tree_walker(); - - // Callback that is called when traversal begins - virtual bool begin(xml_node& node); - - // Callback that is called for each node traversed - virtual bool for_each(xml_node& node) = 0; - - // Callback that is called when traversal ends - virtual bool end(xml_node& node); - }; - - // Parsing status, returned as part of xml_parse_result object - enum xml_parse_status - { - status_ok = 0, // No error - - status_file_not_found, // File was not found during load_file() - status_io_error, // Error reading from file/stream - status_out_of_memory, // Could not allocate memory - status_internal_error, // Internal error occurred - - status_unrecognized_tag, // Parser could not determine tag type - - status_bad_pi, // Parsing error occurred while parsing document declaration/processing instruction - status_bad_comment, // Parsing error occurred while parsing comment - status_bad_cdata, // Parsing error occurred while parsing CDATA section - status_bad_doctype, // Parsing error occurred while parsing document type declaration - status_bad_pcdata, // Parsing error occurred while parsing PCDATA section - status_bad_start_element, // Parsing error occurred while parsing start element tag - status_bad_attribute, // Parsing error occurred while parsing element attribute - status_bad_end_element, // Parsing error occurred while parsing end element tag - status_end_element_mismatch,// There was a mismatch of start-end tags (closing tag had incorrect name, some tag was not closed or there was an excessive closing tag) - - status_append_invalid_root, // Unable to append nodes since root type is not node_element or node_document (exclusive to xml_node::append_buffer) - - status_no_document_element // Parsing resulted in a document without element nodes - }; - - // Parsing result - struct PUGIXML_CLASS xml_parse_result - { - // Parsing status (see xml_parse_status) - xml_parse_status status; - - // Last parsed offset (in char_t units from start of input data) - ptrdiff_t offset; - - // Source document encoding - xml_encoding encoding; - - // Default constructor, initializes object to failed state - xml_parse_result(); - - // Cast to bool operator - operator bool() const; - - // Get error description - const char* description() const; - }; - - // Document class (DOM tree root) - class PUGIXML_CLASS xml_document: public xml_node - { - private: - char_t* _buffer; - - char _memory[192]; - - // Non-copyable semantics - xml_document(const xml_document&); - const xml_document& operator=(const xml_document&); - - void create(); - void destroy(); - - public: - // Default constructor, makes empty document - xml_document(); - - // Destructor, invalidates all node/attribute handles to this document - ~xml_document(); - - // Removes all nodes, leaving the empty document - void reset(); - - // Removes all nodes, then copies the entire contents of the specified document - void reset(const xml_document& proto); - - #ifndef PUGIXML_NO_STL - // Load document from stream. - xml_parse_result load(std::basic_istream >& stream, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); - xml_parse_result load(std::basic_istream >& stream, unsigned int options = parse_default); - #endif - - // (deprecated: use load_string instead) Load document from zero-terminated string. No encoding conversions are applied. - xml_parse_result load(const char_t* contents, unsigned int options = parse_default); - - // Load document from zero-terminated string. No encoding conversions are applied. - xml_parse_result load_string(const char_t* contents, unsigned int options = parse_default); - - // Load document from file - xml_parse_result load_file(const char* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); - xml_parse_result load_file(const wchar_t* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); - - // Load document from buffer. Copies/converts the buffer, so it may be deleted or changed after the function returns. - xml_parse_result load_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); - - // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data). - // You should ensure that buffer data will persist throughout the document's lifetime, and free the buffer memory manually once document is destroyed. - xml_parse_result load_buffer_inplace(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); - - // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data). - // You should allocate the buffer with pugixml allocation function; document will free the buffer when it is no longer needed (you can't use it anymore). - xml_parse_result load_buffer_inplace_own(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); - - // Save XML document to writer (semantics is slightly different from xml_node::print, see documentation for details). - void save(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; - - #ifndef PUGIXML_NO_STL - // Save XML document to stream (semantics is slightly different from xml_node::print, see documentation for details). - void save(std::basic_ostream >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; - void save(std::basic_ostream >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default) const; - #endif - - // Save XML to file - bool save_file(const char* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; - bool save_file(const wchar_t* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; - - // Get document element - xml_node document_element() const; - }; - -#ifndef PUGIXML_NO_XPATH - // XPath query return type - enum xpath_value_type - { - xpath_type_none, // Unknown type (query failed to compile) - xpath_type_node_set, // Node set (xpath_node_set) - xpath_type_number, // Number - xpath_type_string, // String - xpath_type_boolean // Boolean - }; - - // XPath parsing result - struct PUGIXML_CLASS xpath_parse_result - { - // Error message (0 if no error) - const char* error; - - // Last parsed offset (in char_t units from string start) - ptrdiff_t offset; - - // Default constructor, initializes object to failed state - xpath_parse_result(); - - // Cast to bool operator - operator bool() const; - - // Get error description - const char* description() const; - }; - - // A single XPath variable - class PUGIXML_CLASS xpath_variable - { - friend class xpath_variable_set; - - protected: - xpath_value_type _type; - xpath_variable* _next; - - xpath_variable(); - - // Non-copyable semantics - xpath_variable(const xpath_variable&); - xpath_variable& operator=(const xpath_variable&); - - public: - // Get variable name - const char_t* name() const; - - // Get variable type - xpath_value_type type() const; - - // Get variable value; no type conversion is performed, default value (false, NaN, empty string, empty node set) is returned on type mismatch error - bool get_boolean() const; - double get_number() const; - const char_t* get_string() const; - const xpath_node_set& get_node_set() const; - - // Set variable value; no type conversion is performed, false is returned on type mismatch error - bool set(bool value); - bool set(double value); - bool set(const char_t* value); - bool set(const xpath_node_set& value); - }; - - // A set of XPath variables - class PUGIXML_CLASS xpath_variable_set - { - private: - xpath_variable* _data[64]; - - // Non-copyable semantics - xpath_variable_set(const xpath_variable_set&); - xpath_variable_set& operator=(const xpath_variable_set&); - - xpath_variable* find(const char_t* name) const; - - public: - // Default constructor/destructor - xpath_variable_set(); - ~xpath_variable_set(); - - // Add a new variable or get the existing one, if the types match - xpath_variable* add(const char_t* name, xpath_value_type type); - - // Set value of an existing variable; no type conversion is performed, false is returned if there is no such variable or if types mismatch - bool set(const char_t* name, bool value); - bool set(const char_t* name, double value); - bool set(const char_t* name, const char_t* value); - bool set(const char_t* name, const xpath_node_set& value); - - // Get existing variable by name - xpath_variable* get(const char_t* name); - const xpath_variable* get(const char_t* name) const; - }; - - // A compiled XPath query object - class PUGIXML_CLASS xpath_query - { - private: - void* _impl; - xpath_parse_result _result; - - typedef void (*unspecified_bool_type)(xpath_query***); - - // Non-copyable semantics - xpath_query(const xpath_query&); - xpath_query& operator=(const xpath_query&); - - public: - // Construct a compiled object from XPath expression. - // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on compilation errors. - explicit xpath_query(const char_t* query, xpath_variable_set* variables = 0); - - // Destructor - ~xpath_query(); - - // Get query expression return type - xpath_value_type return_type() const; - - // Evaluate expression as boolean value in the specified context; performs type conversion if necessary. - // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. - bool evaluate_boolean(const xpath_node& n) const; - - // Evaluate expression as double value in the specified context; performs type conversion if necessary. - // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. - double evaluate_number(const xpath_node& n) const; - - #ifndef PUGIXML_NO_STL - // Evaluate expression as string value in the specified context; performs type conversion if necessary. - // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. - string_t evaluate_string(const xpath_node& n) const; - #endif - - // Evaluate expression as string value in the specified context; performs type conversion if necessary. - // At most capacity characters are written to the destination buffer, full result size is returned (includes terminating zero). - // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. - // If PUGIXML_NO_EXCEPTIONS is defined, returns empty set instead. - size_t evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const; - - // Evaluate expression as node set in the specified context. - // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors. - // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node set instead. - xpath_node_set evaluate_node_set(const xpath_node& n) const; - - // Evaluate expression as node set in the specified context. - // Return first node in document order, or empty node if node set is empty. - // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors. - // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node instead. - xpath_node evaluate_node(const xpath_node& n) const; - - // Get parsing result (used to get compilation errors in PUGIXML_NO_EXCEPTIONS mode) - const xpath_parse_result& result() const; - - // Safe bool conversion operator - operator unspecified_bool_type() const; - - // Borland C++ workaround - bool operator!() const; - }; - - #ifndef PUGIXML_NO_EXCEPTIONS - // XPath exception class - class PUGIXML_CLASS xpath_exception: public std::exception - { - private: - xpath_parse_result _result; - - public: - // Construct exception from parse result - explicit xpath_exception(const xpath_parse_result& result); - - // Get error message - virtual const char* what() const throw(); - - // Get parse result - const xpath_parse_result& result() const; - }; - #endif - - // XPath node class (either xml_node or xml_attribute) - class PUGIXML_CLASS xpath_node - { - private: - xml_node _node; - xml_attribute _attribute; - - typedef void (*unspecified_bool_type)(xpath_node***); - - public: - // Default constructor; constructs empty XPath node - xpath_node(); - - // Construct XPath node from XML node/attribute - xpath_node(const xml_node& node); - xpath_node(const xml_attribute& attribute, const xml_node& parent); - - // Get node/attribute, if any - xml_node node() const; - xml_attribute attribute() const; - - // Get parent of contained node/attribute - xml_node parent() const; - - // Safe bool conversion operator - operator unspecified_bool_type() const; - - // Borland C++ workaround - bool operator!() const; - - // Comparison operators - bool operator==(const xpath_node& n) const; - bool operator!=(const xpath_node& n) const; - }; - -#ifdef __BORLANDC__ - // Borland C++ workaround - bool PUGIXML_FUNCTION operator&&(const xpath_node& lhs, bool rhs); - bool PUGIXML_FUNCTION operator||(const xpath_node& lhs, bool rhs); -#endif - - // A fixed-size collection of XPath nodes - class PUGIXML_CLASS xpath_node_set - { - public: - // Collection type - enum type_t - { - type_unsorted, // Not ordered - type_sorted, // Sorted by document order (ascending) - type_sorted_reverse // Sorted by document order (descending) - }; - - // Constant iterator type - typedef const xpath_node* const_iterator; - - // We define non-constant iterator to be the same as constant iterator so that various generic algorithms (i.e. boost foreach) work - typedef const xpath_node* iterator; - - // Default constructor. Constructs empty set. - xpath_node_set(); - - // Constructs a set from iterator range; data is not checked for duplicates and is not sorted according to provided type, so be careful - xpath_node_set(const_iterator begin, const_iterator end, type_t type = type_unsorted); - - // Destructor - ~xpath_node_set(); - - // Copy constructor/assignment operator - xpath_node_set(const xpath_node_set& ns); - xpath_node_set& operator=(const xpath_node_set& ns); - - // Get collection type - type_t type() const; - - // Get collection size - size_t size() const; - - // Indexing operator - const xpath_node& operator[](size_t index) const; - - // Collection iterators - const_iterator begin() const; - const_iterator end() const; - - // Sort the collection in ascending/descending order by document order - void sort(bool reverse = false); - - // Get first node in the collection by document order - xpath_node first() const; - - // Check if collection is empty - bool empty() const; - - private: - type_t _type; - - xpath_node _storage; - - xpath_node* _begin; - xpath_node* _end; - - void _assign(const_iterator begin, const_iterator end); - }; -#endif - -#ifndef PUGIXML_NO_STL - // Convert wide string to UTF8 - std::basic_string, std::allocator > PUGIXML_FUNCTION as_utf8(const wchar_t* str); - std::basic_string, std::allocator > PUGIXML_FUNCTION as_utf8(const std::basic_string, std::allocator >& str); - - // Convert UTF8 to wide string - std::basic_string, std::allocator > PUGIXML_FUNCTION as_wide(const char* str); - std::basic_string, std::allocator > PUGIXML_FUNCTION as_wide(const std::basic_string, std::allocator >& str); -#endif - - // Memory allocation function interface; returns pointer to allocated memory or NULL on failure - typedef void* (*allocation_function)(size_t size); - - // Memory deallocation function interface - typedef void (*deallocation_function)(void* ptr); - - // Override default memory management functions. All subsequent allocations/deallocations will be performed via supplied functions. - void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate); - - // Get current memory management functions - allocation_function PUGIXML_FUNCTION get_memory_allocation_function(); - deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function(); -} - -#if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC)) -namespace std -{ - // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier) - std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_node_iterator&); - std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_attribute_iterator&); - std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_named_node_iterator&); -} -#endif - -#if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC) -namespace std -{ - // Workarounds for (non-standard) iterator category detection - std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_node_iterator&); - std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_attribute_iterator&); - std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_named_node_iterator&); -} -#endif - -#endif - -// Make sure implementation is included in header-only mode -// Use macro expansion in #include to work around QMake (QTBUG-11923) -#if defined(PUGIXML_HEADER_ONLY) && !defined(PUGIXML_SOURCE) -# define PUGIXML_SOURCE "pugixml.cpp" -# include PUGIXML_SOURCE -#endif - -/** - * Copyright (c) 2006-2015 Arseny Kapoulkine - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ diff --git a/3rdparty/pugixml_build/CMakeLists.txt b/3rdparty/pugixml_build/CMakeLists.txt new file mode 100644 index 00000000..6dcf26e4 --- /dev/null +++ b/3rdparty/pugixml_build/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 2.8) +Project(pugixml CXX) + +set(CMAKE_BUILD_TYPE Release) +add_library(pugixml SHARED ${CMAKE_SOURCE_DIR}/3rdparty/pugixml/src/pugixml.cpp) diff --git a/3rdparty/svmlight/CMakeLists.txt b/3rdparty/svmlight/CMakeLists.txt deleted file mode 100644 index 5000e2a0..00000000 --- a/3rdparty/svmlight/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -add_library(od_svmlight SHARED svm_classify.c svm_common.c svm_hideo.c svm_learn.c) diff --git a/CMakeLists.txt b/CMakeLists.txt index 94902837..83f38e4b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,11 +11,10 @@ set(OD_BINARY_DIR ${OpenDetection_BINARY_DIR}) set(OD_CMAKE_DIR "${OpenDetection_SOURCE_DIR}/cmake" "${OpenDetection_SOURCE_DIR}/cmake/modules") set(CMAKE_MODULE_PATH ${OD_CMAKE_DIR} ${CMAKE_MODULE_PATH}) - - - # include bunch of stuffs and set the appropriate variables +include_directories("/home/giacomo/libraries/opencv3.1/prefix/include") + # 1. Version include(od_version) set(OD_VERSION "${OD_MAJOR_VERSION}.${OD_MINOR_VERSION}") @@ -33,25 +32,18 @@ include(od_mandatory_dependency) # 2. collect config options and set appropriate variables which will be used in the next hierarchy option(WITH_DOCUMENTATION "Build the OD documentation" ON) option(WITH_GPU "Build GPU components of the project" ON) -option(WITH_SVMLIGHT "Build SVM Light" ON) +option(WITH_SVMLIGHT "Build SVM Light" OFF) # 3. add all the modules -set(OD_MODULES_NAMES common doc detectors examples 3rdparty) +set(OD_MODULES_NAMES 3rdparty common doc detectors examples ) set(OD_MODULES_DIRS ${OD_MODULES_NAMES}) foreach(subdir ${OD_MODULES_DIRS}) add_subdirectory("${OD_SOURCE_DIR}/${subdir}") endforeach(subdir) - - - - #include folders include_directories("${OD_BINARY_DIR}/generated") - - - set(SOURCE_FILES opendetection.cpp) add_executable(opendetection ${SOURCE_FILES}) \ No newline at end of file diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 54a9a43a..d47dd7a8 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -3,12 +3,15 @@ set(LIB_NAME od_${SUBSYS_NAME}) set(SUBSYS_DESC "The core module of OpenDetection having the pipeline logic") set(SUBSYS_DEPS ${OpenCV_LIBS} ${PCL_LIBRARIES}) +message("place is " ${OpenCV_INCLUDE_DIRS}) + if(WITH_GPU) set(SUBSYS_DEPS ${SUBSYS_DEPS} siftgpu) endif() -set(build TRUE) +include_directories(${OpenCV_INCLUDE_DIRS}) +set(build TRUE) if(build) diff --git a/common/utils/ODFrameGenerator.h b/common/utils/ODFrameGenerator.h index fd675b77..42d0b312 100644 --- a/common/utils/ODFrameGenerator.h +++ b/common/utils/ODFrameGenerator.h @@ -217,5 +217,5 @@ namespace od }; } -// + #endif //OPENDETECTION_ODFRAMEGENERATOR_H diff --git a/detectors/global2D/CMakeLists.txt b/detectors/global2D/CMakeLists.txt index e240743c..43fafa08 100644 --- a/detectors/global2D/CMakeLists.txt +++ b/detectors/global2D/CMakeLists.txt @@ -2,8 +2,7 @@ set(SUBSYS_NAME global_image_detector) set(LIB_NAME od_${SUBSYS_NAME}) set(SUBSYS_DESC "global feature based detection in 2D images") -set(SUBSYS_DEPS od_common ${OpenCV_LIBS} od_svmlight) - +set(SUBSYS_DEPS od_common ${OpenCV_LIBS}) set(build TRUE) #PCL_SUBSYS_OPTION(build "${SUBSYS_NAME}" "${SUBSYS_DESC}" ON) @@ -12,32 +11,53 @@ set(build TRUE) if(build) + + set(impl_incs + ) +if(WITH_SVMLIGHT) + set(srcs + ODFaceRecognizer.cpp + "detection/ODHOGDetector.cpp" + "detection/ODCascadeDetector.cpp" + "training/ODHOGTrainer.cpp" + ) set(incs "ODFaceRecognizer.h" "detection/ODHOGDetector.h" "detection/ODCascadeDetector.h" - - "training/ODHOGTrainer.h" ) - set(impl_incs - ) +else() set(srcs - ODFaceRecognizer.cpp - "detection/ODHOGDetector.cpp" - "detection/ODCascadeDetector.cpp" - "training/ODHOGTrainer.cpp" + ODFaceRecognizer.cpp + "detection/ODCascadeDetector.cpp" + ) + + set(incs + "ODFaceRecognizer.h" + "detection/ODCascadeDetector.h" ) +endif() + + OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${srcs} INCS ${incs} ${impl_incs}) - install(FILES ${incs} DESTINATION ${OD_INSTALL_INCLUDE_DIR}/${SUBSYS_NAME} COMPONENT ${LIB_NAME}) + install(FILES ${incs} DESTINATION ${OD_INSTALL_INCLUDE_DIR}/${SUBSYS_NAME} COMPONENT ${LIB_NAME} ) +if(WITH_SVMLIGHT) + if(SUBSYS_DEPS) + target_link_libraries("${LIB_NAME}" ${SUBSYS_DEPS} svm) + endif(SUBSYS_DEPS) +else() if(SUBSYS_DEPS) - target_link_libraries("${LIB_NAME}" ${SUBSYS_DEPS}) + target_link_libraries("${LIB_NAME}" ${SUBSYS_DEPS} ) endif(SUBSYS_DEPS) +endif() + + #PCL_MAKE_PKGCONFIG("${LIB_NAME}" "${SUBSYS_NAME}" "${SUBSYS_DESC}" "${SUBSYS_DEPS}" "" "" "" "") diff --git a/detectors/global2D/detection/ODCascadeDetector.h b/detectors/global2D/detection/ODCascadeDetector.h index 9d152d84..27b92c4c 100644 --- a/detectors/global2D/detection/ODCascadeDetector.h +++ b/detectors/global2D/detection/ODCascadeDetector.h @@ -81,8 +81,8 @@ namespace od cv::Size maxSize_; }; - /** \example objectdetector/od_image_cascade.cpp - * \example objectdetector/od_image_cascade_files.cpp + /** \examples objectdetector/od_image_cascade.cpp + * \examples objectdetector/od_image_cascade_files.cpp */ } diff --git a/detectors/global2D/detection/ODHOGDetector.h b/detectors/global2D/detection/ODHOGDetector.h index b5781dcc..dad5b064 100644 --- a/detectors/global2D/detection/ODHOGDetector.h +++ b/detectors/global2D/detection/ODHOGDetector.h @@ -178,9 +178,9 @@ namespace od }; - /** \example objectdetector/od_image_hog.cpp - * \example objectdetector/od_image_hog_files.cpp - * \example apps/global2D/od_multihog_app.cpp + /** \examples objectdetector/od_image_hog.cpp + * \examples objectdetector/od_image_hog_files.cpp + * \examples apps/global2D/od_multihog_app.cpp * This is an example of how to use the ODHOGDetector class. * * More details about this example. diff --git a/detectors/global3D/CMakeLists.txt b/detectors/global3D/CMakeLists.txt index 86ce4270..42a1dbdd 100644 --- a/detectors/global3D/CMakeLists.txt +++ b/detectors/global3D/CMakeLists.txt @@ -1,7 +1,7 @@ set(SUBSYS_NAME pointcloud_global_detector) set(LIB_NAME od_${SUBSYS_NAME}) set(SUBSYS_DESC "The global detector for point cloouds") -set(SUBSYS_DEPS od_common ${PCL_LIBRARIES} ${VTK_LIBRARIES} ${OpenCV_LIBS} pugixml siftgpu) +set(SUBSYS_DEPS od_common ${PCL_LIBRARIES} ${VTK_LIBRARIES} ${OpenCV_LIBS} siftgpu) set(build TRUE) #PCL_SUBSYS_OPTION(build "${SUBSYS_NAME}" "${SUBSYS_DESC}" ON) diff --git a/detectors/local2D/CMakeLists.txt b/detectors/local2D/CMakeLists.txt index cbce88eb..df897e4a 100644 --- a/detectors/local2D/CMakeLists.txt +++ b/detectors/local2D/CMakeLists.txt @@ -2,8 +2,10 @@ set(SUBSYS_NAME local_image_detector) set(LIB_NAME od_${SUBSYS_NAME}) set(SUBSYS_DESC "The local feature matching based detector") -set(SUBSYS_DEPS od_common ${PCL_LIBRARIES} ${VTK_LIBRARIES} ${OpenCV_LIBS} pugixml) +set(SUBSYS_DEPS od_common ${PCL_LIBRARIES} ${VTK_LIBRARIES} ${OpenCV_LIBS}) +include_directories(${CMAKE_SOURCE_DIR}/3rdparty/pugixml/src/) +link_directories(${CMAKE_SOURCE_DIR}/3rdparty/pugixml/build/) set(build TRUE) #PCL_SUBSYS_OPTION(build "${SUBSYS_NAME}" "${SUBSYS_DESC}" ON) @@ -39,7 +41,7 @@ if(build) install(FILES ${incs} DESTINATION ${OD_INSTALL_INCLUDE_DIR}/${SUBSYS_NAME} COMPONENT ${LIB_NAME}) if(SUBSYS_DEPS) - target_link_libraries("${LIB_NAME}" ${SUBSYS_DEPS}) + target_link_libraries("${LIB_NAME}" ${SUBSYS_DEPS} pugixml) endif(SUBSYS_DEPS) #PCL_MAKE_PKGCONFIG("${LIB_NAME}" "${SUBSYS_NAME}" "${SUBSYS_DESC}" "${SUBSYS_DEPS}" "" "" "" "") diff --git a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md index 304da9e3..18b54841 100644 --- a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md +++ b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md @@ -7,4 +7,35 @@ Framework design and library maintenance {#gsoc2016_blog_giacomo1} ==== - [Link to Proposal](https://docs.google.com/document/d/16Wyd0h5b9-7DaG7ZYJT30a2i096krviFUCcDYwg-jZc/edit?usp=sharing) - [Link to GSoC2016 Project Page](https://summerofcode.withgoogle.com/organizations/6007728078061568/#5675882488266752) - - \ref gsoc2016_blog_giacomo "Link to blog" \ No newline at end of file + +**About Me** + +I finished my **joint master degree** in computer science and networking at Sant'Annas school of advanced studies and the university of Pisa in 2014 with a thesis on the static allocation of real-time OpenMP jobs on multicore machines. The master program was focused on parallel and high performance computing including OpenMP, MPI, Cuda and Tbb. + +I started then working as a scholar on the Pelars project (Practice-based Experiential Learning Analytics Research And Support) at the Laboratory of Perceptual Robotics (PERCRO), which is part of the Institute of Communication, Information and Perception Technologies (TECIP) of the Scuola +Superiore Sant’Anna, Pisa. + +In November 2014 I started my PhD in Perceptual Robotics, researching Computer Vision for robotic applications. The first part of my research concerned object recognition algorithms, mainly developing real time solutions; I have worked with different approaches ranging from classical descriptor matching pipelines to machine learning techniques. Both 2D and 3D approaches have been investigated, using different platforms and sensors. I am also active in the research area of RGB-D cameras creating interfaces and testing new sensors. + +I stayed as a visiting student for one month at **ETHZ** to investigating object recognition using **Random Forests**, which gave me some great insights on the use of machine learning tecniques in this filed. + +Code is mainly developed using **C**++, **Java**. +I am contributing to the different libraries like **PCL**, **OpenCV** and **libfreenect2**. + +-[Link to Github](https://github.com/giacomodabisias) + +**General Project Idea** + +The concept of this gsoc project is to restructure the OpenDetection Library in order to make it more usable, scalable and documented. In a second phase there could also be the possibility to implement some new features in the library. + +The implementation can be divided into several work tasks +- **Task 1** - fix dependencies and modular build : Restructure the Cmake files to obtain a clean and expandable structure. Remove as many dependencies as possible and set build options for the different OpenDetection modules based on the OpenCV/PCL dependencies. +Depending on OpenCV/PCL version activate/deactivate modules of the library. +- **Task 2** - small tasks : Restructure the code by moving the different files into appropriate folders. Move 3rd party software to git sub modules or downloadable cmake content. Use more templetization and create precompiled versions for basic types. Enhance performances by optimizing the whole code structure. All these steps include the addition of a fixed coding style which can be derived or adopted by an existing one. +- **Task 3** - moderate task - memory allocation : Restructure memory allocation using shared_pointers where possible to avoid dynamic memory allocation with new. +- **Task 4** - moderate task - generalized viewer : Create a generalized viewer interface for 2D images and 3D point clouds based on VTK. +- **Task 5** - moderate task - packaging and cross compilation : Fix the Cmake files in order to make the library cross platform buildable on Linux and Windows systems. Add header precompilation if possible and ProjectConfig.cmake files to to make the library usable by other projects. +- **Task 6** - moderate - input enhancements : A lot of classes have the possibility to load data from file/path. Create template versions of them in order to let people also load data from vectors/arrays etc.. +- **Task 8** - small - Documentation and examples : Continue the documentation process and add new examples for the new implemented structures/algorithms. +- **Task 9** - small task - merge other gsoc16 contributions : Merge changes from other contribution of GSOC. Other projects will be using existing APIs. There might me need to make small changes to fit to the changes made in this project +- **Task 10** - moderate task - deb packaging : Create an automated way to generate a deb file for the library so it can be installed through debian packaging system. diff --git a/examples/apps/CMakeLists.txt b/examples/apps/CMakeLists.txt index 264a2d2d..381347d7 100644 --- a/examples/apps/CMakeLists.txt +++ b/examples/apps/CMakeLists.txt @@ -1,5 +1,7 @@ OD_ADD_EXAMPLE(odcadrecog_test_single_model FILES cadrecog2D/od_test_single_db_single_model.cpp LINK_WITH od_common od_local_image_detector) -OD_ADD_EXAMPLE(od_multihog_app FILES global2D/od_multihog_app.cpp - LINK_WITH od_common od_global_image_detector) \ No newline at end of file +if(WITH_SVMLIGHT) + OD_ADD_EXAMPLE(od_multihog_app FILES global2D/od_multihog_app.cpp + LINK_WITH od_common od_global_image_detector) +endif() \ No newline at end of file diff --git a/examples/objectdetector/CMakeLists.txt b/examples/objectdetector/CMakeLists.txt index a98b594e..c6b63a88 100644 --- a/examples/objectdetector/CMakeLists.txt +++ b/examples/objectdetector/CMakeLists.txt @@ -4,10 +4,7 @@ OD_ADD_EXAMPLE(od_image_camera FILES od_image_cadrecog_camera.cpp OD_ADD_EXAMPLE(od_example_files FILES od_image_cadrecog_files.cpp LINK_WITH od_common od_local_image_detector) - - - - +if(WITH_SVMLIGHT) OD_ADD_EXAMPLE(od_hog_train FILES od_hog_train.cpp LINK_WITH od_common od_global_image_detector) @@ -18,6 +15,15 @@ OD_ADD_EXAMPLE(od_image_hog_files FILES od_image_hog_files.cpp OD_ADD_EXAMPLE(od_image_customhog FILES od_image_customhog.cpp LINK_WITH od_common od_global_image_detector) + +OD_ADD_EXAMPLE(od_multialgo_files FILES od_multialgo_files.cpp + LINK_WITH od_miscellaneous_detector) + +OD_ADD_EXAMPLE(od_multialgo_pc FILES od_multialgo_pc.cpp + LINK_WITH od_miscellaneous_detector) + +endif() + OD_ADD_EXAMPLE(od_cascade_cam FILES od_cascade_cam.cpp LINK_WITH od_common od_global_image_detector) @@ -44,9 +50,5 @@ OD_ADD_EXAMPLE(od_example_framegenerator FILES od_example_framegenerator.cpp -OD_ADD_EXAMPLE(od_multialgo_files FILES od_multialgo_files.cpp - LINK_WITH od_miscellaneous_detector) -OD_ADD_EXAMPLE(od_multialgo_pc FILES od_multialgo_pc.cpp - LINK_WITH od_miscellaneous_detector) From 6343ab89bb44e0d1f0ba7f66eaf2243f0dda16f2 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias Date: Fri, 13 May 2016 11:13:33 +0200 Subject: [PATCH 02/79] added siftgpu as git submodule --- .gitmodules | 3 + 3rdparty/SiftGPU | 1 + 3rdparty/SiftGPU/CMakeLists.txt | 7 - 3rdparty/SiftGPU/History.txt | 190 -- 3rdparty/SiftGPU/OpenGL_and_CUDA.txt | 79 - 3rdparty/SiftGPU/README.txt | 58 - 3rdparty/SiftGPU/bin/DevIL.dll | Bin 670720 -> 0 bytes 3rdparty/SiftGPU/bin/DevIL64.dll | Bin 2300928 -> 0 bytes 3rdparty/SiftGPU/bin/SiftGPU.dll | Bin 1807872 -> 0 bytes 3rdparty/SiftGPU/bin/SiftGPU64.dll | Bin 433664 -> 0 bytes 3rdparty/SiftGPU/bin/SimpleSIFT.exe | Bin 153600 -> 0 bytes 3rdparty/SiftGPU/bin/Speed.exe | Bin 154112 -> 0 bytes 3rdparty/SiftGPU/bin/TestWin.exe | Bin 158720 -> 0 bytes 3rdparty/SiftGPU/bin/glew32.dll | Bin 245760 -> 0 bytes 3rdparty/SiftGPU/bin/glew64.dll | Bin 279552 -> 0 bytes 3rdparty/SiftGPU/license.txt | 17 - 3rdparty/SiftGPU/makefile | 207 -- .../msvc/ServerSiftGPU/SiftGPU_Server.dsp | 102 - .../msvc/ServerSiftGPU/SiftGPU_Server.vcxproj | 204 -- 3rdparty/SiftGPU/msvc/SiftGPU.dsw | 119 - 3rdparty/SiftGPU/msvc/SiftGPU.sln | 86 - 3rdparty/SiftGPU/msvc/SiftGPU/SiftGPU.def | 6 - 3rdparty/SiftGPU/msvc/SiftGPU/SiftGPU.dsp | 204 -- 3rdparty/SiftGPU/msvc/SiftGPU/SiftGPU.vcxproj | 375 --- .../msvc/SiftGPU/SiftGPU_CUDA_Enabled.vcxproj | 390 --- .../SiftGPU/msvc/SiftGPU_CUDA_Enabled.sln | 105 - .../SiftGPU/msvc/TestWin/MultiThreadSIFT.dsp | 102 - .../msvc/TestWin/MultiThreadSIFT.vcxproj | 259 -- 3rdparty/SiftGPU/msvc/TestWin/SimpleSIFT.dsp | 102 - .../SiftGPU/msvc/TestWin/SimpleSIFT.vcxproj | 259 -- 3rdparty/SiftGPU/msvc/TestWin/Speed.dsp | 102 - 3rdparty/SiftGPU/msvc/TestWin/Speed.vcxproj | 272 -- 3rdparty/SiftGPU/msvc/TestWin/TestBase.dsp | 104 - .../SiftGPU/msvc/TestWin/TestBase.vcxproj | 230 -- 3rdparty/SiftGPU/msvc/TestWin/TestWin.dsp | 115 - 3rdparty/SiftGPU/msvc/TestWin/TestWin.vcxproj | 292 -- 3rdparty/SiftGPU/msvc/TestWin/TestWinGlut.dsp | 106 - .../SiftGPU/msvc/TestWin/TestWinGlut.vcxproj | 280 -- 3rdparty/SiftGPU/speed_and_accuracy.txt | 87 - 3rdparty/SiftGPU/src/CMakeLists.txt | 1 - .../src/ServerSiftGPU/ServerSiftGPU.cpp | 902 ------ .../SiftGPU/src/ServerSiftGPU/ServerSiftGPU.h | 159 - 3rdparty/SiftGPU/src/ServerSiftGPU/server.cpp | 119 - 3rdparty/SiftGPU/src/SiftGPU/CLTexImage.cpp | 229 -- 3rdparty/SiftGPU/src/SiftGPU/CLTexImage.h | 83 - 3rdparty/SiftGPU/src/SiftGPU/CMakeLists.txt | 50 - 3rdparty/SiftGPU/src/SiftGPU/CuTexImage.cpp | 259 -- 3rdparty/SiftGPU/src/SiftGPU/CuTexImage.h | 76 - .../SiftGPU/src/SiftGPU/FrameBufferObject.cpp | 105 - .../SiftGPU/src/SiftGPU/FrameBufferObject.h | 49 - 3rdparty/SiftGPU/src/SiftGPU/GLTexImage.cpp | 1264 -------- 3rdparty/SiftGPU/src/SiftGPU/GLTexImage.h | 158 - 3rdparty/SiftGPU/src/SiftGPU/GlobalUtil.cpp | 519 --- 3rdparty/SiftGPU/src/SiftGPU/GlobalUtil.h | 157 - 3rdparty/SiftGPU/src/SiftGPU/LiteWindow.h | 197 -- 3rdparty/SiftGPU/src/SiftGPU/ProgramCG.cpp | 2765 ---------------- 3rdparty/SiftGPU/src/SiftGPU/ProgramCG.h | 161 - 3rdparty/SiftGPU/src/SiftGPU/ProgramCL.cpp | 1592 ---------- 3rdparty/SiftGPU/src/SiftGPU/ProgramCL.h | 164 - 3rdparty/SiftGPU/src/SiftGPU/ProgramCU.cu | 1795 ----------- 3rdparty/SiftGPU/src/SiftGPU/ProgramCU.h | 74 - 3rdparty/SiftGPU/src/SiftGPU/ProgramGLSL.cpp | 2690 ---------------- 3rdparty/SiftGPU/src/SiftGPU/ProgramGLSL.h | 267 -- 3rdparty/SiftGPU/src/SiftGPU/ProgramGPU.cpp | 38 - 3rdparty/SiftGPU/src/SiftGPU/ProgramGPU.h | 59 - 3rdparty/SiftGPU/src/SiftGPU/PyramidCL.cpp | 1132 ------- 3rdparty/SiftGPU/src/SiftGPU/PyramidCL.h | 83 - 3rdparty/SiftGPU/src/SiftGPU/PyramidCU.cpp | 1190 ------- 3rdparty/SiftGPU/src/SiftGPU/PyramidCU.h | 86 - 3rdparty/SiftGPU/src/SiftGPU/PyramidGL.cpp | 2805 ----------------- 3rdparty/SiftGPU/src/SiftGPU/PyramidGL.h | 119 - 3rdparty/SiftGPU/src/SiftGPU/ShaderMan.cpp | 349 -- 3rdparty/SiftGPU/src/SiftGPU/ShaderMan.h | 80 - 3rdparty/SiftGPU/src/SiftGPU/SiftGPU.cpp | 1440 --------- 3rdparty/SiftGPU/src/SiftGPU/SiftGPU.h | 379 --- 3rdparty/SiftGPU/src/SiftGPU/SiftMatch.cpp | 687 ---- 3rdparty/SiftGPU/src/SiftGPU/SiftMatch.h | 93 - 3rdparty/SiftGPU/src/SiftGPU/SiftMatchCU.cpp | 176 -- 3rdparty/SiftGPU/src/SiftGPU/SiftMatchCU.h | 68 - 3rdparty/SiftGPU/src/SiftGPU/SiftPyramid.cpp | 406 --- 3rdparty/SiftGPU/src/SiftGPU/SiftPyramid.h | 190 -- 3rdparty/SiftGPU/src/TestWin/BasicTestWin.cpp | 322 -- 3rdparty/SiftGPU/src/TestWin/BasicTestWin.h | 97 - 3rdparty/SiftGPU/src/TestWin/CMakeLists.txt | 13 - 3rdparty/SiftGPU/src/TestWin/GLTestWnd.cpp | 305 -- 3rdparty/SiftGPU/src/TestWin/GLTestWnd.h | 61 - 3rdparty/SiftGPU/src/TestWin/GLTransform.h | 113 - .../SiftGPU/src/TestWin/MultiThreadSIFT.cpp | 258 -- 3rdparty/SiftGPU/src/TestWin/SimpleSIFT.cpp | 290 -- 3rdparty/SiftGPU/src/TestWin/TestWinGlut.cpp | 188 -- 3rdparty/SiftGPU/src/TestWin/TestWinGlut.h | 62 - 3rdparty/SiftGPU/src/TestWin/speed.cpp | 200 -- 92 files changed, 4 insertions(+), 29583 deletions(-) create mode 160000 3rdparty/SiftGPU delete mode 100644 3rdparty/SiftGPU/CMakeLists.txt delete mode 100644 3rdparty/SiftGPU/History.txt delete mode 100644 3rdparty/SiftGPU/OpenGL_and_CUDA.txt delete mode 100644 3rdparty/SiftGPU/README.txt delete mode 100644 3rdparty/SiftGPU/bin/DevIL.dll delete mode 100644 3rdparty/SiftGPU/bin/DevIL64.dll delete mode 100644 3rdparty/SiftGPU/bin/SiftGPU.dll delete mode 100644 3rdparty/SiftGPU/bin/SiftGPU64.dll delete mode 100644 3rdparty/SiftGPU/bin/SimpleSIFT.exe delete mode 100644 3rdparty/SiftGPU/bin/Speed.exe delete mode 100644 3rdparty/SiftGPU/bin/TestWin.exe delete mode 100644 3rdparty/SiftGPU/bin/glew32.dll delete mode 100644 3rdparty/SiftGPU/bin/glew64.dll delete mode 100644 3rdparty/SiftGPU/license.txt delete mode 100644 3rdparty/SiftGPU/makefile delete mode 100644 3rdparty/SiftGPU/msvc/ServerSiftGPU/SiftGPU_Server.dsp delete mode 100644 3rdparty/SiftGPU/msvc/ServerSiftGPU/SiftGPU_Server.vcxproj delete mode 100644 3rdparty/SiftGPU/msvc/SiftGPU.dsw delete mode 100644 3rdparty/SiftGPU/msvc/SiftGPU.sln delete mode 100644 3rdparty/SiftGPU/msvc/SiftGPU/SiftGPU.def delete mode 100644 3rdparty/SiftGPU/msvc/SiftGPU/SiftGPU.dsp delete mode 100644 3rdparty/SiftGPU/msvc/SiftGPU/SiftGPU.vcxproj delete mode 100644 3rdparty/SiftGPU/msvc/SiftGPU/SiftGPU_CUDA_Enabled.vcxproj delete mode 100644 3rdparty/SiftGPU/msvc/SiftGPU_CUDA_Enabled.sln delete mode 100644 3rdparty/SiftGPU/msvc/TestWin/MultiThreadSIFT.dsp delete mode 100644 3rdparty/SiftGPU/msvc/TestWin/MultiThreadSIFT.vcxproj delete mode 100644 3rdparty/SiftGPU/msvc/TestWin/SimpleSIFT.dsp delete mode 100644 3rdparty/SiftGPU/msvc/TestWin/SimpleSIFT.vcxproj delete mode 100644 3rdparty/SiftGPU/msvc/TestWin/Speed.dsp delete mode 100644 3rdparty/SiftGPU/msvc/TestWin/Speed.vcxproj delete mode 100644 3rdparty/SiftGPU/msvc/TestWin/TestBase.dsp delete mode 100644 3rdparty/SiftGPU/msvc/TestWin/TestBase.vcxproj delete mode 100644 3rdparty/SiftGPU/msvc/TestWin/TestWin.dsp delete mode 100644 3rdparty/SiftGPU/msvc/TestWin/TestWin.vcxproj delete mode 100644 3rdparty/SiftGPU/msvc/TestWin/TestWinGlut.dsp delete mode 100644 3rdparty/SiftGPU/msvc/TestWin/TestWinGlut.vcxproj delete mode 100644 3rdparty/SiftGPU/speed_and_accuracy.txt delete mode 100644 3rdparty/SiftGPU/src/CMakeLists.txt delete mode 100644 3rdparty/SiftGPU/src/ServerSiftGPU/ServerSiftGPU.cpp delete mode 100644 3rdparty/SiftGPU/src/ServerSiftGPU/ServerSiftGPU.h delete mode 100644 3rdparty/SiftGPU/src/ServerSiftGPU/server.cpp delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/CLTexImage.cpp delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/CLTexImage.h delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/CMakeLists.txt delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/CuTexImage.cpp delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/CuTexImage.h delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/FrameBufferObject.cpp delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/FrameBufferObject.h delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/GLTexImage.cpp delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/GLTexImage.h delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/GlobalUtil.cpp delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/GlobalUtil.h delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/LiteWindow.h delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/ProgramCG.cpp delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/ProgramCG.h delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/ProgramCL.cpp delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/ProgramCL.h delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/ProgramCU.cu delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/ProgramCU.h delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/ProgramGLSL.cpp delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/ProgramGLSL.h delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/ProgramGPU.cpp delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/ProgramGPU.h delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/PyramidCL.cpp delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/PyramidCL.h delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/PyramidCU.cpp delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/PyramidCU.h delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/PyramidGL.cpp delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/PyramidGL.h delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/ShaderMan.cpp delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/ShaderMan.h delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/SiftGPU.cpp delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/SiftGPU.h delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/SiftMatch.cpp delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/SiftMatch.h delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/SiftMatchCU.cpp delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/SiftMatchCU.h delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/SiftPyramid.cpp delete mode 100644 3rdparty/SiftGPU/src/SiftGPU/SiftPyramid.h delete mode 100644 3rdparty/SiftGPU/src/TestWin/BasicTestWin.cpp delete mode 100644 3rdparty/SiftGPU/src/TestWin/BasicTestWin.h delete mode 100644 3rdparty/SiftGPU/src/TestWin/CMakeLists.txt delete mode 100644 3rdparty/SiftGPU/src/TestWin/GLTestWnd.cpp delete mode 100644 3rdparty/SiftGPU/src/TestWin/GLTestWnd.h delete mode 100644 3rdparty/SiftGPU/src/TestWin/GLTransform.h delete mode 100644 3rdparty/SiftGPU/src/TestWin/MultiThreadSIFT.cpp delete mode 100644 3rdparty/SiftGPU/src/TestWin/SimpleSIFT.cpp delete mode 100644 3rdparty/SiftGPU/src/TestWin/TestWinGlut.cpp delete mode 100644 3rdparty/SiftGPU/src/TestWin/TestWinGlut.h delete mode 100644 3rdparty/SiftGPU/src/TestWin/speed.cpp diff --git a/.gitmodules b/.gitmodules index 0dfef3df..18fa539a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "3rdparty/pugixml"] path = 3rdparty/pugixml url = https://github.com/zeux/pugixml.git +[submodule "3rdparty/SiftGPU"] + path = 3rdparty/SiftGPU + url = https://github.com/pitzer/SiftGPU.git diff --git a/3rdparty/SiftGPU b/3rdparty/SiftGPU new file mode 160000 index 00000000..b46bd5b8 --- /dev/null +++ b/3rdparty/SiftGPU @@ -0,0 +1 @@ +Subproject commit b46bd5b8cc5cfdc1dc163444b72c705569800b6d diff --git a/3rdparty/SiftGPU/CMakeLists.txt b/3rdparty/SiftGPU/CMakeLists.txt deleted file mode 100644 index 10bab6ff..00000000 --- a/3rdparty/SiftGPU/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -PROJECT(OD_SIFTGPU C CXX) - -SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-write-strings -Wno-unused-result -Wno-deprecated -fPIC") -SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-write-strings -Wno-unused-result -Wno-deprecated -fPIC") - -ADD_SUBDIRECTORY(src) - diff --git a/3rdparty/SiftGPU/History.txt b/3rdparty/SiftGPU/History.txt deleted file mode 100644 index 1f8e9154..00000000 --- a/3rdparty/SiftGPU/History.txt +++ /dev/null @@ -1,190 +0,0 @@ -0.5.400 - Fixes some platform issues to make SiftGPU work with Intel integrated GPUs. - Tested on window 7 and Ubuntu. - Note that Linux+Intel card works only on Mesa 9 for now. - Replaced the deprecated ostrstream by ostringstream - Fixed a few memory leaks (delete[]/delete mistakes); -0.5.382 - Fixed a bug with CUDA-based SiftMatchGPU (can match up to only 4096) - * The maximu texture dimension is not updated from hardware since V370 -0.5.381 - Really fixed the bug in CUDA-implementation of Guided SIFT matching... - -0.5.380 - Fixed a bug in CUDA-implementation of Guided SIFT matching. (Thanks to Oliver Whyte) - Added auto-downsampling to try fit the GPU memory cap. - Fixed minor GLSL code issue on Intel Integrated Cards.. not fully functional on Intel - Fixed bug with a rarely used parameter -tc for limiting feature counts - Updated Devil 64 bit lib to let it search for Devil64.dll (to keep both in the same folder) - Fixed an issue on the new nVidia driver with one GLSL shader. - Fixed a glReadPixels issue on ATI when the feature count is too big. - Modified the descriptor code to make it work correctly for ATI - Finally made GPU selection for Windows working (e.g. -display \\.\DISPLAY4) - MSVC Only: Converted the visual studio solution to v2010 -0.5.370 - Automatic switch from OpenGL to CUDA when OpenGL is not supported (Useful for X) - Dropped indirect data transfer path CPU->GL->CUDA - (CUDA part not requiring OpenGL at all, but restrictive on input type) - New parameter -mind (mininum working dimension) for performance tweak - (By default it is 16; Gaussian octaves smaller than this will be skipped) - (Small dimensions are ineffecient for GPU because many processes are on idle) - Added a function to compute SIFT for any rectangle (not only square!) - (Email me if you are interested in using it) - - Dropped all CG implementation to simplify maintance - Added a partial OpenCL implementation (half speed of CUDA/GLSL) - Added macro SIFTGPU_NO_DEVIL to allow droping DevIL depencency - Fixed minor bug in makefile - Removed reference to MAX_PATH in SiftGPU.h. It is dangerrous..since they can be redefined. - -0.5.360 - Added demo MultiThreadSIFT to demonstrate using Multi-GPU with Multi-Threading. - Improved CUDA performance on frequent image size changes - Skipped unacessary gradient/dog computation when using keypoint list on a same image - Added x64 projects to VisualStudio solution - Provided three different ways to limit the number of feautres -tc1 -tc2 -tc3 - (to keep either the highest levels or the lowest levels) - Fixed a bug with -tc introduced in V360Beta (2/26/10) - Fixed a bug in CUDA-implementation when the first several octaves are skipped (2/28/10) - Let RunSIFT return 0 when errors are found in CUDA (previously ignored) - Added option -display to select GPU according to Display - Modified option -cuda [device_index] to select GPU device. - Added Wrapper ServerSiftGPU to allow run multiple SiftGPU on multiple GPUs - Added option -tc to set a soft limit for the number of returned features. - Changed OpenGL context creation. The core library no longer requires GLUT. - Removed calles to std in file ProgramCU.cu to avoid possible compiling errors. - Reorganized the file structure of the code package. - Fixed some linux makefile issue and linux compilation issue. - Fixed linux name mangling problem for dynamic library loading. -0.5.345 - New Linux makefile. CG, CUDA and sse parameters can now be changed easily in makefile. - CG-based SiftGPU is now disabled by default to reduce dependencies. - Made more parameters changable after initizlization (check manual for details) - Changed the way of handling out-of-boundary user-specified keypoints. - Changed the timing function from clock to gettimeofday for Linux (Thanks to Pilet) - Fixed a bug in saving binary format(Thanks to Dekker) - Fixed linux makefile for CUDA-SIFTGPU (Thanks to Planna) - Fixed a conversion bug for 64-bit system introduced in V340 (Thanks to Plana and Wang) -0.5.340 - Added (-glsl -pack -m -s) to default setting. (You can change back to CG by -cg) - Updated libraries (CG 2.1, GLEW 1.5, DEVIL 1.77) - Used SSE to speed up descriptor normalization - Improved speed of the OpenGL-based SiftMatchGPU. (1.5x) - Added GLSL and CUDA implementation of SiftMatchGPU - Added the packed glsl implementation. It might be slightly faster than cg. - Added a CUDA-based SiftGPU implementation(use -cuda to turn it on) - Cleaned up the GLSL code to follow the GLSL standard more strictly. - Tested many of the GLSL shaders in GPU ShaderAnalyzer 1.5. - Added option -fastmath to specify -fastmath to cg compiler(yet no big difference). - Increased the threshold to fix the bug in guided matching when F is NULL - Fixed a bug in orientation for -m2p (missing a ";" in shader code) - Changed interface to handle all OpenGL pixel data (Previous only float and unsigned char). - Kept only the fastest verion of descriptor generation code, and dropped others. -0.5.320 - Fixed a bug (Wrong texture size may be assigned when image size changes) - Fixed a bug (Descriptor storage size may be not updated when image size changes) -0.5.319 - Fixed a bug (Setkeypoint before specifying image was not working in previous versoins) -0.5.318 - Changed interface to process keypoints WITHOUT known orientations - Added interface to specifiy float image data - Overloaded new operator of SiftGPU and SiftMatchGPU to fix a possible heap corruption on deallocation -0.5.317 - Fixed a bug in processing user-specified keypoints - Updated the .def file for the released package -0.5.316 - Fixed a bug of insufficient buffer allocation in very rare cases. (Thanks to Zheng) - Added guided SIFT putative matching using homography or/and fundamental matrix - Added function to change the feature number limitation for sift matching -0.5.315 - Added a cg-based sift matching implementation (Thanks to Zach)(see SimpleSIFT.cpp for example). - Added optional output of the extremum type (maximum or minimum) of feature - Added function to compute descriptors for user-specified keypoints - Included xcode project and makefile(Thanks to Perfanov and Wittenhagen) -0.5.313 - Finished the GLSL implementation of SIFT (use -glsl to turn it on). - Fixed a bug of crashing after many iterations on newer graphic card like GTX 280. (Thanks to Zheng) -0.5.312 - Fixed a bug introduced in V311(One texture size not updated for image size chaning with -pack) - Fixed a bug introduced in V311(feature readback function is empty for -pack). (Thanks to Palomo) -0.5.311 - Fixed a bug in descriptor computation (it may cause error in descriptors of 10% of features) - Implemented a packed SIFT implementation (use -pack to use it. 3x pyramid construction speed) - Used GPU/CPU mixed list generation (2X compared with old method) - Used only CPU for multi-orientation list generation (much faster than GPU) - Changed parameter to avoid using dyamic array indexing by default. - Implemented a new descriptor computation method(30% faster than the old one - Evaluated the speed on GTX 280. (obtained 1.5x the speed of 8800 GTX) - Dropped many unreferenced functions and also some old shaders. - Changed some definition for better compiliation on Mac (Thanks to Wittenhagen) - Changed SaveSIFT function to keep a little bit less fractional digits - Combined the horizontal gradient and vertical gradient visualization to one. -0.5.302 - Updated cg, glew and glut libraries - Added speed evaluation code - Added some debug code to write out floating point tiff images - Refactorized code a little bit, now it is having much less warnings -0.5.293 - Changed gaussian weighting factor in orientation computation, now closer to Lowe's - Minor bug fix on texture reallocation for too many features - Added new feature to automatically down-sample images that are larger than user-defined size. -0.5.288 - Added comparision with Lowe's SIFT on box.pgm(check /doc/evaluation for results) - Fixed two bugs related to feature scale - Fixed a bug related to up-sampling - Added a missing dog threshold test after subpixel localization. - Added new parameter for fixing the feature orientations - Added export function to check how SiftGPU is supported by current OpenGL context - Changed some default parameter ( now it downloades result, and use verbose level 2 by default) -0.5.280 - Added an example of dyanmic loading of siftgpu library - Fixed a serious bug of GetFeatureVector (orientation and scale are misordered previously) - Fixed a bug in def file - Fixed a bug in calling RunSIFT with pixel data. - Fixed some GLSL shader bugs -0.5.276 - Removed file "gs_types.h" and put the export definition in "siftgpu.h" - Added high resolution timing for windows by using funciton timeGetTime - Fixed some problems on arbfp1. Now it can run a limited demo again. - Added parameter for changing the maximum allowed feature number of a level -0.5.267 - Corrected a neglected stricmp to _stricmp for linux compiliation - Fixed a bug in function ResizePyramid which happens when image size changes in some way - Added key input function to change verbose level for GUI mode - -0.5.265 - Added display of fps information in console for sequential processing - Added an optional step that converts RGB to Luminance before uploading to GPU - Added a parameter "-p WxH" to specify the size for initializing the pyramids -0.5.261 - Added a new feature for using existing memories for processing smaller images - Added new functions to let user control the allocation pyramid momory - Fixed a bug with sub-pixel localization - -0.5.256 - Added linux makefile - Added a new feature for allocating seperate processing memories for multiple images. -0.5.250 - New keypoint detection code capable of sub-pixel/sub-scale localization - Changed code for easy Linux porting. Thank Martin Schneider for his help on this - Changed some default values of the parameters. - Improved SIFT visualization. -0.5.236 - Fixed an important bug in orientation computation - Successfully tested on many image matching experiments - Changed the command line -m to default 2 orientations -0.5.232 - Fixed a sift output bug - Fixed a NaN bug in descriptor generation - Fixed a bug that some images are flipped -0.5.224 - Fixed a memory leak bug on FBO - Added one more demo for processing 640*480 image sequence - Smalled change to interface, so that image data can be specified like OpenGL textures -0.5.220 - Added more examples to manual. - Added more comments about the siftgpu interface - Added one more input interface - Fixed bug of crash on image size change -0.5.208 - First release \ No newline at end of file diff --git a/3rdparty/SiftGPU/OpenGL_and_CUDA.txt b/3rdparty/SiftGPU/OpenGL_and_CUDA.txt deleted file mode 100644 index 6f6307ea..00000000 --- a/3rdparty/SiftGPU/OpenGL_and_CUDA.txt +++ /dev/null @@ -1,79 +0,0 @@ -PART 1, OPENGL, - -Pay special attention if you have your own OpenGL code. -The OpenGL-based implmentations of SiftGPU need valid OpenGL context to run properly. - -1. If you use the same OpenGL context for SiftGPU and your own your visualization - Make sure the OpenGL states are restored before calling SiftGPU. SiftGPU changes several - OpenGL internal states, including texture binding to GL_TEXTURE_RECTANGLE_ARB and current - ViewPort. You might need to restore them for your own OpenGL part. To avoid this problem, - you can create a seperate GL context, and activate different context for different part. - - Note that GL_TEXTURE_RECTANGLE_ARB is always enabled in SiftGPU. When you have problem - displaying textures, you can try first glDisable(GL_TEXTURE_RECTANGLE_ARB) before painting, - but don't forget to call glEnable(GL_TEXTURE_RECTANGLE_ARB) after. (Thanks to Pilet) - -2. How to create/setup an OpenGL context for SiftGPU - - If you choose to let SiftGPU to manage OpenGL context, you can simply do that by - SiftGPU::CreateContextGL and SiftMatchGPU::CreateContextGL. When you mix your own OpenGL - code with SiftGPU, you need to re-call CreateContextGL before calling SiftGPU functions, - which will implicitly activate the internal OpenGL context. - - - If you choose to create openGL contexts yourself when mixing SiftGPU with other openGL - code, don't call SiftGPU::CreateContextGL or SiftMatchGPU::CreateContextGL; Instead you - should first activate your OpenGL context (WglMakeCurrent in win32), and set GL_FILL - for polygon mode, then call SiftGPU::VerifyContextGL or SiftMatchGPU::VerifyContextGL - for initialization. You should also setup in the same way before calling SiftGPU functions. - - -PART 2, CUDA ---------------------------------------------------------------------------------- -1. How to enable CUDA - -The CUDA implementation in the package is not compiled by default. - -To enable it for visual stuio 2010, use msvc/SiftGPU_CUDA_Enabled.sln -To enable it for other OS, you need to change siftgpu_enable_cuda to 1 in the makefile - - ---------------------------------------------------------------------------------- -2. Change CUDA build parameters. -For windows, you need to change the settings in the custom build command line of -ProgramCU.cu. For example, add -use_fast_match for using fast match. - -For Other OS, you need to change the makefile. The top part of the makefile is -the configuration section, which includes: - siftgpu_enable_cuda = 0 (Set 1 to enable CUDA-based SiftGPU) - CUDA_INSTALL_PATH = /usr/local/cuda (Where to find CUDA) - siftgpu_cuda_options = -arch sm_10 (Additional CUDA Compiling options) - - ------------------------------------------------------------------------------------- -3. CUDA runtime parameters for SiftGPU::ParseParam -First, you need to specify "-cuda" to use CUDA-based SiftGPU. More parameters can -be chagned at runtime in CUDA-based SiftGPU than in OpenGL-based version. Check out -the manual for details. - -NEW. You can choose GPU for CUDA computation by using "-cuda [device_index=0]" - -One parameter for CUDA is "-di", which controls whether dynamic indexing is used -in descriptor generations. It is turned off by default. My experiments on 8800 -GTX show that unrolled loop of 8 if-assigns are faster than dynamic indexing, but -it might be different on other GPUs. - - --------------------------------------------------------------------------------------- -4. Speed of CUDA-based SiftGPU -If the size of the first octave (multiply the original size by 2 if upsample is used) -is less than or around 1024x768, CUDA version will be faster than OpenGL versions, -otherwise the OpenGL versions are still faster. - -************************************************************************************** -This is observed on nVidia 8800 GTX, it might be different on other GPUs. Recent -experiments on GTX280 show that CUDA version is not as fast as OpenGL version. - -Note: the thread block settings are currently tuned on GPU nVidia GTX 8800, - which may not be optimized for other GPUs. -************************************************************************************** diff --git a/3rdparty/SiftGPU/README.txt b/3rdparty/SiftGPU/README.txt deleted file mode 100644 index f84381e5..00000000 --- a/3rdparty/SiftGPU/README.txt +++ /dev/null @@ -1,58 +0,0 @@ -A GPU implementation of David Lowe's Scale Invariant Feature Transform - -Changchang wu - -http://cs.unc.edu/~ccwu - -University of North Carolina at Chapel Hill - - - - -1. SIFT - - SIFTGPU is an implementation of SIFT for GPU. SiftGPU uses GPU to process pixels and features - parallely in Gaussian pyramid construction, DoG keypoint detection and descriptor generation - for SIFT. Compact feature list is efficiently build through a GPU/CPU mixed reduction. - - SIFTGPU is inspired by Andrea Vedaldi's sift++ and Sudipta N Sinha et al's GPU-SIFT. Many - parameters of sift++ ( for example, number of octaves,number of DOG levels, edge threshold, - etc) are available in SiftGPU. - - - SIFTGPU also includes a GPU exhaustive/guided sift matcher SiftMatchGPU. It basically multiplies - the descriptor matrix on GPU and find closest feature matches on GPU. GLSL/CUDA/CG implementations - are all provided. - - NEW: The latest SIFTGPU also enables you to use Multi-GPUs and GPUS on different computers. - Check doc/manual.pdf for more information. You can modify some marcros definition in - SimpleSIFT.cpp and speed.cpp to enable the testing of the new functions. - - -2. Requirements - - The default implemntation uses GLSL, and it requires a GPU that has large memory and supports - dynamic branching. For nVidia graphic cards, you can optionally use CG(require fp40) or - CUDA implementation. You can try different implementations and to find out the fastest one - for different image sizes and parameters. - - The GLSL version may not work on ATI now. They did compile sucessfully with ATI Catalyst 8.9, - but not any more with 9.x versions. - - SiftGPU uses DevIl Image library, GLEW and GLUT. You'll need to make sure your system has - all the dependening libraries. SiftGPU should be able to run on any operation system that supports - the above libraries - - For windows system visual studio solution are provided as msvc/SiftGPU.dsw, msvc/SiftGPU.sln and - msvc/SiftGPU_CUDA_Enabled.sln. Linux/Mac makefile is in folder Linux of the package. - - -3. Helps - - Use -help to get parameter information. Check /doc/manual.pdf for samples and explanations. - In the vc workspace, there is a project called SimpleSIF that gives an example of simple - SiftGPU usage. There are more examples of different ways of using SiftGPU in manual.pdf - - - Check /doc/manual.pdf for help on the viewer. - diff --git a/3rdparty/SiftGPU/bin/DevIL.dll b/3rdparty/SiftGPU/bin/DevIL.dll deleted file mode 100644 index 7cc8553d96cc8660e14e4507a465af9800e704c7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 670720 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�P)$qz(ez)NO~bvs!Bxh8?R44ZA(qjQfRb|96Hkv?a+UIfDKh_d?AH#|kDkT+_% zo63A{W^uZ`ew!QAksQ9fK*q9*)DmsM_8UQ7^!e}HZjnrH`h_o&Tb$yBE~eH-QI;fF zXva;#%nW_53=UYEo8}-k-Br;Y1uy!~%J&_Pt$_#>F#72G+D~)hv%XupT2_|bGHD~W za>j%u>V?ca9|t+!{SJAx1*6sBn>t-vM4KhUTNJ>-_s2o@1wjC))K(yDBFF2FRC-WR zydg}tW-Z3>$NVupDQpJjY40mQ(ni#}Z?6i_MjT+sVb`lIjWCnjZqmJ13@UbY**!n{ zz>nij;ojran@#0s3(jV?H^h7?Db0W8A~RaD`TRUO`xx>}^>UZ)-z$YEzY}(zC#<<` z#p;0GJOGv9=jX#a=R`_IT+2t@-UiOfbmorT6`gnj7{EbK)~n6Ra@12zzVe)THx{{S z7Q%h{*p(`&t@ELnu8`x|d|aR@K5Y^Ql)w@bKzpsjV{K8$!bR47Umq&Zh&zQH(_M^aRX!@>;7 z_A>SWik&d50;$owym3Sbsc~hO-@8JdkB%Is=ndQ0eWv$4)bXM*QnsBI z#k;MzbzLvUN(0*%5%wul980$+X6U9Fw~O=?){`7UWmcprm&2p{)p@N<&F$=vAIIL4-6k4m#mWF>bwPtcS47Z6>cTLPA?pu#2J6o zH}j9z($8!?t*nLwWyo#~L`30(;&E+-Wf+;D_FV^k*CK9MVk;)U+^bYwq}gE{4*{7g zR<2eAfIz~z_7@=n1BnzLFSiH&iZJ66kKB9u5wsvLz2-HRPGGVp7ITNmVQvIV(}kMX zJ;+C;{i&7D0Uh_Ln<>ow*%Q2!A*#r%LF=!H$i9CYyJfhM(I(Q&v3(---F;`lpCZj{ zTXgD;0C;Ef+kriN`f6r72MKTDwVEIdvDBU6pD%gADGu+4-*R4CybJlI1)Jh!=Dw}( zxgw`(N`yHHcqM;B;ZyIw2OlEM&Qtx)N?-i7V%SsXF=`$10)qnc9y+8EHJ}xg5PG^D zx(I=~HM=L8z8S)NgQGpV(!Fd;dw%?jbmTx}?9QT%9_uCuia_^!i(gu@xB&b<)?GyB z0026+Gf8#^!tl=Ga@aiQQP`~D0S-$RjBp6XP#G~eSNFbOQD$2|3W4p#xrp6GARc&1 zPf6`|=Mi-)TinZHiu(@zYQOmqm+2mqty9~3Gj90p+AY2sqnJhhx=4k>{wLraDl*^3 zb1383wL)t7@5I9;qmB4PwnWa25i*h}IlC{}_5F6|NQu zm#Pju#lKE!^+s*(=39mbsoVX70kNC9E{|NqIPo4wQ=HdM~qhF|A>x;{A*`NZSGNFD&E^O*)2Vzi{N&{BwlO-x|+;_$9l_URE zyO~yMXRCQp{^5t{*1EK9zQHSa4~ZqNxF~3PZ46F>c52mTsu6XZ+N=4pW{NZp zX__^Pi^rsa96K~1Syyvh#aKk(t1J`fwmjXVn^P!Lv`cCg0A2yR)kDtZJOQBI8rua3 z;y@-dL>kDLKU=!s#E1pKB|+$I8*1dfdlDb1D)JMJLX5QsT83H(9t@<@6YZv2hvlo5 z8FKkew_H}NkvChTz%sR|`XQ@W`qhS`(V^MK_^Wy<9^|stHP1-4u%h-TNo??*l$NsT z2LM*C*QZzE({aV3PWa4QiJZtf?CIQal=ZrZ=XiNdx$7Iem&eJ{B2TB_mf}Q76I;wr zoBhqj3?9UW0Y4{tL*T)RTnK+|8i2DK9An7NSQoDWeS7CVD-Yf%kd*F*%pKY2*WiCW zfrh$-`2Do2H|h>;Hb*h)cSXou(jn!|Z@a;@Z~?FF0?ry7vjk1bdQ!TH)pwt5Unxem zY}bU9pGQ@1ol-&NQ;z@+fQN%r<_nB7p&|`_u7~+-P}OtDt|kkKPpkM7+$~J^;+#Ij ztbe8kjF_Q%%V(#X!^QG_QHc0mhun8J4ZY&sqYi1HE`{c2kBGl;1?3VVfLD?S@9kCu36p!Iudm7Sex zj)kC>uw01l)^CbE2)=8E9liDM^}YgK3wlYFFq?9->sRlW-}3h~j|k&yPFfs9ff@M8 z*yRqk{FE!HnYn;nDoAQG16{u#aeL9ga`9c#_RpuU4<0lBf=5@c{nRWw$Bh8O@VLml zTlFc<>uv=XN|e_pvH}@ri7UKz?zC+rN-VH7(6`4oV&3awi=QNn@z}Xe+Z83_wk*2o zSvjP3{H>MIqGx8P9wD?O}Q2zb6J0f)5UYD5+3M%myxK^Z|zN`8Mc@`mXM!ao>}Yj(+A z<7d2t(%o3R&PFKS3rhA+tV@dkl2zCX-}2Ldy%tKmg0+|v0G89sXA{jXH&LP}q??Qe zjMKC$Av;%WP_iLL)H)Cxe0a_2c<^V9n*|7>87SAKk1{y$lG%ya&0Drb@f3$L##{Sg z|I~}4W);rJ&N@i~E}5QyDN!siZu)yF$Em8`s8%(hgw4Jm&$+}PVw2>edlu>{Co>FXkjgvblNa2rxDRlH1tZJut1Q; z@~_uK#$nepxl1QAOmB8p3Edkl=vUs+hdH)2M*nxF%{9@UM#c9D4qzN2MC!i-!f;OEH~6Qce|TSeO@CMA&S#RL2B2(mN@AD5>(IlL^s) zNy^i_`?Gb4GVaVDUUAS+KC%P^9125}gudxnD<)c~{XC)BNk6yQIA&&;7L*nkaV zd>uXwbQnMKPh5LdTi+fQ>zTjRr>q|K!jM8fs=+-$FD=pO71 z(lsSR6~mbP;&wgwEzfMjA}#l9^5Sf2-9z6ucbIgNH#{rXP!zxF_Q0tgCUO|&QTN29 zaN1>gsvcec0{138gg?AN?N94K)>9P&CI&vl(mo(fl_{jj7DEW_N>}q4HT!?yI|F+( z#7~96KapzAPGn5#``OO>^VQc@wZVIu#GD^?ni^0O@KAf6dt?&t*7>xN#H=~p0s~K@ zIk(D#od*AJt`%RTXcnSF-!#V3g190&T$%CE$yyGfc@G7mWDs=c8D##XY!*Yb*WZHV!yY)z8mE)2{#A#kDt zR-I+2R}>}Lr1~}GqF=EM65`pDB`9y9KbR$PI^SdF*jk{CRMCI=IsQEdDmLV&PzZzZ zCH6f(S_qgi2Iw{bYF3DuH)CLwk|Vit*k(D=sHk7gB2Vxw3z9xJH~!}3h*yLc$+JOa zNc-8CWSxh%ABuC}v=Deeil?b(nCH(Gpc35ERUNIr&n+g|;)FS4&MO_)QgBWRb6g7M z?jF(1d5EH?wz$+QtVaQugYXKSh`*Sj(h>O)NCymydHGE=>G;jSOH}P3>$Gf zSh%%>n=RNqfYn`6Tz>`PT+6nW zJGftvpGv3Y=+$Recw@3fkwaig};}hy?4{&j`I-|w2mV0Pvc|e!jhdK@uROra7 z(-4;e8+VI!2QHTWg$IS!Zmv?E=&mo?Z3DdtEW)en>pmEU#~-5(b?y(~$U$D(D^7bD zMY5Ph5S{H;bjOkm|3}fehcn^*aeOzkF$}{n409hwOOwSg_hIfrE>RJMB2&ZW&fLo- z$)!|Cp@d4zos?Wkz9*dJ=KlIyYb4U4QYvrKPv}IigqL^$7H3)Ym<#V4#zjyBCXcR;rW;-s=YQ&2PT1#kT+^Kip*Z z`FvnI13_+*6h1Qr#SfQADo946vl_KLIBEb6&4Koo@-;1|ftUo*Z6?C~eL8w40m>rl z{(<~_05X?p@UqR^h&e9{d1jQ0c#{6DMQ8w`yZv6B52Uu3jN!Aw20uKQy!2!S0C1A+OmDE(kk>~EJ>M$r&paT>4>K#$qH=9 zTjRZ#JFr8BLBDaGq7QOjjf<*8!ofu&@}UEnLz$()3(6F9V2_d7>LDu zFKd~gYRdobk?xSsl(deFW^B9y|76l|TSXHewLFs8(got_RqxiuoJ=Amcz3qDKnWN6 zTVGnCAJ@O?M_$1?ARP}x_@B*stByupS6?7vtdHM>9DOASjfNU`L}6!bm#rWDrGxtG zi(85w%i^BS-EDeTaosIGEISv1jITR9IzTe9292jd5we~ZVaeq`aNM>vN%2DrzeJ zL`aX~BBc>g>T~Pwieg9GKIHIZR1gdHrCanpjCZf<%LSRd>i^koDPZ!0_ zAUxi^f{q2{pgwJ#ARYAUFWe23c@rwN0Q`p%YWR}`X$1y7(=AvzrRV#x6k(e(XatI$Ugv?{`TsQUL&4dz^}U*cO&o}5J>8))B{8E+Y(cWCEKI~<5?K$^jhLe_TNjN zSL-LRGM!c&BF9_GM*2cq0&OsgKBahQmov7&=!l7a2fT^645R@o(Mjz)mkORF^Yldq zAeRBprU#+#nQ}m@xG(MbE$!-8&>2?@t|W_$8+?djI?Tlb_*km^$B6vs}FDUK4W?m;uYQKIKFyyVAJ|whZQw^4)G%yHH^y=s)MK0z0SqM{W=wphTeh326Z>AgTMLj56 zKxm*z1_5}#m3s=SPC>mb35TH?zF6~k~kHiMNvztS2^K7;Q znO;O0%u1GRXMGiqh`wg~E1;()Qe=B$x0hWj@{L|yy>9P)Rcl!#1Uz-l=i<}EiQWO#t?zcp(%aCdLp0icvIHB+Uj44%@O6q zdx;f3^+@~9P--zgighO&2JxiF_srPn1}ls5{zrKsAP0kUKHwXM$YYvjTg8phy-Cj! zqL2wxeW8bHtB%%AMt!pZzp=k%uJ7f5tlE`VJ14VKh=hG<5Zk1^-$V(f$GP%o=`GIV z4SEpqKGS_ddJdPeb+%P;>|_}UN{{7Nx=PHNH?=xX(MUBm@qh|=D=7jrow`fL#_h4{ z@2pe2oEnimf2zbovVw8;r#+AtW#5jb2|J?1?g1;_O?iL!_DP_6SXa~mNxg{hQAsQ6 z6IejTo_YtO=g~MN5L;C^rUMzcecd-INwCNL-V4B(wXi`VvDuLUR{4v5=3(ueenLRf zPc>x*Yi`#LNpEVZig?O zQIkM6vs)b^H5#3CA}^Dpg8^0LIoVBM`f{zfJghwEvXv3aK{K^$&ykVVYG16S8{&@O zSUJdd-u~Q&jSq0|S8yDyXf}#J;P&Q078Wy_3Xrgp8txvUcX!%6%(wZ`8_1k^Ow4|A zY{OmL%Au1mciV& zUBI?2$qT6s{+_7Fe51^|!)^#_66x3Ix=~RqjY+`g$#rXJ15^=5nf~y^mmk(#lhyY$ zMqu&u%jozwoNx*_oYtuCV?E?tK_v|Sj_-A8ome7@|7YaRhI1MJzG@P#5$TriSq9SydYY4sAn%BmyZ_4 zI681mL&GZGB0Br~{PI4ZG(=hFe(L_6==I9EbN)ZBO46aR$CIKFnLl1(6ID#CvJGw| zj9*aB=al#bImFawI zj@n*xz7tm3^prmyUzx%;l-v7%n`rylzhPwQ@cfXz6rY6?!Fbs#UG4q6gGuSzmr@72 z!)sHi9?3&!4;h7+C}C+k*6EripJgx$jUJf!q`C!*CfQ~)?|Wv|MDmdZf|CWc-_*D+ z@T+5_R2=S)vC9c~FfzYxmBP$a(eKi#k*A{E?%waYm|4V#bzD$I9zz%gid^9udWueZ zq*q5v7JU5|o`i(;M=T!_9r%}P=r61_#9CG~jH5^;3-fJ1r$kF^Y$zr-wgD|D$ur)l zlQ-;PFB;cGBklz=@FB;%-RuJwT!Jt;CLlpZVC-gfB}D3lVU&9#-p zaYS&GYkZ}52fS<#piilfltD-^pP6)=J1A^oGWN`eM*Scvwoi+NFQv8l%se=$cG$d>=Bh*3#DdFl4J+Q=iU z{Jv=xchG^uwjF(oIH@N>k%6|!BfjNTwryqGd2)dqUgUOg+tZ%VrkT}l>=D0N>&=hP z#7mbX+WzyJa`^G8dBf0Le^y9`YcjjCM$^X)^43&d;mk)D*9~y5n$Iu&;hET^E?)h% zxl|ftUB0rlvM!yf41dK&01d019Yx1i+`TFgi0tRTo~R$5Zjnfo-*_*4diRCrc4&gH zv?)X|28}nVdkhW zQ~a#B*21;0vANzr7YfQe)F}V`mkdy~`e9OK4bm>;& zQVOiSGyE*Kkt<;_Tw7!whtQapj`)fHO&5#r1FtFTLPTcti9o{rHB0)%Y}XD~ovz4ZgB!B;+4jz^XL42AP z-c;;Yn9;F^Q&rG*D&4s;bQ^`^6JNy3oU!Th+c;D>c8V*egjb0`(M*NZ_0-+aVg9-r zuj4;c1bF?dABa|ffU}*lc5NO@?7Bl*_u!b*cuo3^wKsd+ zc3$(FjCl=sE^eG!FX>k5={mOcUV>;eh7%KmN)XFy1;oT32nd0_H(bn^e7Mm&a{H$t zcv}aYl!s?RkITCFL7->&rSwice|_YP6+YO-<)f*J{6luW^Ku76uU#Ary9&AcA$nE$ zJ-DC!^^`-a#)`FhsyKvmhEI%4v);b^tnFp!+w(5wgy;0_E5lE3UDCq)?A#z*t<8E6 zc8Pmdp*oL8NJuyG#XPUG>J*?h_^z<5Kqj}}h4On!PHyNL%!RtOV~zVc{n2Na_K)Vi zM^N-TPlr8>aWx1_bn=k!^t>^{J1CwlP-c3ZwwQcN&mBu1+2h79Mcvay!Kr0Yn1dE%L1uTt)%Wz=C|c_x`;Y z;AcZqO4DJ~n6z#g?)X<)r&yW#Pvid}kEbMyY4fypTyA}0l-~GBZNNC>;_S1_*5@hA zaduAACjkpWK|cuXY&$sgcN4YpQ7ykhk+71{s&J8ZPdNP*J@-slF#VlYPk!=eDG^PH zm$EMW)r4iWkCXnVrxjev;pbS(Qb<@RZ2P-!x@d5o$vEAQq6NCwihYJ_)*sF6QNM{- zzBkIJD69J7W$G6ytjt8CpI?L{>ao{sY&}FWk1|DDz2gH;88idILz52VEAZZEg_W&n zssJD6tjUMc0=16>H?k=ud^pU#x8834-=||CPJgdd9((T0CwX5W1pF%7GU*I3FNquS zs+M3Vh4cKtcGr|@x_8WArC#e;ztL1%Zr&5-RB933NC@pZkP?lgk(gU1ko zu3ok^KNQ;kDj;gYmiXFrecq7H%z6LfOJj-- ztJG3~Im$iIddaWoXolO{foL^OlVY}`Va{h8$W27Q!AbLlKS>cSr~!w!j5va)fQv{e z%xLkPB;N~c(}H1d;;$|r*{8KW?$pPYmMYejh+j(s2T;Zk0lG6XRrCU7S`@Y{Z=h0)%pr{h5XWk@|T>1rS7GhL7+mx*W`Q1r1To)d$MoOzT~ zaI6lf(k>yUG76QXQGeP0I7Sv>-56$cX4w zd%}xz7Ho^9lM-0%vO=CGKQ4^e`Z08kK8(iRRX(oE{I+0fpOZsx@{mTh*YnL^km&^k zzvXNdWSjTIts2Y7T*Q!-AE4FAsq3By*0MiW!k_eW&U6LbfYK_mEB^-slbT6$Eh9A&4vV2-7Tc+*N%k2) z^(xz2M;^ZsV;av%5{H^Ux)3*VYBIb@a-R;9DCe+k zv7tZwAUvSk<@@O@zKF?lr-N|&&<3^f^9+igp?fT)LN2H^^JB+fr1j%iCY0K%8-`pk)x+QJs}S%TgI$hyar-ROpGC4JORx)&+(baGWO*lSwd$qosJKTOm7kNUq4oY;agx6~ogc&zPBPTs4e;{N zAAG0|S`sUAbM;Q)xkATodq%o@!s6#sB~vXml4lP(FMS9YJ7jDb1d}!>Y|>OHPi77P zx@L)h2MsC<>2sTw@rJNwY?iyY9@kkZH6clH-eZ)8-UIifA5yw9Nb+ztISs0MwfcMA zuil%`IPLew;D-8i^Ig{&I0CqSiK-l+d(&DJhka>acGAhQvTxef)BelOwSIjb@G98m zNooqARFWbH^NRZC!4LWhIQpaNE8k;w|Hy#(G5{K5g<UMUzHqgETsWg}VElc=D&*ZkULCR= zdQxz!`vp>(^QywsMQl?Y{O0X1-a|dlnhzFlumoE#d0+qeeONS=(J`mRGh(m{(@poH zZ!}q{z_iN$5epnf0pYgZS$_E$H{ZOB>0CM3z3A$!?G9-He5N~@&ZModoDbcg92s3l zQp&$Lc_c@{H#ABdh2Is5&E>nfFy7v`!*Sxz?Sa4J15BNbP26Z~9Rf7#WEiH^7-!-7 z##RY2kY-kS;0fiE;OVh{E`Jn3fo7G>){y$n*o?|-q~O&;`}qM= zgj@4Fc&xCqFA^oqB{gCf2P*254jk^Vm+}4}8r*r}Shde+(g_}1%g|E9%0H!|GnT(h zy2G-b-iU#kQLo&yixc~k|JN!J+`ke}pjfbVocK#k9# z;y@G|{g?|QxZeJU-puI+`KU8FZIxF}XZU%VpRluv0PSy#A@W;?x*HOHPyf`woP_(f z1q|F8#895WX{<`HabgYQaiiAr&_c(&E~ab7X9t6>pkSq`pK;kCHtJn=WDeBcTVBR_ z@N$OS7^2$v@|hyim)e<;lTCSWuo-wscrBW13Tt{1k_1#Aaw-V%8dr|0N7JYp?R8T} zvXc#1R>a`6OU-|vXJ=BzYwgWwD}olw-GC#G#do>`ns$ACJ&rc49ec7T=&7IxlGloR zyAmhi=Nk;na|yHeHJarWYYQF)!l~eQGH}02(L3rRWNh{onT`+;^Z?c^N6X>%gMa-{ zAzP9B0nV&p^k>7o;XUzeUGUdq23_ZyB*Dwx7uG$$8iR!uCLCnUv8w(rCrBO%%kn8Q zS#P-H@V2!-{G>9FW|Z4L8D%jQ=^{x1K1)3}lkpBl-x`RmLQ7`>c5^3KsK8zH-WLY` zrFt}XFPfQI&ZP@%bNYAI!6$f+S&!!7l0`S>wH?4Z86!LP88eLf$h<$uKRMP^&tV5j z#JYY8VvSejecAyjH49V>1q~gbqx2x)^(WT={oqkyN%`J+zUm-wPo}L18qd(0+`JsC zN3vRV*pkr)#Ax?-J19$be>xJuk6clro`(_+f4^Wd^U|B|f=+AqtzMWQWcuK3FtZ`; zbTFs&QS`kKuGW)cMSg3b!V~E541{y?u`!=M$aC_D}P ziTE(VY&-FOMmc-{41k~FK2G`ha=?6%)o(u1@o)g74`!q8N>kMD>9z4rlU}F)c6GxB zn<}T*{J3=70vgF@`amZFd=UA`=z6qr(s!xfxcj{W{3BmmFKLwJk-j_1eWFPGVAbC~ z2Zz~mj`1my2ZIB0b_{36h4k$5mXAXpYy2?QthsPc4uSmX?!JUvCQE&$EE5G@ZH9c5 z(LH93y#E>?^G#eZ_RKrekH}?jRq6>fBya#i7VEk4Rz>wcIgGb*)epg~qCjC{L4lL+ z%UibHJ$pfjTDA^cvN8SI#9{r1VqOQv`F5}_@B=fB3{U^ITXe}iXMWktgz0rNt*k)4 zJ=t)qwaelABNi?-v~P~A{vXA_fRBnmZ#f@S1M-P*8g%xDB`3$z`{*@(Gca?{8`sUNy|*?^wQBd|K>-yeMiyWa8;6%OSS8&BA4P zR21xF`gik8Q)t(Uud#LK3pnDkUJG8ccT?*?yNd`1Bfvz@nh~Rls*Jf3iaFmyQm;y~ zg}Rg$fzRYZlsFNkp5(-HKFDQ zEKVl`0paNkq|B-b9TSOxD9bZWnX3bo*St>z>UGK7GEbXCaFfN8$DYU^>vizy zdSW&gBOuR^2Yp&9!k|iv6G=brcL6)>?P3W9tA_iJxp@ETVND724e_h7dDS;5nvR?b zG#;bU@|s6GrgV+pJ5rmkbjSlD(QVbkm9oW13wq8=rpig;4Zk#ljnm17&ISl+LZfGq zMN3|A_Z@)63vLHbsbucu0$OXw6z|WIyx#^%wH+px=)VW-){b2C0%babg(4e9hOZ_icHh+D(Ip-yt<4-@-+Vh|rHn@XHS~;OEagh^z0{-n>dXk@y%NhvI(} zWcG#q7%^aCN?KRm%;<=Ko%d%SuOm+68G+iLCcvksruAav!`G81JNZ~OuFDd_;3%bK zp}XR|i7T=%wBu0ngZy)FOUkFq)i30QeXm~q6q24@LiQHOqlOB8+a$_fAo}t$MW?=3 zYW%F6Q^{a^Uer-oJp*3U&H@wELD;ziHg61NtAwCu&I2|GW-UWMST~*mLczBoxGEj> zv#e@Y>!c%V*v3O5D|Ei7LvgL&5{r#aX1bG&+;KnhY3iqB*8pypFK<{{qMkszpGU2d zukHP^WQ}_YP@jrL`jN#1FxA&)ZEeVa_#vz(_k@s}I0XrY`iY#2KB`QzArBu`5hq(x zZ~qOuZBCv7%mR+1e2T2)O(@3vi&KFA5Bxk?J*#l)=I*L<`}hx$oJ`I4xW$Zc9s`TZ z9af$KtQEfjW^Ss?0ty#DnlENpZBGGkmyU9XOCtMY&vS>jVCjAcDY8h`C6YIKXcmyx zHZBDa-vM?@}R%afb3o`Ud! znRdv3g^G(_7wc_C{4uL+d!e84LD11&V&~J1+fZE~{(cs~vhhM|!j4^`z@Q4u2=gT2 zR@7vrQ)yW0J3vQ2vuXzesXAufY!~68DpA0OgV4Y|ZzZXuJ(C!zRX zD{0v|wz55^fTtK$=VA`2(TEYU-6PpP7$t^sih8{fAP%DuFu`}IP|PUdl%}KrK>q2V z5~J6bqih|rta+7sD94=#rxNH@XhWR)HR$;ztVh&?m$l$&20FgfdstQHma^nv0Ausb za=gA!<*wMh`7?{O@mhbdC8#g-DD)?;cw_H`yOyH7y1;dj;4a(yY2t9zvH=}~=w5L* z!K#c4x;UdVJ~t^`V7JB2dwr&Dn}>N&NC#GdhkgF^x;EaY7dtgb5&||rDyRKMLBQA& zynuqrvg9{sUvhLt+zbQlc4LIN3>#I3Ik~vl3V16-1wBioJW++~9f!;7uefj@6gy#H?YKs136%D4HB}6M}`FM8{Z({v)!71)+ z&C|F?LCHcj+I<9TMLExJ8ELHVX^H|jIcwnm1XfJj4SK%0uNzbfa+fnYx;h9JlBrh+ zNC4V4y<%}t*>DlC1lR;{0Nk_NqOpj}fW`+9%a>SaibU1gP%aTr0r?^Z+d&f!jUU&N zDf)YoC+Ep$x8s0`U{e66W@r>a<2WN@T|k6d#UfxEQ2q%V3&>dl>=%9c1t9`LoDdK| z7hs2}Li5%HasaP9kN6MK*h0s#CV&;U-6DHxMRE~@MW$#176D3cq*wt_w)0#YnE!w0 z?I@tOGfcj=gQI-+F7L8)&D8wDt&pSHL{+Iba&YVK9QL=jC<1kNa0#}x=m4z9`p0y- zsB~5`A}aEHa0y?L%jP-~l->Lkk|7WolnKPB-?I)~P$aA;V2XqV9Vc^W+I{SE5T(OVo3K z==~40uCSMdFZ*2=>=EoEhhS$an>UUpr1=~?*Ln!eJ5*|ke1;Ziqfds&NFCRVkcJwR z#;-&lWObTGX#fj=0;*+4avYF(q9yHTii**qPSIdfC{)|d1l!(!E8^=Z{0^OCVqc9# zzzFGnLh1^Fqz~)$hA5#wS^Pl!$}%#&{I%bs*%sgI0l09Js4;YpDRxSmotv|d$NKW9HrUPT-*3~zvK_YEW~~0{d*hhS$vFqe#Ep88o5a&V!d2_X0Pij;8X@F4J`WMW|JFMSsYRoh4a}xOOL@vI z-8WTF+*dNbR;GXPX>PjfbOBN-IrWnGTE0ybQ3I`%ZjasT}~1)D_3bYbFbtpP6W9=c5f%wvA!Q3 z+85ynjH2qqg&))@WjqQsX9PGLr9$Q6~&CfeMCgWr#MS~{Szs%Dk9Ai+fZBqW81*F_8fju;E-bGr$cpUYaz5>EzM~nUiTI$( zD{bPmJUzI9FHJ%yEN{A4tp<)s&;Ppi?=~-0xK-=buAV4ICqq+=Ilp3WD9zE$&_Wpc zYm*}|oip!1xldEFHj7Mbm1XyLjO_k(r-ZGq4w72lyD2IFRM0I~GzGLg1IK%`AeNIY z6qwU?$%34&aPjZZg1hF}4V|?*g1=S2F%30D70=Tr?@_zWF+ZcMtqZv|$42Qvd87w- zw25{1O{S&K9fAn;b(usyy!8g0{PUNzOmHUtQ)5U*GQgc0S&@(H@=0DN9p}riOiPzY zERG1nI}P(ioGJ+Ew8iO1;iVN82+-0&e7 zYrkgbYG@lf{1BWUa~O#3>kLh?)OfHoNWD&S%~2GDiar#AZ(zGDF&+z{)`NjN*=31E z7{!W^vvxHP{$->^D`ZjnL;v%1z!vpmvC1i(*|VI&5}R)RWyqv-HVEN6Bc45!Yz2wD z@=kio;&jA^!fubf7h7MEP7e`asJ7!0kDd;HX}yxFxZw$c`iIp9f!;{CjM3F_gTfiE|AdWZc6Dk>bs|UA|=}m}Zqg=Nyf`uRJOAJ0~at9vzH(4JET} zG>N91Ii)C_yzH>_gwTZq7ZYm_7bkau%_eT04vxpQ^}*rCp$WV@iRbg!7^lh9K>#I* zh|pFFPV!@6F7q+drqB9YT_==Nk6Se=Ri>pFUb8$*x_or^hK(^0!z;@x`=mW$iL&-W zo`8^2fit7yQiXqn(CJ3Q=kekUvLp+&+pTQsnWTF*WZqiNU&pTR06EoKmyw4(c3d>% z6hxU{kEM`~3kLno2Rde_r~|K)JrKOBDnRSv&R7->*v*MS8^vIR1Bc%0zb7HEX~zKy zxP9mpAXXiStv-j-#Q{sOh#^y`2MLJEw9tEunV;YQs~6R+bkC|-+v11uqF_(OgzZ8n z64uSauOZzEl9S%8kP{ZT!Q|v7?{zUV(^g+xIr7&iji*1bD&8;k7C+|q!HkH%6&SNx zYwOw@7!1C5)X?0lkMs9Gy~{uw%dp~f9CF3Qag<||Wi0jvqWC_Fc~?@jTX01IE#`px_p6>jAPDR%S3~x) z(!&3yr3)E-l)pl1rur$J&w@qW?(D31qvDBSN^SCoFJvK)Bz3}O?2g5v3Goqt&(S}u zaCBY}y5Stq#fv0yz{TTT1e7uj@qwiE&OrE^BXDkaRu0pbMtQWtJnL+V-1FZKWaXc? z#9lB!s4is7ekgqw_O4jv%zo-2q)_e%xG!g?sJxzk-4@a&9>g^Ti#&!=FP^nk#E2#V z4%!b3#~yG-AP_H`LUR<5$Uh1w6a$+`M790m-Sd-e=|;2rYKKPu+O$+;X`OuSbaBbG z5dok~z1Qc83hAe)Mk*fyO0j)L^*&2RnDUrLDdk@zuBT$ujEa)4Nyb}s%S6mF6YLZp z5ajku^z2JgUPnt$7lln-?Yr}!=q;u&M)ppRz|gPdghRBxz*F&$Zs3%Eed(S&A44!p zGV=H0dll8z)^hXXG1CuPU2YWrhRo4?gl_hTgTfr^(&HV-y+(#6UEvkS4sqVf*-{oO zn#aww;?^fG6Qi%%89##|E}GBCsgR%A`b9&}RifTmoy6eZEw>3;=m{daUZxAnNNr~058+TGk60)Ta_i+ zX(0U4JUv)=QwJ9Z-j>H^+_YP({*4p+qU_nA@4X6Ms4LH}d3ToaLJ#)52kPXxq~q)x z#$4cp!OZQ9Xv5tv!6c`C9Y4!6cpjS)>_#q%^XL_sj)Iy+l@x7<%nL0Ui#=$VYB4^P zJfK6pI(hj)5mZedVz>-Uy3po=>Bu>&`a2*)QGKO!T3^&3yXK%HkHTk>BP6MCqVTjZ zeby5!bRyd%sVu$fJt4Mzk~!`j?sT{0ykhs&@I-IP)BF$SX+%%tdrgG(8m9Kn{U;%w z?YL4q-vGrRm98nRei|X>$Z6~aE~m%!^wLR)gD^uHT%Ls;Oa5o60hD%rn0!*c!scuY zeOX#dPZ+#01v)Z*v6Pfpx~!yAxM279(&3*Ng#h> zTvkpmRIK`lPbf_!=eE~*GC#EK;`?r7D-0SIMEh=_pYKbt!vPWWC-Pw>YEJ3`{;Kh+ z<&jyb%iHq$`A*Wp8$`!1$ZJtJnprtpZ_QOC%h2a&BiiV7Er>!GF{6J_7C%h8{=;8+wRsvw1SIT z+s^fH;l_avz-soktF4+KSF?8bjY8BC_P!Vi@TY70d1GCO-)lI31Og`U`d9=Kz)$?R zmcmE|(m#%$?k2GNv)#T*tOs%J%#rrZjUNW17zi7rlNB#mTJ`7cY9BKimD&;!V`0{S zm5}f^6US~|3L}qmje%X@*Ungi$e!A7;AbOdrC+wab+rGrL|+U9K8k1sUe3C*h-!6a zJqnB|a=D;_6f$^!3UIQ~N?i$9cxTNO4G(#qDQ^Oy3{<}WkIw7KXBahHstWi8Bl~HG-b_>;b{HEX9^IL#& zb%ZhdFCxUEN-7%m$_^;Feg{qDYfo3SEzbX>*>ImgP`}0crs$ombx)@l^bNgRu7@{M zku@*01g4<2r5XxBl6a}xgf6E)D)k43&a|^27xdCqEu|kYv+xhLS{_#??z)3V^t046 zh>7Hyr{ZWx;vOI}ArUEXd_>KRnszw?{l^(_tsBkvJwKS{`TbI76s}MBve*G6H2&`1 z%pqU{p)w}?T+q|71H5G2EYVA4_(8#NnAiiQ{s)m-wao%~*Gf|NLt2dvXWAS2j3s&{ zfo$WYf1XbGW*(!1IAm_cO^fD^@!9ZiAmZ;&F2AS+Vce+L@?LNVo?tK@fK;SS!VoQHk8Or;)n2mxWLDJNy*WwWo{9bJm zay_oICps~8umD?W$90p7q&qD0anHGoq(Yf!LTH2GS>vQJ>0Xi^zWDwiDkQjgBYj&id%6? zF7uTBEx@L9R%+D z1SEf=XZ*jtkS|Jdvpop&Ki1%UJ?jleH>$oU-Y+d8?D#RRxxK7i$xUEBMIX^Je1fK9 z-~s!T#=4ocjssFOZLTWq!Jj9Y@8N)g_9(^-6ZaETtDFouX%&HuW}0xRhXWy}h?b(m z65KRP(2$6&Y`vUfyK^+TX9+Nm7pq|ogGu8W{=Ht7eh7##l8_jn`bi_X)r%#gPAUi} z$KWHZZ`Jk#gr|u}R6XmZ9lM`g6d|;SLp5Kb&2P^JWg==J6Z??D}88_R~V;1LW=_8Rx54RtF zXJO_$Kv#bZXL`usr<|xRPIWTvR~xDo&u1-y9Bij&D_iBGT#N3@9esjzUfydjsDVy8 zq6ZLZ3z{y=Pg>Y^TPm7wj8M;f53_}1z-fXUvb6ISkeqe+_13i=;|;1vfI!I`@j^I| zsd3mf&@NA>Gr`5wKYvB6k92k1c`viO*_Pr~ zv{eCD7)uaOE<3J(26E2`JT3re=J{zb8UtjO`KzOaucy4OOfhmh!nCzpqU!)9CQF7szneIXJALLkVf5oKJ~FG4g&%sDT<#WHt$T|T85&I8Z zt>D{TG3}bG?tI%!vg~TlxKMioiM}KY@eF_;=#%7?zz98Kn;^2@9+LS$H~w7PTu9Gz z&6@1o%h?_g+4pM$3|gp82^K^_#^`J+pXYeRXzGN4f!CB6nkB>XcEQNniXg`sT#*XW zL-$o~x~YJ1KMa8LNe5^xjNR@s^;f+&F-d7Gnp5QUJEU8|I62!O^j&0b!ccF~)q0hZ zGv6WeaBLtP16xw$t?{V?6+jpKTzFiHh$Sfjf-BHM+YQMLN@c49`Lv__EB-K!d<5%a z$#v>gD!Ge_-)4b4?oD!4Fz!is7h4T?U;UTP06Y8@1}j71)ocJ&=}OemZqM$>E?bFe zdGwdx(Cf&iDCO@!M82litZd>};Meyp(b$y@r?16d`LAG};>XWBXefg%2*GIxOvLA& zH1w#Fn9^`wuENg6DtLou9@~b{FiOw0 z;_K%h0vpn4^m5GgLGU#2z)$CC02=6%1h59e@q-C24w zVz)8`MAbv^s0w>Y&_ovVvHJ@u89h!u^wJuG%?QWs`v;SOdqgXCVVY`ZQbob|>cxj? zv!%K0gS^Pn!uFn3t0ezRWn!F-D*lojJMhkpOBjp6xXOhyv>Gv?6Ms=UU(SMCDx1w= zz8UH_h0A|-(Hg=Ujl!4(NMJC+_-)=-=xduvv5NCz^03L+K^L-B$;$M*Gjn#=^TDwM zcWEMuI}b3}vduGZGqlr|kenQ`A5Alb5bzhW}nD&GZCz^?DVpu5nsvRPo!u6 z&nT`{&rQQ9RTsl(fn!a)F7CiT6*;1C9dlA_>yvVpyYs?Z@YjeOz9q3nWM8Srfrw7U z6iuXAS*Bt=5}&b@`H+6)*6o`w1{a*P{Wv+%{GNK}-u|b36(EuaL-+7w9_h2sBkU<; z%ApIn!u#}kIz#22MP@goq%jEj$1P5ARE7ACszdx|FwfXO& z9l7yxD{L(K5^@&QDEccqOiCtHJOcYRNI%)!=}-}YXuy~I_flx=*V{KdI1AsIb$jO3 zEYXlBG?V6R>vR1Rn<`b#@rFZ*^~k@=__mTVfPCerT9*TK>80;9kd5e*-t)|tjE|}U zio^eH?OcZ|R`SB+kj1HOW;?JR`d+Au2M4&}A)#{@a5vQJ{;SodqBje>V0b(_FqY%5 zxq_tBfv_Ut@Gv+yXz1bvTlYBDA)~*|P97t`&c>pmJiY#2YT8hM@Qs2RYX5i_O|AaC z9KcSdTx5}*$^gt2!#o`hbxnqH)Xce1^pDqb&K5P>>~S zbs}eXCzyy>%axpCpMSu~x-Zt@iBgvid3<2_6BBH*W|l`>|^$$8vlYsM{s z^n_z42Ov(zmksHr{ykuYqR9B{Kny39Wsd6C@$7CJ*L|ZulswatKF19W?=iejk}sM; zj-C_AG3q(kL8dT4O+XJcD$`CWOsGAzVIDuWXLwEfKOt-Ln_m(j$y^gKGDRq6F$~Db zx$EFSrTizEF={%CGAm}_^6$@7t~jBtPh2F)hC2Bfk1th6Ja=DK23j4M5qF|3tsP3OxCuIa=QA?Ddiqn*9(6q?P zB0Q)-ls?gnhY*OQ10c1%OB{hN9t>^mU!}??Si2%PRC9y1ck)c)o$M_(_Saph{>N~y zk{6#+aZIP$&HUSED>%P7y+37KyzN8R5m4APT7dLHVe;>BdH3uHyvH{Gm}rY-{7|v% zA4r}QTyOR)=FiYOD>x@FWJPyEB@GJzFXSf!?v7ogsbd~QndOZ@Qy+=_BAhBuU0t@n zdnUed>SL4ck!L`ai=&zk9hB$7qAg@{`K8izHIFHY_?U7_xb9P48IV8+EuC=OEjT*{ zg?a=D3uEr8bNa0EfXd0kj}tZl=ywn~NHw8d@7{mS#~7ZDwMZ~nkekJBwHi*>FFWjZ{@sVN5EQNws#Jmt5;<*4isdNy^GvmI0m z0E*ui!aOgtI2L@}wlWcrV!!fR>X_|C=5r6Ii)h@73e(gt+_zfb11nidWj6xcFNA&Y zngitQ()~4nJ)%~j-Oh{~Z`1ulQZknFqa7tXKwxb`NzapJULxVhX>wxkq##ci3h+B< z(kfcf%v%=AUc-(-=yLo^`6RRq)PbSjVIzidJG*c&Zx>+*LDULmS9WPR`61OCmfxvM zsCzeyjDDV^8tDMq@cXN)~FOcsYbXl z>cp2jjOdp4Xpbuh$~rJ^)LZ0vIe8s#$NwB@41_9Qfykaxx*UKoG9zsI3pLsVsn1c| zKvAASsx7YCm;Xo6xyLj0|8aabGuto>!(tf5hQ(r7%q1+!gTVBTwfJZEgk1_yqTFHY!06NDwnV|q-uP_qywdg2BVMyw?9=tgPW{kX^pS61?(cCr#l6mq>X$g9`G;>nS&#{l-nS=dmfmGwqR z%-c7WAHFv#a~~18UY%?zvz~k@yzL3^L?MYFALN)t{ux#3`jjDEcf;j{qmMVzqdaPs z%LaOX=zJAR&wWtnXfCg$5F?z9OsP#DC{^Ch1bo?!GD9TRGiOvgth6#DZ=K>;>WY4_&UFf_~*PxRmF$?p4ppD zF!CxpiAyz(RbU<)#4DQ1$qLm;`oLa&Pk~Guup;_4O7t?I|2ZD=@;*|{KyOWDI#HOr z*0|X!bAQdJmSwixT6B^UrTX=iTp502Yc@*W9%x|4O)-WrTF8k&!le(^od*tNm^x78S%aCmYxtho~#FoW8c^~`5@c`nr z^a=r9M#7TS&0GnFL}mLsW-6s1ttw=-L<9@5o+Jnt1O>r`svS-5;}(GTKw9w;J#=03 zU*Cs2*UlKTGHH&^M|PZz4}Cd(?yMFa3vvf?`|3`ebzj}dq};OG3mh2MtARo7K4fHm zU>0bf0o)cT@U6moIh6Tf4-R84yktLxD9^oddP<<@_N^xKNVDy8tyywI>%?;uDDnWc#>3li?MBsE4;EQvjH_|1 zcyGmVSs^E*=0_?Eyf*rTTQYsYf$<64PF-8z>a@Jk=u!r&HaHoa2d>z?Dd;dZV_y+B zNKF#wkz07uw{a@ghWor+t+5i*xGZ~NXZd1P*Pa7IJ}5txmYPCylt8tvbgXix5s4lB z!Yh^FJ!L;XmZ3dm?zmTZ31z6-vwc`aqRS?_yBBpfxJS(Rf`j$y;~>wHuT8_O2YYs& z@euYq4H2YMZmjzurz%FCq+tnYB!3b<>~3x&WqJ9h*aRVdp|>r5u|qF(=W0-V%hv#) z%$r+)t33{rY8&!h{X1zF9$98&smaG?Q+J|xnLStHKjh%<2MJAq1HBlRX28&8_g~@; zik9d&OEXsu?aWlvJu2!kRY{Bv@MK8(dO$Q4{OiOQVB)C=cnvwxXlcyD_LCCfR^m+& z8DzQX5P~hG_S(Z)k~y&e(?ctQ?~t=0>w{)h;s@`!PNbuy0$XI)lM=AcI>Fe=mVw2Z z02>23mg)|q3tl|EM|%$5R+JS8^B;M1E^tftMPb^M7G)CR?Gup<)14;Jg6J`Ps~O&f zALyTb`LkEld6cUgYu6@t%}NPaEJ|}l)?9_zNS^HaScH~j34hv(lvMP9nTV`>xxrm2 zYY_bXv=YzVlcp_Z-nUe@^;z&C#A#*@_(F*z2A_K!WgiN zJ>p2?78afFE)ck6qxf2iPkl?Qd;|3+>DJwawl?x&OSdEV!eGq87dRv^)Eq9Sspy-A z@#*XsWu7p1fqEa;ffq{e*(qV9_*H$_T{e{#v5*i)G{8K1AhVI-tP* zUHS+945c^GbBCX>KfN$_cLfy}M?3!Yq}Q5zH*IMXiN-+ap%qdiWIui9?Gak3$fLBB z^l;Uq|6(a9Y+?i8!%}dDu*}CB%|r;#R?b33uHpXvqh*gN)&G`{1!rI&(|KSDb`2nA zr`v$OT#S114p0DmK!1kEG;f#c$m;dKfHS|LG(p+dr(I9lQjUDLJ3*`j|Moy9^|(%* z8l{!_q@%rk| ztAf4G*u#%>)8~U~R#5oN>xZ0=LWqk5PWMAiT5a{!?u)*(NJtY+11JxzNfFEQfD=aX=eUrJUii>vTp8l{jRZsVyyk5Y((O<8^ir^=Z)0vBH{ixo|3jL}Er z#_|kCKtXh{C&W)*IhS-4)rB+s;IAln?NfIaW#Ds6-2UwTrO>SO3ul&N{<_tBzm9DY z9(4uLqu-^sA_}3sirI7bM2-xDH|S!*V8^WH#7=FXx_+rf{Eg-d$W3Sz$;}T65V7Q? zO4K@?`1ak+3j%@;th*8tn?B#jyz%=^=aH9|CBD|6rlTUKA@0`4+C`t@x^zui1f(ii zPphz`k6y!8HTu34-FVjBy3KDe^~{XdbqVgS#Y;h-vV8X-X}=@)^MVV$HJQ^ub*V+@ zyWq(%txzbq#R6@YACL&Ku&}5&1h6n5$o=;3hgQSg97!dT*=;i4`lux__n+T5F7g|A zXHr>qUwn<5!)o?-lYv-{I?si3ZqutcP!zYiLc(#rbm%Vu4*Du!==Uw3In6JrfqO9J z+nPBWA9|0Z{8cLwiaK8qh=v85>mBM5I3&vlpIoZHhGBW6c?`)pB2+V529mnZvUjK^ zoH$0I=1lEyy_4>fgz>uk5cRb#hROKI ztnI(AQrQzmJ6zJ>KF;^)Vd?uNY{^S)<1OVE?k&_>ApAUwjEc>jTQw5DE(1OTBCh)Pcv`Ogo}wU-!ByLmKNXGcBp9RP&*gZg!}8?jQiApRYT%oxf? zdXKj@4G#?Y&mZLCHk%=$O3N}kKlt5o5}H%^)>T>ZVx;KX_p!_W1`p2;dlwE6H)2b@ zLbJrIF1gpJWJSqFle;(QAx%3W6!2q+@XmZ|jh^G(3n6Ji=*AwoMnB?DG_huj_>b%W zGO6DJ9MGP+R`>Rs{B4+E%`8S0s0i3T1OA$J62lf)5*oSa@v^6*At;pqi-LKPr(Tf> zr>)JV+{c+V=_c=5;?N)IR&vhq9K|CE!e^F#AkC1OQSl=e8iS^^n`3X!*-e1qVc6T~ zliv8fe#P&>74`jYU!Uo+SX(a|Gtp*F{6UxVQAurD&TwQC*6829w2RjU;|Pba3qqnH zFAm(bJ2jf3nRA)+_}DNLdrbz79i-z&0CY6#q2=XRIMXuuPv-VvCjy zbm|B3FPr!gyrYM^YJvvrB#8n9mL|9^kDjoS@7wxs)`+pJX(Qyg+CU^J68vYXKVewh;c_ zRw?Q^Ek%Fv?BJV>EwbIfOVVpd!udjki;3OY`|O$&&~P%(@>R9cYZ5bgwg${XJ5w?} zTg=#DXEP>EM&cX=?R&aX#&4{5&a)JXu7PallTE-hj_UOX#V16AN9-mfF#KWg`q68y zQD|&Wq{(?irfS=Z6HoFI^vchrQxOua(3)wksSq+F%}zUuupcMOEx+=?^ax>J+1vM% z;=Igthy>M@!8IFs#y6iEuS1XRW<}UBC@oF+U6Q1uLJDPI5L@c z9%flkujH4k+c`9mrr5C;>hJc>OhxiaT2xKg%&MD8S^6V^eN6vC`WMhNX-&nU<_L37 zRx5Bkrtq9GiE=G0n=O9V@_Nf_i(pXWwCq@W_KI%DX7SI~ld4Cce% z@(V(LH#2>qCbAeUZL;qPr<7%9v14NC-En67KA2PYAL0B^0f*kC43?Zp>6QGg%G|@N zEv?F?ku&MQtlWcCuL@o-R%`n?;ylP=UJNYIqTd#|ATW7_k`HCP?tNXsqoH*^ULO|Lcx2hpH)q7Az@?D zQ+rLi>!)5#Tu^2I_RQq!yjvi){4oZ3zy8*WGkN!dKYrm_>|qbF=iQG%>&pg-&yjW1 z+LWnduP;D7Dr%pD3MHnDHZnfLUQF&A5NjjEHI2O7zDVW_r^m`0IcHGgMEmETrtpeV zkyL9_-m=`~&$kJX!*B`Pq6)XOy>x!Pa)ZgQSKPYfduJ(nf?CDITJw97uc*;J8P%k= zJq7%WFfu4C0;<>~Vq-_>k>~I6_Vf>@I#VS&B@fMoYD;xcs_Yb_JwUl`d0?P(EQ0a@`>nX~+%jrubPQk$mim3ffgj=-S^Ecc# z@WMo3Wg-qDpn;(nEdUFWqD4WR98C3yeZW(~!#dmV+=YPvK&0UVscXpej3q++Yuqk$ zo>jgs{Ed9trR!YDq#YO$v@B#Hr&;(0krrmTENLtXqu*}FW06Xaeb)x!auqNB?Uj$JV1|pu+WjZuSOw`peO?Gtd~3c27IbALO`&>M zXksTal9^W=_b*%iR!`=Y(m+^-Yy=1SI!WZ3g=rE8B2m=0I zJ(btLxu6Ncgu(Qj!{~3!Hq*s*yAk=htYFW|=ZoJ{u^NeJ) z5}TPQEG&d#X4I5B7+OZr%^3pRA-ecT!k9u-nRsWI?d@emwyA5>eF8b04ArZlzvxfc z!{0m#DvyFaju%gl0=`usP^&fgFsUja0I6@QvzR5PV*@!g8KrL_1U zcNJ)abOdK&^Mnqwq}4$@(f6KP36Vw%=zS0$T;9hgQb0pb=FO+%iPT$501d9nY06)+ z@oBgl9*Q$=1@fW;QlcZ{f?;69gDV%+HFDP@I#S3U5Ha>otzL>mfUR}{i9e-6#31&F zk5tG;Q9S@2Y98;z0baE4Z%OsW`6OyP5f3iJo3x4$u`zU#xxV;`_Y<>m#=8y6sd^EQ)&MN zVB|%{u|Tg$yTAvNLs2IVVs3DR?^rT``Xp3+kSFlWE(rnVUeosKEVxBl#G^e7#Et*c zJ~`-0cL{T`2g%4I)=q_WUced&$2***KEeRw+j@cVThrJL1X=W}Q5XgQ%vV{}?lNB$ zfJTz+xJ*L3 ztw6H&u`YQe2j=1u7-s`}M!DRBQJNagM&01llzn&E-z%c|`W;&X>JS+mGq&eL42{T|50HmMr$r$=A~L9jbQ{_s29E(N^J;CIgtD_i-&G~fvzLJRw?!-J^3#P z;9zG2;i{R!KK&=3=LoTF)5^NAx4EbF(_^tmeqbDT^2AyHljR3)@|?|2op*$qWp7)& zjum^EX9=2$eel}M{I5pkx=BmyY~2=U*Z)xC5RhuYD25zMPTO6har6K&fq&}qyQP6& z_ju!p-+afq1aj)?myxPt!m$q7x3&C^O00_TX~r$>ZC6AWleUH_^;Z&jH6P)wYQSO{ zzqrI#68@9Ei}n}(+~E(U0b^b)RCaN1Tea1~d|#{nhKdDsEVVrk`jZP-EHxQV#Ko8j zwy)Y>mk$(L19$w{FliF5nGNUP`1%lMeMwQ^>Qeg7%ZvY2?MT*zUV7<8k^M-Q`Tw$S z#S^KW^J#h>3-~PCS&7tBFr=r?@iloz#RZIpuaa$f>1WJ0`-LCk$+aAf-uKfc z$(_6t5cAaDp9VIGGU4+!??o$tkFx|qVVh(T2x6sWCtP^Gkg0Tx>= zOoI!=li7Ao0C{^xZ&b`T@Wfy6;^+a`ltS3UcK;L#q^Jo_FRpmw8*`A5slRM{_5<;& zye(2Ycf4l0{CM3fq?SH5((<_sC9GtR!el4uqWZ@Q295Z>Z}|XbOZ!{H>xXr1PkIv- z>JLhutG;zEvw12;!?`G3ImSN`)v9!~4lxsd-H%Dk=x2$Ad%;;N34NK6vxTt82geaP z7ea}L$8V+$-*{!CP|OA^K`=K}WEhc$b{sG~cH7 z(X;k|&fi0HwD5t0r^p(C>4)!WkkcN=u$l>h z$Ym{in|{hkngnmrsNk0QNb-4$p_;e}>{Isp|n6mq%88;1CpH;DUjt zLZ9#v6!1^*KNo@EOL@%bZmosJ{bjBmRmv*8vu+CmCcZa0XnC&>s+!Fee@xDqIradp ziX!879o-$QicofjUTlUZLClOVqQ#4K7GWHa?-}QTs-JHC2Cpx>T^>>ZPlWv$&$KfK zEeIBP6qg?k3{a>3Tfsk`he$1CnTBrvgMygR^##dGd}7T9EI%@^s0kh3%2C4s8B8g` zm<(<;g9`SFHj@Rb;Atp;EL_XM|JKn}*6x^qT@)?iTM3>INYhM_E@JWi_5YHw+P+S1 zX;92V~CO$y!`e%k{R|t|`1&mlhUYw%sXr_S&pR_1H*mR7Uy#hLZOS^ASqTd+h z$MLgkIaz0_+2YVsrZ2h)d+EgSODo~kr5q)fTrx*8>N0(;OhH1YC;g||4yJS824qP4 z!I67)<+i2fOPfKe(y*`k^P=F7>e0up?Cr^^@yoVp5Ek_hniTQb6lGcD3LGZ>L@dJK zZ_mMx_9)88=m$vQntKI3Cz~{NypwUDCf0o7Tl4D`0X=W%rWNJxkC4co3tQj_yaLGD zKNbU3F@sH^4MV2Rbsqu%o!N;wZ{Hhz^v-BbQB9s!2i+}e(ZR5GM%utr8G*v~lTUtw z-By@(Q4o)%TPTnoaX@}Eu-8`bDHGXG)jO=-+sj0|_=CVG{HKL-U(|PL@K+*<1-sZ- zt5{E)s@aRq+3E%EwBlUI4;59u1a^)zY1}ltvSJGE4*K~`p=9YTAgJSrgrG{Zrr|8; z{T3pPP;Q`XB3AH2N_dd9%-7aQxv@dKfZ%#-iG`QQ!Bfvb1N)t^FIsyYq-p`tJ^a_y?v~Wbb=*|?Ie%IeZ3rBAO*4tmx1i3T(8X#o=f%U_?b~tGWoi2V; z^r|Xde4`@$b-X49@#OWJW;bosS){kbFa5EL9{`AbJ~U7Eb!-w^}s+}lf@YD zibT`Xx?IM)3Ar>f=s74Dr_1c&(@riXNR4$n0E0XH92?hLpncAXuvLdRto5w?-Eo{O z@$Y$k<|}IA8f+Ua@nYS(n zh1CcIbOxvRa+q^L`W-8lI!)Y5>#9wkvsef_B;MKDI?z9me8nz;703oBnmgu(rQPTj zQcc%LNn?cg*Pz1KxnZ1}8oWC>w{V>u^YF8-N!OEn?P6(UB+ddkm`8d1t(jPX}XB2j(D7B+KDED(|}_%^25h5SmJ=8ENQF$FJU>idPYANU{vC^#V7KE@ zHOXt$o08N`byWE{O*9M*fD8j{kXJi{3s4Z4B#w=2Nn;}pL-5b@wT6~kCZ?b04DWsX z4pOrhrtqEe@Axqd*19VUR(|d2{z#omL!D~QL%z>T)cjc6R+FeoHX`U_u+aF!Q4LIx z#K0!r&)5}*$z2&E-^sLpdwl-V9+J z7tpWmHIJab)`0kd?qa>L*JRZ~MKjaF!(GWfNv9OCoiXScT3HC7iY;d3&NPU>ao{u) zm_wG-S#YDe-&gHPh|UD+U3x`-7qiO|Lv)F;t# zE#mEl)aM_qo_TpiCq3GK%w;w#gFp4?#+R4XE6fsfBzfdIhJA+Z!$ax?!x||MGfBo! zCN0wtq6L-TO~taP)}$(hxJ%%LpwTqeOq+gqT=40|HZ86@nK*qXTkh`3c#KBo#QHNa zj4n=f%b1MMUbOf}w&;~)*2I%(axzs~zS&{(os+N1(+Jf6covf`R~W&uod$A?KRBBQTFv8~h?0buafWUpP zE}>kmoiC5QCZ@m&iIq9Cc5KGTlpi<9|HgYGI+!$>x7PyM{8`k;-VHm=A4u71lPC6B zqem_uRaCY+Pmo6f41aS>{H`+BCG8Dq?(a~l(i`uB0hb93j2y-*7Pd%|gs6^soh$14 zO+C<0b>o+LMFLo}Fa zK7ZyzUz$u|ON-DW830H<|ATx){Wr<@nFdV^*Fo0*QYI0X zz7MO!zc$>O0|ZSLq!L1)p@ICx-`!b#cz>ZQ>ID?TYe6$N>rRzw?)ZEG$co+w;K;rY z5nLI_R+mj(m$;a5X={n9ZUqaU*$JgZYE3GR zpu$@2;gM}KGqa{cyUMqubN4g3hHUO$8<@9Q7?*erItBPB1DG<$LFkTOd7^xg`1m_C zA!^L6b)N$CqvMYrn@SqvPSGoQ{pidhQ9ld%8RxWIYg^I3ujo}kie`snhaPBfLco}Ah4!~=S)#6C0qc5ZY?(gQa{W{V5=1&Vfhy=e`Y zJQDowP1pf3S3Ko&pSO^aecin*i7zlbNm> zCx*zS-JOzkO!WHw^2b10B6OhH9p^66VD=~RCH@&R)Sd3CQYjp3Dx7v*>WwLsb=)#u z^0;LlPNbza)GO0=bxkcCD)$nbJrPs3@?fWyZ^P)|9x$>Q+ui6!HYP?k21Yh}jcnX5 zejFni*=V$FtatbK+D;=i6G9-Mt9QC6kaY$l*82`9Evdlqno)_}(IMh{Nm7efQTh!( zq75UaW25}j_SpM0JES4;4@%E&UgSkFhayL0Bd1Np70>yo7uGOn?POtoXUECW9ce&JV7Tk!DWP986IsYl2eP???g^UvQM9;sF2IDo zgx1biSrt-Ag9BI}(!C1-qU|gj;;+L4#$#eCN65R*nsj|B`l|8FF|30FQT?YI3xtWS zmV=t0f9u?ZRIC^{U=j7(iB%7yR>3*-dfWK^GDCG=t5%^;M&{8{Ztfz38Y zRg1=Xhrucs;N6GMZmBf2HiqwSy0fvyJsR%0t0)(Gr54e1CY%}04sYB)3D_67SLTg2 z7LW`-3}Oa34At!li#_ZDFF9#8d~#;^T71#?P>g2xsZ(cEkwLg*S^5?DTH*`e=mNYZ zd&q{4Ck!S3>CQ+m$8MxQ)eq@^C;>m`<#Pgymc{Ck2|r$uFQSmKD&IWCHdM}{@s**( z`rC($(cy2Nhw1+7AXwl=><(EmmoB@JOd-*FYvhxk|Qr+6!@ zKq~I*v^!5DY07wO>0?_NzC0}x3}z=NQx0XTK^=6^`_i_(ed=-?B7$DV)6?(XpY`XHb-l5@nf zGwZG4YxgNvxAHqf{)gQS-U$r_ScKP~#V6r)+;~EPrD}UD#7&l-FdOxT1u=%`OWhr^ zYpHI+2RIYckb^4*yQJB(*av41Lz}a^0cbZ+%C4)(vuJ^lY<-*{H}amlP*U8B8VC*0 zJ9HlrD%pL&x)EH(f6^1PwPkm;7MfFhP1PbEf6ON=bBGt`Q-h!4JyqiKTk6@w7Pnl; zZ5X8#f{{vPJ!^}J+cCDVlQ3wJP}JK~Qxn?#{;>TNyoXC#=BkS!H!UZ*%4ihVphWxu ze3ZB6@8s0&*yJ3_K)pq`z^2ADsH0PU2d12L!MY@9M_K;ptM#?2o=VE;e;~s9<|(nv z@5JVi<@8kOyyB&K*TRI@OzXGyXuX=DpwWv5T&Hh-$!WCkInUL3G#qE}nLiXH06R+3 zH{VjU;t!6 z<_pb$!jTa<;@4G`0u|RG&b}?y)beYET^>t*pk4JUj+^>Xl4|u)e`d9K@DfkftE$xZ zie=9+@G+9(JG|l;xN7gIQz>pfhDJwV^SduXZ8VqQn&*EC!&U8Uo$viwhw>LnPnCS9 z&`*H?!7ox)`R3WJCK%Wx-zaFUds$NSv<kC zp*EP<+S-Q_&-d!<6Hjo$9s3vqVTGLbTKy{(|EXF27Ae)mhZ2z} z8PHx{uA?|xe<&q4`m)qTUp;eVWyX{qfI3U^>uitQYyS&HpdcpxUwex{N}o8{=#@43 z*f{fCQv)1V22SUqOq`<9YnT%6-8MJ!?MD{^6~l0V+LAze(3+vb=$46P4Q@1ONm5qH z9Ty%nbt>`N<{1SE;%~E|3E2#c4qN>hf&Q!cleiME)six7%}Ob@J%ARK!KV3chCv(9 zb)rL9f8gB`T4|1X{wHrQ9dR)S&`WgWk;{}K=3DRF4VHyEHg`yYU|ffkawXhh3g>%D_cg;!DwV$_$hl1v6C)uLJC z86*s1$t7#`FsbiJ1k+2F!g&|!eAOVp>#w9OM?fcu*U+>LQ;I$vl>l)waaaJESDH~4 zs3j7;Ba00g*{+k&Nu3wZ0K#ssU4|w= z97Qz`?;DHvK`nnSA>D=jU~gm(fS@#JAjrWagWB(+U}^K?G(RxRhWWdvO!MWwn|NjCkvLO@)Fv6YFm^06q>XgD zd8p=N{aEQcCSs?7vk$-OXi!vpp{{H0%L=0YMQYL8cXJUNjqi1P5dA}AyL(+zvrh9S zi_2b(U307Jn~1ILImdGiI{*+6q?7-n6%q_XU{I7F5e;6uhE#T00Xq2)q8X5Lk_aiZ zw9e!o9~sCE1XdM?AN~`ufgq5`kQ)@y=WiQx45-JA0A*ELQY=yg|O}<7J412oGnxsX>KCSqi>O-TO6wQ;By)Gz; zmil<;T!8_b2epvnyI?uRuDNKQ5EijV@&A>ogi}^g&F`9vz^kci5O(i}j;0ooq_dl& zt)olcJGYyor%&1UlqTphFa&-Ds_q^!X@xjoeUS10vzSL5ot*!Fmdoy>;f9#WId_jP zF9h3vvGGSlV93ERM7VBNjQDay1Fz&JP#DOa^3(z9){HyeMIQyc612A@sMvLjN~i-g zF<-%I5e+exNCXOvyX!1JjSt<`(nu`_-j<@ZLuUL~+5N<4$CPzuA)fDs?Zy7Tf_6ih z3*-FUmC$Y|4N0jP+1>@cMOT2xtLLv3?uNI5C@HO|s@eC+OQ){h(^GjM6)^+X$+tZ| z1)iN>Twd7?d{^-RNM%b`*{&=gO+@UKT`IYt0v>HUmzL6%?Mj_%JK3F*oTr7ab<8G` zKu2C$@DD368U3d+TzklELCDM|Z9Qr03rouJR`{JyIM<``-`yWmtR_TOg=FY(C$OG+ zQ09STZHZP|p;v*eCubT}I#lt#SZ%Afs*TskDlW?DV(4RPa&9^<5%ZHuxY4}Wh?86v z1g>;5d$a3!uG$wbzM^B>jKQ0xMu7zxS=g9u$sD1Oc*1#H3W#QDv_>P~RHL&!S#w2$YF z)*lT7(ec1Qv6Vps3++R9Z}@-p%rBqU2K=*~824cW3F)`tKn)A5sG+&|}3 zYgb5Es!w>Tb;OxB)*NN!Bu(@ED^U;fHawikz#)L+SGAK0HFcCxwB+2bY(wEayHCOY zD|zbwia58M^?yYGaZVXd5PALcRLtE|6Nevts_;7eLm0dJxcl@3YZ-BdgNTaxf79UY zYAz!tzbpI7wS~eU@8m{N=|~X-uU_=v{`$i9lqi`m;Uf|_$r#Ir`Q`s&ZBs?cMGUX_ zUjt130bDFZ+&Ek!BHw&1C+CX4g_@3UlQfy+e@s+D8w7gz{PL`de~E8J)vU^_O5Kb9 zr(^T~PZyW9H9)F^-3f1VvOANBqMT~sv_upB^CZT!&>AuFL+4U4O*LV=l(y&u-+KaCDZIUW`NMV*qlY~24UF;!`IW=DySlJtCQdK>lX8riIfT%fRK(<8l-7n8&@(B#v@kDzy4_f4-TzB*N|80$vAH zxu2MAlCC_E+h~qRJdXr%jenT6Sv7d@9e1hQ91;|pC8NjMUwX*lp$I{Su2hc*^Cz~i z0#88CvkQu;?rt#SrM?-=+st1IX1@GJEq>O+;J{q4rQ3U#B2vm1QQwj*Cl|h) zEWEMznU!73s?6_Y^45FBH#+Duusy?DE2%kEUfETtE^% z7mn6E_Nh3^9|%qrbl;gb`4PD!dQf#uTc3*(o1eaJqxtf!tVZc%N)uKY3aow+en0Vf z+m*c9mABm0)MLB`w;t3ZVbovF?Hj_FbG*#di+)edDd!S<9$fb2I3}Jy4Ad?BRl5Er z{vviwd=UUYRnscXCs$f3u4L28rdkEtLNV>_xCmzJZaGg4TAOAuXi$W#GtG8_k{gU)}6iU8`&6>K!y8Zr(QO9TKNa>&3bIYos?l1l+LY02N)M>Q> z>hwyL@%=@l#D0)4#{ptxg;tzJQaD zg39YR)@i(#GZr=QhibDTjaeFx-^*AHCAi?hVhtAWiq_t0io8hyO4&QIBin;p zQ278@kOA2qQ_5a|A2dMzzFeYnF+F9qLcB+h6j2>eTo<;W+tZ2W6Am8`N@|7X6MK$>i#b*N4gDVaa)caV zpW`F-#~Q3FMs_09fR8DO-mF1xVmL@jZ>^ZIT0nmijClh&P)`zZel3~r zws#;f>pV{KyOOjNnVKJH;+*|Z&IOzKv7YwvvG}0LKin&)#-{pr?QO^cdBTXxS4t_H*_}c*a80#Cvs4-yvVUfUBWe)J1+BiKv_JGO5SMDJHy1 zA$X7L@tIG0Ohat&#^BCY^6sQzm&@P7_SVl3AAhs|eWH(82E2J9|MVSrbzy1FLm1UO z;l~}B{N~iGD<)cN)=!SpAp=&BLj;B_RApRN#|{$Dn8yLGR%`FwGr9jl^U)D6Qe`AE z%0$=x)?#0nY%dOnfGCSsec;SQwP;Z|IF-bzK-_^BLz_QuFqd z2C37T;zLvDG1CEUlRdnJ^9Q6fS#TfTp@jfLwG_1mwv&?7KB{H2a;sXu+VpVBK=fsv ztUYJ$T-e=qjZt7;xur*Eit5pQG8wiR^Q2G4p|4Juw(pnwHuCk*kGC;~DK-9Zxt<3X z?qgU&$%a8O@ugN7Mq53jm_R{R!I|;R_$ig6ADS6 zt!yd10q3a3rR3VW|F>rf(~3bGIkel>>49Q2=KxHQHG^fhl)ig>-^Yf7qMSZ*pQ(nX7OF^KlV785B_-U5BMdE(Kz{Lf+|_sCV&OOdlLk3ka^ zZ&fMlz83eh2=4l27Ppe*Lq&De45i3s+D@}>^Ckq9x`zM8Ep{>SoX}jGgHv^13QIkK zx}~OYmjm@JAO78boUfgUZI$S0DYbw+YGC-cB+mv_+qECy;FbQp##4*N{&D(Sy+@@> zPl%j;Zni82hC^3)joVPGxQDIl>BEu_dk|4!=|5=-upTA66vq%_ogetvqP7)NkyIik zkmBFEA^GlcaksbK72US)9Iy}M+=4cxbnFkte<`v1 zNzW?MgpEt&C4n>2f`^`htq9dgHXc(GA?0(8*%&cbWlN^zxXP_$(i-B~2R4bxu2M^-ljN)B9a%S*5#Z2`!dq^1kHJc#esD&Hb$T zhn+?LCf9n79-Z*pYWvaS_u^|~{pFHSI-NlL>%%jre{7BcHJ5sqARZngybi9*29_z- zD_(IdzaS3u)u~{U0`A*R`>xQw0ZvuSlipb0Fu;p#-|?llfuiAtFmsONoHUXb7RmA* zISdvpO$zUU3-Q~px0^5ZD~gx7jQrE8eVeA1yJg(+HB0iiyWSD||B^rMKm5MJoAm1? zsmE4iq8uzF=Q(<0q0G3b3)Ion?ZZ0Hk$rS1#ca=Dl*lIZzeB1VC)LR`)ptPWZ<=if zB^eKgOe}SN`pMRlJ}bVGKt?-IK`UAJ$}nHfJz3mmGD2VZA4gXn&-DMtH=CK6nS1Vy zVHk#C7|lIb<`!Z~LPXjeDb2B&V+^5GNOV#}j@(Bg6{+MXR4TQS=)U~+{cV4IK9Be3 z^Lg*_`0Vj`zmDhg{e051o(w*=>7ws}Gy32rx6?)w1jqItTBqu8BO^ThQ?NlMp+wAU z=!pJU!0fP=YUJci_Dqc8I_8@ES$~OFGZ(z=vFTQfv1W4%^(Gl5(eE}hpvzS0?zQdY)ptzb_Vpp17#Jx!DWp*l^4El#cjo2MN58ey^ww_68i|k|K3ipzfj#;t?o(I?kvW~-8K@^(+ z%3+9$$5R%{6-E)${ypYu3A92dJ*S-$HFV*5jXLd`fjl^R{KmtFg9r&!u6ZeOJr}M7 zQ+5n~Bq9mw5dHJ3IC=5eZqfz+!CQJ!&Z?^}7jjQ~i!k<%K!)TW7uP!7k|{3gg}YGQ zK_N+o9X2JN7@kDmjYavQ3aR`mi9$Aw$D>ID9e%=ncvXg(T6t<=&4ui|om83u`aKBdexDy8UZdPvuVod@bX{X8@yi~Gb3oPJOb4yq^E3(1i;T_)_C4c0C zbHN-iQSsDnjvrUD+A6{(;Stgb7)2A(53R%YJt9U~^ZZLB4DaiyK!d>&Az{Ko@4kc! zogNkSU-1_c6Ca-xn)K5sY1_u8n$6G;dGThGo5vuh7Urcn87S-Aux=~4(q{i9u4fP{ z)_U&=I|iZ{prGR({Pi2VZ2H(fo}2GwSKZ)V-83vR@W)YbXccs{y7)DeWXAR`5}Q^v zbtj2J{$zx#JgoytwINTYst+Z9of991JBDnvqa9rTA*|i2ayv`IsO7M~JNe>PANt;? z{X^5DG7?X*+P>e|0OlT5QDViXN&xu_x#YQNwQT^Q&Cvy6RoTFga!}_FN{A$}TBW>o zTAU5#cQhlE=Eld{-(fzS<~zE~ZPcmVB=f~UXaFGkmb3lc3lEH=C^}Xx9kiWb<5M+2 zbb2=6J4VZU1o=lSHAZ{0wPa-k?Ti)#C{7F9=C=7#Aez9oPGCX`!ZYz!ckkSa2BR@5 zjvE3g_>DC_tOW)DyH?ZrvL?4gf+Z-AYmBD{Q#JxaxF}Khu}>$s(uqnjM9^4pkn1U?^s(rYu;G zsKQTg4w`|luWq8B0W+Y;nI!D%Pm!`=;v)k7Rjil#_@|bQ=~$zWh`bxfv)~i{3(b-G z-cSg`{k@2&RJQC)bPzC)3Jg~Nfc;*#_?1xQ`C-qp;&JG=@@z7H`U^k5oXl^Arz`^Q z0lrp5k}z8v$1~I2^d;>Wj2?UP{dlu+E&$9t)`Mim1V3y^uK1f7t@zV);!}NQNNos= z>FwnAlP~pqP(VTo@DM7gDXi$ zF5~db;*y^xZ#V96NXAvC{V1W^)6HyjWtY7x;ADzpCA@-dyo#ya27uYy?CsclnUjoj z$r^>cJ7@AOrJHh|-mz_59#Qnx&|qi@a1Gh=hjk~UlMS-pV(u@>#qA~c@FDMeAAgLm zajNMHRR@ZS^nOn{wI2X>xd!ReT3p_`x9AE6f&s$4HV=gYJI+>p5hD3Q4lBHKECQ$z7fNQFxaJr3B?9+_zd4EzU! zkvpQPzXEL$KnNO`{inQgnH!Ej`QyYBFnQn97Wq2D0pcWF=sl0f&H1S=F9!hky+9U< zJ!Lfxmx-z|Z@ZkbSybw5% zfXk;sQhAR*xwc36va5|KEl%zKwY;fap1rjH-v6NgXs~U_l?UU2IX#6M-DuTw_XZd) zlFGZCuCl{tXxA^RZqnj_0inC6JFE6MBaAP9$yA$vxGyJ$-Q!TZS1$eHp9PEfTk6fc z2+qV0QpMp4x&OM+FkvIz!cu}Bihs?-9jQ9i=NN4vd6XnXNo3qVGwfbC7M^XN@!;JZ zlK|d%Vo_$x^YnjMLFWnKn*Z>U%l`U?B^CdD+#7xWe+(Y-*RQB3c8=3$Ru>Id+@9~O zv%F)tn_l{zDZa}9z7S|cg!>ZuKq7@VA0hws$=Yl(4)Ix;LNg2n^%-U)|C(rosNu!< zjk7xzX0u-&6(+Jp^S5wtcJQitT8tj52bFC1l-)3ZwGfXBvlOV%0&u-b2pLaL!@?g! zg=J*FD1rgL$ns3#7L&h!-Pcz-xe3bCDo+Afj`~w}I8`Yo6;%AP<~08~;dGlp*%FNm>p18;bA*6o?S(fq>yFn|g#cHL5{QjpuB=NN+daJUCbc?Ux#E%8a-6kEQAe}r zSwb@YDli+6J-8wq$YJGD3PjEVKKGCTWJeoimoc3lP7;gEuggnfLpZOU1nlW_F~+)ALZ+TP&}FAD*<>hKYE0!@#6n7LB1DgTTC@XNCO)Wbiv9Era=SzzB_{nuLk4u5 zJDtu@l!F+_SW9PL_1!;#S+z991h@E%$<22*%160|7#{g|TjX=O8!XoEXP5-fB*m}^ zCvATb-Xj+7N2x7< zT$H4{eH~zTNi=w_roRhk&bXZf!IYq4T4GnuG+#>9 zsxJ$_Cu~oTNbG>6Fr=)Sb966yvqYGQRa7V&zD)I9cuSvRD7ygaFzwBU6TTQFombvQ_5IIssbcq>`_5calm;i+pQ z4w10CbJ9geWi*v#k?S3bmxzxzT}Tnx`q@KQVPa0%$2;54>#53@?ZBTA(Q-l(cFz2q zIRV)D$P@hf4*iR!>;qzPzL9+#>I_y?WRLYz+p;uk@PNJpGaA>UWRDv|r6^g*fTMq5 z!H(j;8uF`a@9ubBj8pP=Cl+$>p7ta|QADgKfgD{lL>T|MKViwD)$2vtuH%Z3LojvO z2{rQP=sw@q(^8_E`&RW;Q88~SFG-H+$AP*E#Qk{DeQAH zA&4|Bfs)qY7mqIuqMk@yFCLb7x!(R4$uvEa;;Rvrd;0hdiR-@6BC>!Wj^pq_TKDV) z7KpRxKT5Ctz6Tv%z*TI9XmUJCUfp>}5|_UxBGQnW3ina!Z8pi?$NpI83UL>^(WL2g6y$)?&z6A^fn{M$ z%>kL~n3Guk(oB-{W3E*AAJGE`Lv~yR*^DGVj~I5V!Hb|psJxazD*kBqoWn-NSDyPF zyd`i$4{@(E)xh}K9cPO7Yjd5~nnax2QroJ_|3{VtNOEjF}j8B+-;*7idhW9jZSjx_`@1yag;GqfKKrFE_NB;6D^ zN2ZwWQcc=?@gu@N9tXNco$_abXMHYkOflX|e~PR{uYUwD6DSpM)B@G;PPmibQjyvU zMSc9d*MC7nd^PLLe?jAnaOA(Bk;8I4_c|+=Vq=^|Ua-BaFuFnYMWgeJEtI4Y3)c)FS}IDwlH@=HroAXeb2k%W7!w=zgXDVv5ZhJ>(U_BWolz}S~A zILSywI9Zva9o=7jl2S4mK5}9qN^(Jr9#%Lti`^}dYskshz4ySAak1_&C1|fxu;?7l zHtvBy=lOHB5b>RH4m%I(JezTLQg7;y1NgmiLY3o1p~HI~K@_0ZU>XjhfM88K zZn3gbA#rnWpx>oa?$`f`8p7{2y4w#Vuwq74O6T|X*qP-_*CH8Ct+sA5nChHtfceMo^a z6Ch;fE08Flh(8^1(teFzwd0y+(+C@{h9meCNW|KO&F;M3gv}^|?b5gcijI{~kTIRe zN(7AD-s>NIG}>k

%Ra^Uy|WKrOnBRYTb3ceKV8ah7t>O@V%tkFtmxG!3b$hXMMLJ=V;{dF^eFFFjDM`!-tb%drKd5vo4$xIx0|6zt% zDf0F2!$Q_q8F7r%!3#c%{vSjQ)BHB>%Bll{2{0lFm_)u1tE0?U&d+Z-mwP&YjDk7S7Cg5k=&X%1qZwEBKg*i>drhH{zC`ljdgv z8!A^Ls(%6u88YDhdQBFS2s97a7y@Om2Gkhxu2jIa7lPdmC2UBi=w~~hbFY7RtE1vl z=QMyp0+O=7>A_S=^}PbTK;2t_G8>usXMZ(!LhwxGOJV!%y{*QVK^x(jh)~*~qCl$n zje2(H;niajJnC+S!mY(9j+9w=Iauvv5~UoV2`8%}$1<&lp|jk9d;E_rS$#g4UW02< zYl{a<9NoUb>3b}(ODa&m7)bp`r_!*c7YjLBp^wB=*YyVy>Ecyx*vt`u)2XBOX)f1e z3XhEhgfM9bUQAYa>R5)sA}-afx0 zaGTg>%$`Qc@1!qBre-+zwY`2%)hh=#cGot31w`$H!bGY**{s50NJTjbRQ$wif6!Zf z5jdn@9HXbSntXXZC@_Hbn1WOCvH6zLvF;w6IqzfYh}}mwsP!rKbvN_yV*&EUi9hg8 zkBHBeT^w|^wDp?9W$OK&CqEn1^4X`9rCoKv(BC3-m3qs_$3MVDIRx}11yadzY*428 z27sEHdQZss04YE+kf?yY6!iV|5IT`=_a5bIcAsP1lBh)V`i|J*yGnvSsfv`WmXcLP z{`bw#G#gIr#bS~z7+a#ani5jqlBe5N7U z|1$6cSnwh694JppaBEq(5Ir8$9?XjeG*zZ3ys4ob@;pTcGw* zT=feXZe^S#KsXP!Mx$UF(=!tGj|cCb3S;U&e4DQ>fte|rimqJixic&)0mF}$^G^Q=o6=mjkx z|LlRY6+-k0wzlyFEAr=%QAGt5AP$Dkh5(9pwJ(;OtF==S(+noZHIJ>E@%N_J+n;Yrs(79{dNG00 z270PYNztu3IPsuaAFTX}Kpq~7NLtT1bAlNH((a2#@|M?z{)x0fNxe!U3A``U1w6k` zm_Jc9kp&g|e!&@mt39?#SA1j;49_Q8?QJG4Dq7&w?-xxcw3S7fqhstHQ75185`kO7 z1dz3k4 z6;x!?v|9>2YvG+62JG9LUsk0;(~~DU*mGY|Ujb{)!^Iv{>D)W){c)4#juM!afIPn9 z=6|^pi=GTk$7B(*0T|)Xf4S57q@;9V!TphbJw=RScJPJ56sImyc#klBqDxMm(`8Q& zNSd9LxRTb23q@`^g#VXAAuz_P|K(7H{8i!5f3-FsUE7$q(FPD+`NpSAl_HB+#k>;0|bKZ+L4<4$EQeeMFHG!OpaxjQ=?(4*SO z9toGEX`S4#H4Hw`rvy`1$Qa6*HIRka+%{@R4i(9DF`Mvn*ytsvTLFRrD8N4c!L0M_ z`-3o^kdxyLnpm>4%6VCxojaZIh~R=CI}Ht@xSgcvT6hE@BE2T=KS&P0GyGw9VJnQ z2O+SBtqS}Ypyv_fmt12!%;ri|iu=*t#I-R85-?w-3Hj^ebuSbEP9kKYL*V_W1sCpJFE!?L!Y;7}~-6rCNOmvXi?UNG!YNcDXzI-$MYf)ahS_R7$- zw7R2($ht#|ajMY@)vkHb0Xa!?zz~pB>TvokCoQ5&pPt3~tUXSj7UL;69CVi!HSXC{#-F=6ggqOxKo`4B!VVac;8ubMxW0hN`PUKwIV!ikH{$g{zzwHSf~_VI zpe_Pns;Ve7i^S~9LFP4oe|5^i;+<1U>)BWJnFrE%;tz*hn(Vi)_3Nu7VQ${A=}+~^ zp`Kr*`oXsQg8~!XUz7!6E}cucZY(Ntw}5jbXXRYhdCzgS{0P#6E!vNtmY_Lwo+fJ# z!Rw!8CqVimd)|GDl&tXb!v^1@+$^F!8B0{r$e(lsz>w@N3=QVV`S#t|+t}dD^~3)ioUXBXv^=m3-4j@*#d-*00M_BB(ci>g;JWZ$$8Tr!4_YcfZs_b z8YGCx88><#=bVbIT1^%!1Ajj?H+7QRuK)5E;S$o5B zbJ6nls^KD${A*Q>nm_Qkn+;QYVPHb;>`?4Hw>wi8e^XlUFD=h9)uzl9@nlfQXnJf} z5BVVflzzSbVA)lbBL^)OileS5g?sqL?!Ho>xFj){Fnxf+lp$@Pw99J_9u_EBPdenP zi8v3o(+(Z1$YEP3_R|4%E+Io2#PRwghT1^7eGRT8&QDj_}1fEAnWnt z7R-Z7l(nZ_37|~P2fbeAv98z>$j0LWuZeFl$NXO1A=$KHDsWx+JS1=~$gjXf&h^@W z?CuP|hdlV&&u~^Q?ERB*_jg{GYq~3e=JwSi9`&%<08{-d2#Yz;e036}O7@|Y9}BfJ z%VVslrv-+8_VaZ-HOccG=F>SKacJ_BRErBt6B{+*q`l_WH^`d@A=a2rRn?eN9l*X3 z6};B(_Y05U3=Abzr_kp<>Hk*c`kGz1ZA4Pbxn#DVDb|Jqv|JPy$7lNFWy_J5HDi7{2U7ALp%2W?X=$F$+SvZzSjy={2qaSSI_l zWpByod@!8tli|C(6YAT_v)pm~6T!`kn`U2`apLU317&Zh9sc@YupRUY@$^Yt0jc;V z@<;OTF{)^avu3AW>AkrPiUbxu(W_~Av`HZG4maos4t!>|EpAJKmaUtKbr`*WaaVfB zPof=w&FAZ5q0dD(plB|(3cx-zh@E2mCZG1&>ht07>Tb`df;!YN#yi(_GRz<8wB}x6j2VDdcB&msxJ5! zDkWy}yrAgjj)=bKu|p^YX=B0YX#S|96a;M|(e=Exd@!t1N8Ik}W#ZWtDdY-tRf@f( zufWh5W%_Ta@?ufBZFI;CKabiA8H5|2HN}KgK7$ zpz`n6-70VE9xc`Ev5%o`6nk45nfY%I8K~rs&0*)P_Tip?V@i^l;;$Z6eas$h?D6G4 z10usRlR!7(Qla^R_-~W@*y1Vx zo;jc#s9%gbi-fNh1YyiA#J$!Qad9Sl<%13HHu0oorVV!+rtaEItA)QnrLiDKr7>YM z07%{m*@g)lUCv2s*_hMVEJ~Fc8?Ey84EW4DTWk1n_DQcnY+u}OruzICByg$^WLkaK;aEU_sPz3Y8Z` z$W|$A&8;g)G@HAG?RjI#zn}UdiSfM**&RHl`A9;uj5WMrCp-e*R{b0)DT6+th>&&_wk6;~_S+`&rI6*^H z8DjQxkLn&Ww~8_JLlL$pu2}1hIwUv>3Nn^9LXvq(KxP-X&b&Dv4nzQh>W>z;5~dlK zaHritL-b;^n0w+gr~Bv9Ra3;A=NvO=XrT~N#=k-Q(urVbeh8F?Af7#IU%3NGFowgQ zuAI6IFBR@=dH?192^;b^C`PKVSSOidpqFEa44RDrO4V?}dC{0N|5oZ|Xu)F+m}}xP z7>n8k)$xVXW!OW(S1WV%*Rj987-7?BvY!cDm(9VzK=GB>pdVPgF)`;x(#d9Spkv+D zzb#`fCIE=(^zJ5WcbgTM70)3t={u&dAH5aM5 zf!U9t250z6?sY>9f&Xz}2P9mzh%4Byo*Ch`XOLHi8N2HVX(}f5hj51n&2MP!+jZ_F zc+uYO^^N1&evX^4Do(<#fL#XAyB#A389_t!FqG!YKPj4z!?o)S>>n-x6iedu?HPGcSLQFIvUzk2XBOuAy6?_~dZXpbYz|G&oPO5B>|#~1KcyvD2&y+4wTE@l zd;de#qXuI}i-o&o;>VMITNLn^KRfC#7BloLU1_Wl`R?{cqJ-u>PC;(Sjl_2uc7x%t zjX)pYMkWm>&pdUTq@S#BTygzsac)H=hwa6ohjX~QwtBs;2d2T!L)u22JZ+~QEN@y; z1Nhtl34qz+MHFt&=_3xbBV`nWlG=+G%>e(rP)|m=9F%ywnFwjpS_X#HTx$Nq^FOTN}dbtOJg=-k5l-OZ&w-$LuH9Wx>w?VRE_W;PEo&398;1 z9vuBHQbmJuJX_(_W1{HF9MVAT6kjZP0|G#rP5U1p=_}glq{Ls2vZbUGCPJee(1*4F ztIj$pbU&K=eS5oLNYeeAfVPX9W#hGg{6AJ}bF#_c`)Gn7MX@GF>L*1IM-znKLL>KT zT!rZS8nXm0ih0lMR+9wQbb(QjP77)E*tkHsPJpn0P2-JC>gqI~TFZO%-O9eST>7!? zp%Z)Z7Z@!o(sPDbbxXwiG6ZSH8+NZdj`z(QE}A#Kp9+W3d%)VFG{JduJ*(E?YfkSa zUJ4G%!8V3jY;>>OJ%bP|LK%`ze!mUn%N_EAnr zfNy5QA+d@sTNmP4ra5%4-${_hTUhq#JkaLfm*?NXnU49YV(-qTd>9lcC{oTrX?fI) ziN&lxf9t@^z>_fVM=xDl!LBANWWI>yS&zO4;jhXa%F5^&l>zCnL1rm zVI-#7OT3+&FwB9!hB2+eXFgWyzD+I2u#!a1S1t~2!!o`g*@!B!i}TqCh!l8TiZD_7 z=hGkAU6t|hufv5U3Rq1~-9h4Y?9dp}!Pd>GYscd*!p6Sjlz!^QK`|vXPUsDUcSv1!y-{#|8b}hDr2d!C(iG5%6T_lA^_UHI(Ao1aYU|JQg6@g{MPJ~**@q} zr3BS7338o!LA;M6bdY}{OIa~I7&!4m?VgEoIlM!yOPRC2!}g=dbhM5Zf&FZ?&C*tf z8jsr$$jIu^1yHlav#I(?G4^2%cR>4ovm}Wdwi4uEy-4+7e_s4*Uz`+jXzTYb)(YqVTnXkh%??E5g~5z3ARZ8u9}lXyr2<+ci)4?>FeW z4kvn72M}Q#1fIvpsF|pX+JC z0CQBIvH;&34Ybr>BmVb18fNt=QJO<@3GW-I51nI6kMvb8+5j88v z7A?I_$NeR@K#eWjvsZNXofWx~$6LXxmR;E+RSBzcB2stGN@-~@Zmep5B-7jl3KEbH z(N;HB!hIclJ|!AC={e;5S}=G5sMTGawtPwQA-<2<;5;Bwy776p-$*y9np>4AsyU$! zFc=FBD%7!yO9&+wT89I}e^1)xN z?ia%*9{&DXr~@i9PHc?mDdWN}oif5GXqV`ePG-1HzErlF*08IX%nm;{!b^~!irL5F zlVft6mCLsk216VteVrqMs*OM-9I@&onDt0ntJWmX((NzUCH$oWa{+1Fz zQrWCVcxvnF6M7^sU3hxp+_kxvTjbMkKYUXLXyAp!z$q&izu3dagin(5N}Dng^! z?mixP-iG90`)R)G{xtVc&U{m$W-1O}msE%zGavE+_fxxBFX zNC`&bI4KI!?v7O!G8;=*&|>f73@GzmP)WFYMse0#IhlRM{?6744w#61Ox8N^+$YpN z>WY1E##ja*eXkGG+Dcc14XzA3tfvMCALxqwg>cUpuhlSfUDh4D(nt~O(~p(E+5W)M}!) z4Gt7xq;k4QRqWpv8o0=T5}6Jl-zB&c5GIusq&7EOt4Z`~Loy+OZh(l%B%9%{edyb3 zlDj;V_MyBBkIO79{AOyt3K;u% z3932Ot(%lPIhJH5c?^5Dumb)O^*D051Nbo{SOX-x>s(=-%@jl1`QKYiVUEomhN7?= zKY{tQ(=5M-=%02}MFqpU8ek3ypXM|NC&B+hOGfB$xn@7=-3wNicp zTeVEmjQwnBCV>)y0eqUAGf5d$tqny%}gDl#nH7NZ9L}E(n))Qhi1d%yhsU; zTb#3xNlCTt@hU`NX)cNRsz0eXt1}+;g&b4fc4y?-OkZqmnCrcc>wM23$l&$Fu`*94lTz^@xn)qbSjRtq#M+y9z%NvuJP zQgP`?jZ7T&RP8<&aq0?RMdW+1441#Bc#v}7a9{fv(G7h>yU}Xl#_{7;=0c|KdJ|OQ zKY0qmhvaUdJjEmPPkohMr_t$1>~SQH-S>Sd1Bow05=xQ8Dv#>y4@lBk8hQK=JQ+M- zYQg2-18y4RSQNOaak=+S=bp+PK$lm8#V*Tnc-6V(guU!hELq^3_6Q}N7<$lk5I4Gz zJ;t#Ve<`U@_!w$)F3VrZhz69J@>-C2+aDWO2Ak4{-?A(S_1DHXYZz*RzUJ`2bhwF z;O?=S!TT~Rd9GDV(pMBevUive2h&|)I-feE9j9s?hz*o@)lDbAlQd+cbRFpIQf7EF zmtnwPGTF3)Jw+f^KV*IOFsa4BzBeDM6kNZw6THPx`WUFf->7aKRECZ28fF`fUoa89 zo+S2D+A3?1snu(8tL_hMk3c!6TP90#y?jObcB#CY)P7pA&|=XT!|=ImU!$mCtW%PN z1b3812S&Y=k=2k@igl?*%VFd-@9kV_R{;M+fmJQ><+_OrXz*_=7za)ZDW1fGY5H%H zcfVo5dbe@naXq4TT~wX!^jfL|8mZ|kgYKlN6EYuDO5}XRpEkK;?m*DB1c&FKQwucQ zY3&p4mWRpLwPZCh@5cRpj;N|*m&2qwoD8jMr94Yh?;>hTi7|2(A1shHXp;XeZmG5I z>=%P6ESaLi&9Vb~xQRz8Yf$Wu*2^5o|e@){|)X$_s%S zMw@+vLK4?{tAU#sLtcslc4h7Sq-@uU)T%yL- z7!ePc??}Z2iSEM&WD&Uemw-WZx8AO?wC81QpD^)>EZl3}mV(VwoM)-!Eys zK~d3SO54Flhs-Tlz=K^Hsjbn85IqfXdA?s*YQjgd+4VzV8tNJ6e7*+Di%Gm3IO!Rh zx&19nBVHh}%C5zVLV4Vttal5wKD;K77mtel{*<}%q3*Y%5k?{Dp|j(G`5aMxKZ1Dx{n zUZRfWWJhgyi3W}3J*bW3U&V{71X-^7IzQ47(OrFu{cUdqHUbAK#^bhcARUIMbEx)y z7RnE@2C3l`*Px-fUZ8!U(@sn)?dL&#EH_ZI02i`@{jFWuh-hK>^+ zjef=vdrWIM^B~OYz;))k@B)`;0@4I*YF0Vi9IHtb67*Bu-4sRt-ZhIi^S=SLp7#8B zVfQ6#fpIHpJm)n?8FcW|u@?eNUydVA#7RT|$IDgQgTbd9*YM8oAUndsog z7b-ewyE(ExN|(|(1Y;`j%VWbzr5VWsM?3~1A8yj?64p$H+|yVkV#7l+YbvY4-C$M! zOqq@51qA;P8o-6IyCpLaG zZ2gIKg)0++^IeEx3>3keBxera@bm!FQi9iO^OuwBK$1UF3^mj^D(=Z!Z$#R@CIEt7 zRepY)yXmA33Le(Ia*w|Ii)2=eg5xw?^38{o5&~4P=wD$UV{*k_!W)dsnS8}3Q>jkz_;{`uxj9GA06=c?ZAZ|8tZ3J98rOk#JLblB9l)uJcd+m z+3%^4IgZVNZ`Nv7+&m%0nUWRA<-f_l=@~1Ha915X9{k7*o{*6#G_C_Xj->*H=5O4G zJ=m~tn12Bk!zF@?EpEv;^87uDQvgf5e!Ex1MMhG>{YD5;-^@PuxCGy5a%ynD8Zc0q zG^+M+537GQY1IAO3ssRHB7le+d7oFHEE`{OsjES0U@^5}+MP>^9|7+%{J>en;CoEl zWeL4U%Yj#N{8+~UL6<#rJ`iNpW~WCKWJx}j%W~28?B9Uhwv#BuTqIQ(OB$!Qn(JVieF$b%)NR4F8}G|z$$orB9meOVD+>_ zO{=LM>h9%xp|LpYvsIi!cqfMPZg+_W-&K9;h9?=bj7FsY1i(XAbdn01DXWg$K(%cZ zYVzh#3hqxcBKWt&9hgqb1N;YWWWfWNtj&iz?8U3$=%U=Af}*M&KSnjljXzxcUYycT z837K2AX><1-MY57FUGN1E$AA+1fb0&I*^ZO^l#jz93-`8h>@iym5OzzaOlr3!&Gh7 z{T~rtc@%j5#r+k>_e<_y)0GS@jaPl`G)+vK)1G4R@P#S3i6_K#9}ORf#VQa-GxB$i zJ=eCgC8kjub#D1KsJdU=RgdnvGq{LZqHY2SdJ#V*16G&GlYLoGB3JcxGevqQnk0`= z&{WGGjb)(7Rbe%-z3TlIXgs*;%?y?xl)baQpVg}VjYy~f?>lmTXaKEUs~{aq4|tV8 z|0rACd*-^{hCcFfY}=O!{IUU&bYwVP^x!Q`81^{?sNb$*yT?7-POl;WTzoU44}(Qe zm19@4jnyLl?kD8^VLtwL#jlK#O zEu9D2mqMpyAjGYFNek_(0e)bjZb!v1(M?SYZJjpTH&WZ`zJBshCyp%QA0*bYeFyTL zS@#d9Km2AGDAkD9P!4<@^JCCO=g~4DQeKiu;%x0n_qnwE-2lxn9UFXyUVOigpSQza zt#CFUn~{_bKSZX48tNcb97xpD2gzbNc_W!Uk5(6F_f)u(NF+6pVZ!CjpidGPaH6NS z>792bB=~rE2&-y?3JUyMRB$8AFAj^d9|@J z&|dzmsJMtwJ|_3%n}~vVDObxiY`)^_{U>hj8Hjm~1n3T{CNkw7ny9}rd`b&s;0cGr z6di8hY1?Umh^!(ko{q1RSTXkep5LkeM^U*bAE*77=K=IFh*ft0p&3_DbwEYc<@nAn zlFli^g6G^WPLW_yLta>UOXV+qB+EV}3`M~6e4r*L&$}t)D4<8qdKl(tDKk(;UymTv z-1j|Im;n9p%m3kM%U0I3E!^*k{J>$y5lUV>a5FgFA=(I!vyw_B( z10zxf{&K0RPYpMWkoi1G83jTbR>d`;&!W8xt4qrrv`be87j6Zh&C8M_2`T4q77AgW zP?3PEy~9_981(OCw&h@ zy-4~+(l=rmxjCQMITuLgSu?x)(Q4`|qdF_qL4R+L=g7bC zQ+!+IUn2wg3OdEF3NqpwO&7xGo`#dd>R+z82J18QL&H9nP+X9_SUe*m-Z6gxT<9*pZ9br^+>w9cw9L}r8LC58(jCGd#aw#M*L+z9 zj1StzH%hideYg3Q8u8Xy)g51TbtkI?4!WFbw2z=(cID0u>W*KUKoC&bAX6@YPyJI% zY4{M6)ZZYueOSnYJ`k#xxtg8hn;8$3)-lrl(PaAUL@V1VS-JrQM%SzCZYV&pFRCCo z&y%A!Z3S{6`N!pwhrp41sj<~#p?BS^BsGTf-6WYFsaCo`PRyG_x6>HykhQqs!)YXj@tQ%~Y}@}hy6$+k z-tV8>+(ZxrK@cPaF-nZKv`Xwv5UW&BH9A#MS|xVGian}A(N+gKZ4ye2wpR6{+8Qk_ z)uD9I<(Kd8zr1pv=auK2`+m;NbM86sw`hksY*pQ;Tjt{BEa1@Kp_a7e%Z3cK&2D0G z;Wg)%j!svzk_~O3VW=@nTX z4wW*hK7BFcK58MFO4F#%3*77b84}hnuKl$}`Td9rDbb_NFdx0kapD>-t9s2y6TY>8 zjTPO0Mu1QH27Q$G;|k*9=9fMA2@C}yiiMy&$2Swd zKgaP2Bs6P8iJU!^VNQ5b+@p){IS%`D3>_`aEsWd)O?GU~iJA~kQXs;cWGj=gE0d;n z$0D7mV|UyC(Cj^YXcT|O*t-`dfven*eUahY@8kDGP>x4NQ|+BB1?rcV;u0J;{NrCn z|6*g(P!ujx!z|?QiNLI?tg5-u&G$CNF0M|NazO%k=G)| zZ*KgNm2J7_=xbrs=07*I>B;B{yvN0jjc(p}$gv|2#Y8^VG*eL-e`arEJ;R?SX66BT z<<+uaRy=zCJ$hlu80k8#;u-|IT>Wufh(Ge1yOrJ6e96;-kU#EH;$U=}iLNypc_wk> z$>^6R4jPJqT6)+A%=HVohGTHP+_C068=83GuSCv)7E(@*iSE$_ZL;AXwZ=bGS9c>5 zDS!Oi(ajBt{JNEGOZTUD@18IBBK>MQ#Q5n|Zrga2Y@>fQ+Ai9OIMd_dOn^KVoa?L5057dkW7Zkj2xE4UY+484N=4qK@*>J@(=z}G<2vM*Is~wp`$8%Umx`*XJ|0Qnh}lL1KON!#`ttsz1b}g0IE`a1%QpwjJk8Ew zPf@pJi>cNg<=$1qu~A};cx1hb%Gl_&Gh{xKYEBMKC#0>5E18p%^Kj^~%yQac1Vpm{5Cu?pv?};!BZZe_*kHI*v3{V?b~bDRqYH6%Y{6 zg96bY1~m7mo+tE{@-s_P2W#;3EiQeSf#NP^5wvNF{PWr>;RW)YM$2T{+OzI8s*Fpjx zjt&j+9b$Jlkezyy(gt>c-2d$wvwrqN9mY^#5`PU8zCY}#K1WIficDr+N%}1i2B13N zj#5q+%b?kJL~)%TE*h`>$@xZeiQQ5vDN*eGBXtO}16OH*)A+?g4tV%kG~8_>I|q)k zuL`YB%rsfT!f|js2VV1@4;`WvnH0%*#QA@4m^+(?1;p}47{N{86fI%icFl&T6-UR; zED-WPOZdK^dDw>^2$&o4p=@|#nfg1TPIK13saMo*obk2eQ;8xrZr@742+x8D&OZcy_MMP%1Oi2~=iB+n z;!FS*fkWW=o=D`pBohr6NG1FLbp+3ch11XdtP-jbqSr*qhh047-8*fU!Gm%*f23#Ge0hj*eff`UrgvY9~eC&-rF87 z?rneFyUuuZ(G+Ri`L2h%AVYj_4$;w%$2V-|MxW8nF=z1AuNo#Cm%SH8$-;L5^fC?yARlGAtWY_f5{YXI-ZJ!2!LdX60CVWr9ZXhVlhhfghKo7 ztX7SM+&sRxtYN68Ye|k8Ra6{V1UWU4IJah^c@=KhYH0D>9l3-^wg!6wyR7U`(3lSg zy?ai6Bra;w{%i<%y00g%L_AvAi)lzyNMlM{;giiEpL?tqVuV(WP6wWh@a3W05~MY6 zxfd&_iz+G!5}w}H-4PU=2D(mOo-Nm!V zr_)|u*F6G!j@DB>?P@le%ss|$ChW|H?Yc8^PzVMFiW$0|*MwS}1O#C6eHt&;2Gh9m z*yIZrl6sy0Xk_|%P;kdmPuaKM-g$lE;{(PaEccwdUsOi6BWH9WTl`%6pL| z6gu2sK4BBgs$|DV1G(iEF}a!7x0ml#7Claow4yay3cG$dk#*Sxay{p8k7wG@xi$ zn$cB#^tou|udQgSXJ^d>pWni=HAHhUx)7CNYR^hJUesO8hn_q2`Kx+*s<9;o?Kr6} z+#tK>L&&p%_4U;CGakOKqA#LbC>}dlC@Plw>q#dp#`yU4bZJW06YHO=`w7Wc(*5UR zJHd#SC5?WKHq1EkgJriK0Yj0XI@HH2Chy6COfK~ohK0*l>qrVlIpE&eKNlt+G)*X$A55na@e zgyj*+Q2s?raTQDK5x)pJ%_IjBAG*LjjzsbQ(jSy`o05XOQn!<_C7FgF{oBzQBqV&_ zn0#1C)LH7HL!=AH7eYn z`wt!-j{f2=Nfy?j)!Az;SZZSHef6|>P6yyE_dm4mGC6#t^g04~b?GS5ea)#%Ok>d) z8ZL@NK1k5wMoS-=1{;MK;(QH@k6B_vGc5w&M;-UsQW6fhIb^RFj+DR%*`@BzER{H= zV!9O&wjP0iM)wQJ35nQi#1*RB`IavjW;8}8s;ANo)2^^@4Mq#R6XOm1JmRzlM+LqL z@OcM~MRi6*BlUK1h}Od`Ba-L-)^-bSKvWi^^GkD~U;2>BoBd~T=VoS9p% zQEr9_K0JMyRT5=KT|!+D)b_Xy7dQ%UfH%2i7zAf-+1E8)Hfk_}!};R-+~3fvHBRPh ze~x0ke%k#lE;&`LoU4`**?vK(x|LFOn_=?4=G;~uv%WLZcdIHiK-_>-nq$N}Bb*rK z=p}Wp&p1Ri-E@?o^le#!>UxOAfR|F%v7NjL^{f^esJ*&KOL;*?WcDA~u zT#RoMlIm#|-BLJ29cyG?Xt;c(B{-D>TP}-C6YOCK+TQW&^Dt^Pi4(l~lyNs&@adHM z(iMNglLiZX?i9qBuX$UbsT`r0<4|4!I$MqZr!ar!-K`+Q(8Etq$vqSqgekHq)=h~J z6e@dY=j{WSUS2uSZRNFieW8*hbH( z>mi)%x2gZ}e77MNsmEZ{)N248W^rP4p-6WmuuhgR4Eq;c&^E#Jtmp>lAl3mA+9IQ?Dl8Ze7Us~ zFurtEfQevv157Wd@i{IpIfQ4Cqraz+5MRF-g6cS({(dJ71}E&O$_ogLP83w zpa6*VLEGse46OaFpXwai4@7#mJfIx2Jy~!x9dWTL3Q~npeDzciT&m;U8XXo*tpKaYRAI@!>|s=N7cse zx#6&ml&l$bOq9hxB#IfdXh z4|OY0YE`>dFw@bYL=&tK*gUMxXeqoHYnNdo|K>Oe7%u|vat zJmGlP1$e8f?h`rjMSJHyD=?6rUbSRxRsc_j7ajkupIvm*@kZ4IC5C9RG+?}UC>eEw zDzZX-k&ZQmBF(R(4Rz%Zuut~8mn?cR62^&Lo;O9#aq6mJ*MQ+<(3c(|xzPn+27OO~ zxz<44!22uI?jal4(V4`VI7yRo;l9eoA#TNeQ~rnhInbx;M3>W^jwm|aW|6ZZ1*tEk<#Wg^zL3gEmSzw=#zndZadl=&D^78 zs(SrKL2g?t`iva5>P~?uUFvH8kO3CJK_o;L=HT(2BHLNKG+yeLHFO7IN^7?EaD_z5 z$;;Y;r|c5{mP1T&GpEQuqjlXQ82(~xE=pQpaMZO(IK4fP9D*nn-$??8*NEvxZo!5E zy9~$1@l%trM*iKwo-E-~ASCGir(&UiL%e-C!=G9o;Z1jm-Ag{E5h-acdT1~d_^6le z1Y6kzGS?83p8u>N(V)+_GtLYb*fvHMV1Ac>u${rc4Zp)2`%9!8g+0Mk@A&Z82X)2bL%7#OA7td-J6lNb*=H-@#H|ehxb2g+ zzT-R}J0Foe2qYtJgQjAB7*-pP(6vZe|K1Ub+kd~jmbw70dT-f@6 z+CD3El6)1BsnGfE94~UWw1MO+rrh~1JtzJ4;7HL#^7Ra0O{$>lCe9mb3%J5=7s?EP z$)W!a29^Mv_{^y;L)Ru)=4Du!<`dUt56?_5cW+L9d_W_uxpg!b#q716GcoQp95l>e z^m;)DFW=j}W+qBaiP~Oou2JK{$tzd{V_kNQ&NEB6s2&g_&J<^T+i84#m}yP`nZ79$ zc4}WzTQXy|B<48e=NpUw<*iouo>_Uf!@E-S0mq45_&dofse}KT?G+rksAP@^Pu}E9 z487kx-C}8JL5OV%I|6H^cC8?o`vPA&LwPmkfVEAeW2=zF5HvpDPNxEJ1Y^433H?`d zFIZA41|3KFz-3j%)3%5j88cvM z#%&1P#ep@l%i5r3fa5=tLhdjO$h8J>`jjlh0Y}n9)zm7bZBJDffm?g$Jr5APRTW+M zBeQHFQLtEOa6<&e{MyN+d&WzuUb`#-n3Wb0BG1#$F}h~yd4c2^eDvWwzV;1YQj`Q3 z3@d5}1vHRqJMqf;sttI@wi}U(dQH2SqEhacYgKJr%w0@E+x)W++IntDv$U^0oe+oS zxt#M17#2UGrxp7SRN2M=0t=u*-;a)oQ0rML*p;4UTDC#Q|j9kDv)~Ro5sO z9MZCVirw9xt1@Ebz*7y%NwrataU2&z<20|QSOG~7!kK5&wEC{@Cdcz{lq3wwQQeG| zR0qUFFTUDiZLuc73DctmjJiS#-i{;VT@)3IXg`P3O@iIp;Q_zDW3l!Fad^;O4;r71!J3%`vdY1>7qDBX--9Xw*A3c#XLkM51C6*o9xo^Iv@ zNy;A2O>tjO?~=Y|e&UACQo8+tg+SFWZ%tr3jpcz4%T=%kHF5RgTB-{$WBsUfD3CT# zVRr=r1*|lU0nol7K2XUs`*?1;{vVyOzv*A4J+ye*BckZrLpL2N#3QHsJ$rJuV_c3v zkT+tJJ$v*&Wv{#KoBZ9)o>{x+!U#9!X;zs+?(TU2o4GdEou0njE{JAyOLVAAF;8^2 zLmp9GzFt)M&m-D*W?XKdn2R7JmE22y;^@G zz>ieZOpa+2Bu0Iv3RWHD zMo}i;9YcbR-w-RdJ>>k6vQ|Hw@C8l-e~q?sITS_cnw*+6t9!j?!O%gt8sp~CVXNzh zy9*`0M;@6u$Y#(|N3Y&VVUf4FtPXoh{zmZ60_sMr|qdLil9B z_z+gNeQ@b2YfbC{R}`|E_C}Q4g%LG^dU?DVU8lZCfPD7(=w@WQ2_E?5@PvpCm@!cg zNWPPV0hn^kN1>yBaTdUCMFYv?yNRM2dRB5W#~_GfrAj9VVY|5d^w(syxav1!UT+J4 zO9C37^UvMJO&cH$_XR*LHAk!2k*SCVFQaY zisRyI;50n}Heb`Byc7w$>g%~1yX3T$ZBM@m7YzInNZn2O?^)0;u8dGpu{3Klc_}#f z0|ykK*?I=q)5UI36p00{_3b$Jz)<(fv@ZJ(gila5Z7iHV8)53x>TD=w65L5b1JVlgP=o)ez{b(^r%Rf>uE2;d0C$T6819$W( zxkVQW(tm7NlN8GV;QoC}&?JdG$eBPh3^a8i$sDR&f%OTz?1;8XMu@ktl+E_AVaC@> zNW`>EQ<1h(s$z{}TSu~VQj|GZzn`2#)5jQrMnlhPG)BIdf&#%t-iy#)F!f533nMI5 zaW9Z&4G}AA0rU2{Cz)GDc{ZG8K;NYV zAMO51!9~XVmjdLzTq{x%ROy~@ykP55Bcl(nRn;7w_7CRSy!$kL7QX2yOMuI$`C5`* zy1{K8Wvxi;TRD2jCRZW=SwV+tEs+*DRd{r5QGi#=hAOVCR=U~@EF;6EFTG(n#w9RH zSPK#s5Hk)|Cx~oVizth1apVwc-O3_CX%2i!xO+J*;f0ci5*{%is`=|(xbd-kA z8~}Z$>d&kXtEF*O{Sh3^!p4V%^o%m9?2;$~ti=te`pa;7g7r5_`H0I~B00?W9Ap$? zDTvph#>OG`Y6=Y!g6wU7n5tSw8V5?Z%CdyULWAnJRc8QVbqxT6kg}lAw@72YScuWm zc7v?mu23k`OQRvmzRDR`g8WnUF0m4O8({@Q%iZ0fzB$rG7yWT4gJR*#cGU5?Swmbr zXRcFN7Q~lFJf2Yqz-kB5{qZmu(DKk%`J$Yzg3!s5V+RH#F4vBqx2dvHwIG$#Ou7TT z^xd*Njuhu;VtUUDrQ3ReJZb)eW>Lt3z>^MYAzAbqN0LaojzBN}s~{Xtnp99@(Q82$ z^dRvNuj}uTwB7M?ZE1GaykntIjF8ITUK~3$+iwBd&|pnMr2#xry|T|D18HZ~yF=L^ zeN_j|gLz&KP$E05(L1osKG^{NA&?li85Amn(Ra>8= z!N&BK9z1+NpCo@{>9oG9a$9V8SZWLFzb_r?m~{yqDK*?z8>#;FN$$-EOqn(&RTs&6 zUhW>i77}wd5t{fgTOs9T?+=?sBSTe6M5ly=)x8|Kl-z6^Y#@;MJ!*KB;MUmI9dcZ0 zC%)F?p~(-3CG?j?wJ;QD{Kdj4B3f&eSmXXpldY0tZZ2AU^|vpRF?!QHW@mqpadY0APt8vChIe<%%2sOSNn`aOze6 zT6{(pAtWPFdL`uj;(=?vITL|FQYYfpKftoT9~(#gIgqpXuNB`OQq!v{`<-pxbf^|& zwOtuL{HkG5pxZs44ePgpiR5SInzdQ@)WH3p?LNd4>%C5}9V#Aoz%^O%NUytZ32hwI zFDBm2wORZ%&|S^^_`y`JI3F0+c`b2kT<(j$g5vk9|LqkK?om$m*|0qP1g;Nz?eMvQ z*{GxC+ACoNpLru+UhMir!sml^d=9AY@6d>Y zPUZmA{lcX}Xp>Lp$9)E{Nagiwe5uMM%n#Vh89`KSe`y>!wRt)=! zEs%dt-FUh3?1^vN*znS>yy0QE{Sc5GV%G!%Gx?7K--9qJ+@mpBwPd?kqga36hhJ6= z*5|HIOf5=WwJ_Ha#8)@1(QI#fztrw#3pScK1BOQNBDIqrjvt2SXGTuOu_lT?baGsKWn6$+qky87%(r5^2zf4co%yy6XzbB ztY5n;xYXfhORa7Hy@zZn6H_+p8=UM|$0&%<_6i=T%RaIz(C5VaAgRZLty4!F22ubG z>G1{PoG(5KcYce_{g74zQ#q2ckrkb*!k;G}kBcYjd6Rt` z`|?ITA>Qy~m6eJa)=i7UfVP0e54quEdBE<<@Ad+Fk7N{b^q&0rS>em6-c_CUP&bFX zk|LkB|LDZIHg4fPbPmwT*mmhbF2c)SL^&UackPiV*f$a|rqd++=eWPnkpG5D7m=kk zk@F!GHn1K!H~wDtkOv-ri97o~G`jDKWUCXmW8@8RrZ0D@=Y@JuvTNjvPv^p?Au84C zFI=>Qq{?fN%bfc*DHHd1TWLq}ZoQ1e&m3BAQ+J-VFdbZQF28w``f>~M?a4szS28ZV z#bdAdj_9?8V~k#HMDcv44b zN}+v|L6Bx2vXm#V+#)H}i|-$P3KvJ@wp|hKEx_qZ;XUyx-ou`_R}G%S!#HaPNZ;~wRs$qCsZZ>jn+vi^51#um*Gn@F%&Fq%q!FTf*g5C?u&4M_Yw-T$pLc0E zJQVPTrA@MI`t+|%JhhIkq&N&TfDOTkJrw>nqlRguuJ20;H_#EgWw72 zH}2~Gx2RCf-5)935ddK!wd6Yc{;h;Ii)O-%#Yb1pgsAtrC2b*jzXm8Nf|-K%KV?9s z!tddqaJr<4eb<?5;Z8z;2<2pyVUflRmtbxO;3 zUl|3exym02nn~St#O@B9lHz)8P+>3rI=G+lhV;@sp2=ovTM>V!CRO!#jU% z`)=q2nQ058xDTn!;{^=d8>9QPi?TfgM@z!8shum?sg(=tx3bhpg@y{15cHckDotlT zO(S%FDnrJg-cemGx4<>)YOcj6GE7>y`}hGF&&q5XZ1qE&{BT*SOG4|Y9KxF0fTX_) z61;)dRT1N#jXoMVPlF`KsQOpM>98Pur*~QcrSPN3!8A@^uBCk?t~N4A5{|Q|Rj9#~ zZ-@{DjEy_*?1S1vj&VKWSqv?^9+Xk8k5gNv$rs-=d4jX!x^h4uc@d}a>4h>7K&*7` zqP2~C>ql}AM1yrG#pTho))h}@Ps}}Kz(xD2EIZ6d5JM*M?l0-G#u;z77)ESOvYDup z!u{mDu`@&_Q!>qYwuPs!uD29a`OTNW)t;<(*%vXcC^(5cWb$2J`IOW%LZ+)3`E|Ns z5e|bPk4*Uw5%GrDE1w06uw}D1+2(4eJa9##yVOo-{MUEz?NiPf?1Tba0{@YW# zQA_e7k6);grexAoNtaI#b*Y+3I0i>QyAiymy(+Al?7FZA3uAe>s**0+tgbkMy5au1 zAChTLC@x_)N|O7LZqccNDR=UUz&Sw|VSoRk`k`8y@tc zfepFXQa7&#YB!nX%9BZ$%)ZXU2n(ZU+Y%?9ujGW~|LRZF*5|d=I!@?L&6q-(d4dlf zconvD)@#XkQ``F`0|JS`0l@9`@rnAGV~lBYqX@N*?QvmVput;t76|(+ILWj4g93fH zAtbMWhV?XIsaHe$1@qI{{Jv%|fcqGcCF!*{Qu)(kh_vI4S-o+K@&xCi&M!4+ue+PQ z`Z-+YPTdttErIRvmD(-4yl`;G#>gRXd3$^nwgpsNw&=Y9)`_<{?z|?R0ws0^vnhf` zz#MEB4Zj0$7Z8lMMxbEawME&s1R}x-^Kw;u$KTSW-JuDDik|1{eIt>eNhE20Pux%Q zTrR;F`e=5isRraTY$0fZ{@VJxU_i1&TcJS9h9nKfc(ctK8vlS+lu#Am6jIb`dKdIB z^?8z#a8qo5>-z0Qm&N-BQQV}CD@#n9?e5DSkaG^p*)AzP1K?NNh2SR z`q!NnN@Wgmk$afACtqES)G^Q#vLT-C>yU;HD=!_pbcQ>Pu>h9N^|GF6;I=N93yCdN zK!L~m;qIp|T(foWfk5-jPvP3Fzt{9 z?{G68!pz@(tqcm76(#np_uVfW^ufL}zZgBg%}>6@)ql-vTR_j*Tjrs7iAP3H+$hpl zGBgmh*wy5ga{NC%-j$5oQAm#sFv;5T<7CqFHDlfFS6}WQPbtbwBqqmzrOATz;H%8b z5+CJ1IW7>A!R8~ro~1SvGsWFE4c;cEvDkZsObNzPQx4oKM$8&|RmSfWNNHRCiI8N{ zFTtclg!bCxRw8oq#+XD*!9I@P*4}K*YbeWzUHV-8Nd`tb%k(cIWAfnmhhtZ=;oaiT z#!Oh|ZLwlrb)yTcy!Q7cOAnrb<=eTDdtrhts(JZi`Bggj zRl(8mMEQO0=_BYus?`U_U&~a$o)=`h%WW_XX7fr0_+f54KR9fCKVlI*58lZNJT{;H z`j-DcR@?pg$n4x)Z?_-6k2p_RcE9Yd+Qv&e`!0s>zplI7^c_$`%UE(#)L-9P^hSq? zSp~_6!-Bk}Gj%E!oY?FwgjFqKn7tEpLS%c?l1`dAm#6fZkLW#PryY*(hTQaX8@)FQeoRn^V=EV_23M znU|lZ_>z1k(ezkaS9uD!)BN-FA?`#(k1zWT)^m^T7YRUA(rC8YMzh*+`8C!MXD2V| z@GGjq3#FgdHbPx_YN!<@DM6#*#3Z#BB7{;D1k##V10wp(-`&|d>{Gtu0%L`Jt?(~* z9AFlgLS@fGw@sfSgvj-EF|CF#xv?+K%=^ks9_HXu{NGWHYVWrk_whiJQyD;EblzsJ z*Yob@XEFRwG?IPh1cb{&UOkQSm$8zMa4J1^860W`-TG0lRIW zUsVI?o3eX@$Gq$glYKo-CuO!gsYXOUYh7bXW^wM05#GFbHk;$@I2 zMydM14^%Q#Gy2RGFQ7-uBJ)sE>2^xO)*+jWy!pK7*B!M!pYwX`em{bx)H&QH0~_@f zVlVojw)ZYyy;=Fjx~~;>J6AN}nS2chr5F5HARzX>AnvwIhPt}Sd@^;Xr#+(`kY3PL z%ht~#J4AtZp1w%AlkoLp)WwCFY zUh{v&`13B0S+sSmPZpJE>2ci7Jcf)(Xh;`OLL(qQ@9bpM?=9(4R-T<)i$mIM?JhRv zegKeaGpf6t6M7H$nq661x(=>`IKmPIq)!|k&bLlEPG7bVUAo=gPB3C~u{H@769xUQ zgV2PPy_(K4+<6sKX)o7NJ&)2az^>)&%F@?LKa6iDq(j!_v-e4~j}30zOa(n|kF_N% zm-vIzJ9>%MmCYy^Oc3;JSu$rQjLqYbZT^IFOXf|1CTqy8gn8P+K!*o?0U2V~I$8YS zRRw9i>6?)19Y2e)53m8bH7)%oQV!MIYO|~3wkoa?{rcOCo{rD69{n*^&d1IE6Lsej zc4zCS_IWPKASVyGO_GFOYMZs4RO!zoJ&H!Ku{IzwQWqNjSsamzhF-#{z zsxob8#d)u2Pc_-H28WBgYYDN2;1gOtzr_C=RoP~ZOnn)Y6@2H_SRw`={3wFlSwnUO zGT&WagL#s_^PmLLaY%V;ao%ooj47%VeiJCGqt!Z;MnAA!U7%wyiL}2tcIHTR6fL?W zQE+x;>H~teiZE;uNm+UW8`Ss^$b&R~AP^AQh)}>mvk0B$MlMv)x@fz)1ErMbFxg9? z6wT79RoJvvH9P+WYwx9V3%M@W<*TtkgDRB_y@y>MJK-dz$u=u6l2;0MqKr2Rb|7JSEP?h;+r$LSXxw%e?o>i>}mO=~AFU-DSUE z+QVblR>c1?>aE&HQy$+R-gkY9zTq`CU_fDH-F=)yZR2WED$fK#Wa4Bp6~E+Sd@62K z%vW712P_$9ZJMKaQpMz*R#Cg8mwDNUL((_qy|~$v zA?$3-^5Bgk*7BrbKxgNo1qCbHNw;u;ggNn;O1I3gL6qi&m|Tp~$#j~e zf)olUhmvq|b1>pYkc&0QM^cr^l6@DBpHgTH?xVjYziqnu?o;j&4gw(#Od#U=9BkZr z8ms>BlA>V@GPJ)M`;Y?RERooBbBqD*uyzZi?H8`_xxu0SWa>0Fv7%=L{(RXa&!$;s zAVY2aDzmxi($Dh%_A=uwthc>G0@`muPY9&!ZGR+n5v4~5n}rF^nRHOBy@@;EGoKuv zytNJ}n0aJ+T4XmGw*vxoNw;s8X&K8lRpYk)dn{q>_2a}4uI4s#+8c0hp+UZ+8fJKrd7`pD@KgaRGbS*GZnaOv z)-gdwCW=W7pVJd$>!{{JuCNY^K$A@4(8=~YmyocdZgOwTn+`vqBcCSB=NY8qOy)Ym z2rk@f9}cjZ7@nGLLWp+Uh7*;F=tuR#4b_4uDq^SMH=bwN%pDA15X^`G!QoJGIy=7B zA)}vF(2g|VK7?xr>wL9rRMFg5OJ<<~X|5V}3t+q%>y#m$bbgnWCF|xTUaupkpyUq@ zS;NF=nzjWx8oN_?$V{zkic)Mn2hp|RX&yhH%V?oNZ!laf9aBBI?7f6cO&^UFZuG$# zFB9Amr`jmoZZxfyl36uxh7rwLH`r&4!66SAj0(n;kwLQ)O73>BK%YZ0oQ11eC8ox9 z*#$2#?k2T*V5c!h{xjS7z8-haqgsCQ>(_L(DL_CBBoTu(;Eot}lp0qMVHMOu{d#*H z)Dhdu92W9FaXnML^Xi?)1fgqCcv}Hh7PyVN9%#ilpPL&1c3P^vz=Q+G{%I1p>=I4O ze+hRRhH0lgu9`bh zHUsXuiV2sD5;=}}eHMEzy{}2ZOu>eO%z|-I)5j4pB>SZH^rh(-zy}tiMw}56#U;^) z?9O1!DC`13jZ$3Oo)iGNE9O5RsBLnJr6N3rXl=Gf=mr~5vXoTmSRD2^>Cm)t9t&$@ zk5u-ilfhh-)?OOI1{A2M6oB9(%(DIyA58YN1@1}AsSkkZAT7N*ya=?y?ymvTAo5}K z!#=6RhIu(v5zQUOZu6QT^scz-Av}mMP&bcDCEM*vn1A-+L9x1oeLSLud&nr!3{1xQ zSzO&YicriVzHK0W&e**TAEGPPE7dl|=lTHNc2lS!kN!tZ!o%kB4B&E)Vr`%%#pAa} z-SmB-(Atc;9i)3B%50y*Asa3nw$E&bq6T#7!Io>$%3J}yURgHYyn+8dux+QO6x@&x z+sg@Oz_~XrfTstEICtEuJ?h#qhco93l4P$#Bp;i>W|c#fL3#@G8kyc=t?C^R)PADv z7p&bMUU%yb{pIp#N!98zI_M27McI3K+eDlsEV(ef5bgk_%u>z40oW`0AP-8r_gCnh zq>#^G%L*m$wmX~0^h_6d>*amd@-)hDC-prO6EK3hnjKe=40*3|m-=6-(m9<&7M=7P zaQXDuiXYa8l`NDW5)Pi%D{p!NQr!!PmHDhK5%-dWc1IJ~>kdsRt4p1iUNQ-oT%%gS zK*Kj*YT+;&y|X)@KEoe@MC(JM5J>YS{A1eUhh$}!`{?n-ThNCM{;YMaSkWIh=;B;F zEa(fJ--3`O&_-;EoWE;p4ZzBYs>l--dDk+@E$$X&3LYnAZT}T)ao-9svD&N9)D>Xa zk01%BchZ4jI9>*USOR9?;0|al8d%4XYcFLtR;+N?-65M%J{JE{{^)oJ-t9YX)?)Bj zFn<#;e+1}YhwQ=E^i08f-Tq}Li-*X+{|sdU?u=Oc()Uoj4u1mw?bHBH!4RxKy!sRe z+8%NyV7p#YP$kA;bVPkC95nKymXsdWjirf1oU=8iNtj&~Q{MXbG&0PYrN@9KkI!m*3gs75yivJLZy2%QG`KUY~?(Y@< zi4@+X;zajLFq@yu_&FL}??LoAd0fotDtRgYXSa0T@v?=!5Hzkh7cIK$b`5Xu*=YgE61Jj= zgJj#2)Vh21Ntw_%t6M2g5V3v{uR(ahNq=pKOc-EoNcMr za^D#TqgQZMH)VG8qq2K-JhCe=S0{}N+FuTXUv<2+ukDQ%>ub9w`1vlPRDXS!JV^fM zD_gBq5*GC=fSxZ*Qk>`P4#1ELi}GRWmIW3Ajj$)SXQtJ9Em{79*^v%qou&6>Qkgf+ zLje$63Ss}4hRwney}I)qExj)@e;A)WAz9X{GA}VG2OJe0e7OHO0yHpH!MvHQY+VeB zFtoU$RHo!9ZMR9Xf#sK?6y!@J?+l@>gq6&~u*5MCAz-Id9{3k&SwmCnu;`^%dS&TI z9{6`BsKby>dP*n}%SPxT{bws4J-+i;^s``J{%>?@ug;%lO)tQ{0OJuO)N(IM_<^UP zqI7KsfVpSxUlClo9gL-P%9Dyt3~Wck)nz zCTqk1QH!v{1@Og+D(>?1Agpsi28M)u>RVEF%AyKc1AfS^2gB&YmlSb!Vj=I&=}0;#Pz)gwgn?r51T4Wezf@P> za^JP2IwD-P{g_yD&0})rZ({3oq6mhOgjRBfiFfe8Hr65Fzb0Kus?d!30rRj`UaB06 zZPFe4>GY^WAR%Z7^FusdH=x2Td}mUTh3m*B-G#5qlg6c99^MyP^}u+H(eZ^!-@-Td6$3TeBxbt}FF9z)y`hjvOL1~B z65oA5S;Xs7R;x3;iSC!WliIU)d1?AcQ)y!!O1^@`vqvcbFJ^-H`2ZAL^{@43mcM`% zY>Qd`6!Z$V)_IDRNktby9^2Y{kEPwpGS4{6-J~BL7*D#4P3_)YJPN|nbBEHntD*;x zF;avrWnwBW#ZPX=YQen%Iavu%VTWZ~FUgR$sqj$bLx8m~5<)*hwRs zi*9SW=*WZn(=W58MVvVV7R=Wee+h$^{psCP{z1q)+sSzUd5TzS`>{RVUj=UGV$Ne_ zruI;*ZmS3?%!QrT=}XZMIEsx$-XlRixUatGxwMDSuZI-f9%2Gk}a|al*<`KvE1Z=7nRjB3+Wn(f6)cp zVyUFh@GhpQf}z19aD1(c(%xr%IFBqT zW1LC&6k$>M#gyIbN!!8OJ|QkO_9N{#ohetXt)7$vdfrfDd`E)NJPKQ^qH6q)$K5|( zm0!j!ZWLkeRd~0w5tQ9M)qUS0+U~uV^f2P&P2(dA8|nG;Cs21!$6m!BuNplQ%H^LI z254_9zsCq`Oe!Qt^*qH1qHh{+)d>8zhW?1U4w0W+68Y`v>FI?Smrwi)zz0EAW=UhA zSoC0eZ%QtLQQ$pkkr_V--_d8!SLBKhMh{v*5n`}o zKOl14UgrN%bngF5e-9kr&CJYfE;Ed640CO{-_3n)HFvpA$R$M@QDSqQYjdZBa*3#P zAxg|8mvnKd$R!k|_NmZKH@?2-m-ip=K96(W=e%C;=TjL1QjlK=XJ2SlP3{3t$|+G; ztFMaV6eCatIiHNOT&W{B9l27t)MKk+akv)?KMR4{iihvsb%4FfZ5eZpo1ymA_`UME z7T>L#P+p$J-o4}y^V`tLkm)&T>Ddbx4y|~H{m?3!VGxob=U9o9mO&epFm%xaz-M)( z?Q@bNpP$H7wGeJM<@l`GQSN{v-xNivL3{)(R^T+z=kleZCjFv9k#v!Pyo(pOQGm6J z?^I=4JT#9WZx)nxb92%hBqWM~RWKas3d2&^k^hVM7M(`1@AVtryV<8&ckC$y(po_HTP|MQ5%3v}rxVyeU(r%bOAaQbi$ z9joE646(C+`}o*@*|%6Ngs;eteG7j~Qm7}77a&rcjcmZr?$nt-OTp(l=!>i(9YIb8 zJJHO2`R6@904F}0JZKrvWJ%5Do*av%tDo_t#8wEK-Pm)#L!R}ONfk5$(<(wzm>&K(-VF;vh)~jnZ*cSy{w4uBuyVKyHeU zV~uKsgmGZ?)IpMZfiyd-pVB-z{@j3qcxYP#pRgl5GoAW`R)7vZI8?d4!b;8Bm&F8S zq0Xu6ObM!gR`m|u>5>*Qhwp#9%=!UG{7mAAu7fsnXZ-SLK*>H^^3uVj*8bM2ds4BH zrzdV6@MyB|_Vm+PA(+{NAl`31rF7!CASm79;Vj)+EnR?O9DYd2SE=DmY(sVY*U-5IHTs_BPDU(cewPLCp@7=slz+~>r~r5ek!ow?8S&K z>sRKWf!?%IA=Fp;8N4|6XkL*_G{%&sLv!ZP8}ix*&TyL)Ja~CI@C8*63cyGn{jn{> z@lX8)r2Y}s&_YnTITo@&!9PkSKhF{!55Lwa0TQ-T(8FS`%FMQA0OSET5}Jv)Qdbp-jJ5eJm7cZ&#j!A-JF?! z@br^yyXmZbB=UUJ<*s0nOqJ-8DeT4P9(dN$n)vVF-aJ|6Vb{AMpfE=ml4%RWblIC` zeZ6vF)#`faK@)YCes{Z0DU8`szuIY&EG#@-w7zaKI4G#paw9EV!dPNsQpWwrfET+S zcRH2Z^b925=@^ImoN$HBkKHx-jhSW&5TgrJL3S`H>Ady0-6eDZaxhHtTl2Ah&lQAu zarZjf#X6*GXWHVO(NcR(K=a5)#lq?n=T`+x3yrzSNC?BTWt+81_sQ6fD_(W1V-iGpH<#v*l+O=OCA~;^r zucAlod@BeX49Bc~80E$zmqluF&pn!#Nk;!8xHA-=6j-feG2hl-!^3(U4;9C-jKT3z zO9U|yTgSiLTzQT(!o3Xoecdoio$YfGD)|v@EZ(#G8k71-7anu^;B+?#8Y>cQGzk~Md1z$^^0*^vIPB(5dPJGq z;BG1Mk2rII0L6jVO!#MU>WhtBZA3;ynyZhN%aWx9AM-9ctnrubV>u0nKtDG~ zz>`03pu2fv5(1ykt?+yC!8{3QM5-oJ=99B@nE1!Fm#f{NYrACsgCPnkLxibsVPx0; zU}U*Lr`V5NmYC9(uERsD{XFdV`S6Brf0721X*Fwl@TwJYBl#7=nv}rOa!h=Zy!Aa*Oy!1OG=b6J~xxgSGKJs06LxlX0x1D~$qQuz{`zN1& zCZF3R()$wmc!rF^Alav3mphqMTi?I$=iF@DdSJk!HK#nN_N-~Pz!t8i!}Fwi9VvhG zn!j>4_tWgGJKdYDa5>_nVnzoV)QopjJ6gBWUk#GnCVtl_l=@*nclO=IpmS}%>C$t1 zEzQ1gK3y7qVK|wu;dAquA0+b1o4&J|nhjQ6ANhT|Ey)OU5Gmu6M)JklcL*h~Tq(G>CDDsi=EhKV*=NzHH_5=-V_b+RBCiZ0Lx}Tj~!NWWMMWpvd>!vt%jf$li z&lQI7Vp9jo>{5YkpAB_SNj#qIMrzsH#^oj7JKt^m>e_I`^`^6C04g~9*`Jsz zyH-~wtKJ@!y>hBz(e6H7dNIgM#O4mAdMP48P*-Gj<3IlJQU;EM9UHBMh-PU4HKM8dD=&hOJ8L)P zJqU2)TGSb!4pqxVXD9ba9hu_+j=#0dKX2{4&6$8;PAKHc?~AdW6>DKP+;5_WNN;)^ z0@Y`ad80(rmUdPato%_Im!1rz4pAwjsU9w2h>#7Au2dw4>-sZZ45VJDo+UyXS|F^) zeG=M4??$kZjT>hIqymgsv4XxA?gYR#mu3RS`Z-5HU_H=@u}o)>fnHSCz=v!~(6wq0 z`6Bu(BqDBzQxvrBKgIEk1%Z)>05)?X6JUf5PEwNJWV}Xz3L#mqwSxtZYp5@#);>G$ zt5_u$bO5@FZh8$-^6Vd^^(_)DGw!#}^4l$@044HzQvU_@*gz3i-u+UDR-fvK?>L4A zfwOc|kAcesVQ5A}R=)D|Nl% za*lBwiK$2Ux8nW5L&C-rPzA{KGb?8qYg^T1jXI><{@xMZcK25#_)}Ga(A=+jTqHfD zJaC$4@ADnGr$=W{-Mn0@vIN-OyHt3tH6Tzx0%KtP47V&Uyvhmo11{x@1VLAeN%FaZ z!b);ks!wdwyOA0)iFh*GAV+tDg7CqZV+WiI!pZ8#`{R+qHKLB!Cyr!%{EY5YJE*W(Sp(I zm)XKG-RE{+=n=@NR~AZFbJblM$&w5zD*eebX#Qc@H5S8SNeypDcB zl9UOe$dm`Z2Ci))WMsD5`^oI3OH1Hoa4z;TAY5k{yjbHSkiiJ+6XOV0$+b@$>=5-j z<++r;tm!uLri_b^17-g3-Zj~(e%FMm3{oXKZN~zqwz5IR6KTp3U3gG%17&wfK01Eb zWK)-|_g(x}qc91JX9;A|Zk%9jGU73Ano28e(3N5HsgcBoTFha(7_{`J26?zr`1ke3GjEm(p-8 z6A=_SF=uE7*ggCaWwonIzr9N!tZkG`sBc*%YFZ9JPUMG!4(7ZwI07DyMF<-a|DC_V z0#7~e%46m|Pauh?=Y9yZPYUB$B`#Uq--7q|#6hh<>HVOAfRhp-Ntq_#_-71&e1UO; zInyUn+Qil(^0g(=fo`s)`c^f={iB1V`gS+!z9A=+z8OTSpvY31p%g0J|JcS^{# zA!x>vd9d`R`)Sv|M46JX%AE^mK6R^QD|1UDQ^<%o*B`f1#Z=K(Ub|#Ie-AAUz`F`# z8zWv(NUN6kxMd{CDCJ-_edyeNUk}=j0SOR3XoI%u%KDE660d=v>ZcpP;hnqU6{$&H zFEj|Gf`dCIA8?l=iz6|+ox3KL5r-U_GN+0bm-_bQN-vqf;{^uzPDjBe95)pMmud3k zPHNcie`ma(9}}X<9?7%Fw5xY8j2{`3HN(^u*`!3Ql^uySEO3m!XJpQ|b>+sKYVLI# z>fotwNBrp2z1(}lt-V1m5!4QecGdD}7YEVr9s_N3c35=?K{kXw@vFRjKeQgnu3ytJFCFx4&cZZt%*#MW1FPji)D)`{DK;2h?hHpalmZQ-3_1q^MsXMIY*!Ubgf@ zPGAU%wTjcbT7M8gf(G)!3FH}hqglRfXKTvwFcY*|r?S*<%?Mjoq&&ii-Hwp~2d zIEB|e;hR734~3U1aGI$lx0vcRPN>hB4zt=1+f-Pi+b8q-(?`xEPxD*dCzWkKnWa-54JXbP-q+`-D*^C&_r`9Km98b0)siu5llk%lhdq4 z?;_SrV&tJg{LG^21$UU@fbjm1*N4O)`EXIbV9NE~i;q0izp*{Oxd1)v8<;5|TkdDH zqV1SCh+A@U1+vctN*N_pyr(25Cp!oR%AWvbf|~8GUWtlBRTH>1+LX{@Puun7fw^)^ zQ&aEhYca-3HYB@5*9QTBG7C4Cm#Fe$&sO8vMp}S(dX7m`nx2_AXwVx(5H7MR_JrKi z-an?Rd)DENU;hB60NaL=G6_0$2{?FEH;5A?BTo-`WKV6WC3Otqzahl)e8*$vVkMI$)$w|DZ z_(JuK>SNI#-loSK3~KhcCi08I+LO}9WfIhqNBc#|BL#=pe<%9LS_Mf^)npgBTSMEy zaklk$BURX&Z zgBT-KjQ`}%+qfMRVZ^R)$Jkwxb)}qbbbsi7sQ?s9cN?bxOFR92$iLHXL0_PkjF8D` zgNFNu4`sLP{61eK-o}!$hlqOvM^8rDd(p9E&HaYsFpTcuqSyYSVgubnWn|$gn@OIB zGGZpk(xxf)aS+hK@AxvX`!DzfztJM&g$TspLq+3-CHIar)-On0a7MoUw{5!*>L0w+ z$FGo{1A=}@IYFcgOr;_F_*8GPP(9`a(g(wMfE2nG{EsEb1=oKLgjZZ{HE9rfpyIri zAjTyNl$kmRSZnLyX>!vtUFlG4OaU)zjT@alsAzv+(EA0qG za=TAMA%Ze5HJ8DBV|Exzv+Uh^bYz`qdygCOA^now5g#C0IQr7v+||4Z^^f_;f2X5s z6V$-FALO9r1KWmP_Np0N_g!0C@&UqGth9LEffXxxq1V`p1K=r<2keP4o86S{!YNiqGGsfb0;ylMZf6tS!@dp=dRRTTTE*#UaB94~BU2ysqvHe< zDv!<@%YgZUQhx>USaj?25A~m*djW83eytE}EIYGm@Ty*EV5-#DTC%Su`YTW!<29I> z+5!Y}AI@BcI=?sq42oTnQG;i&Z}GGxmyTY1;dz@|R}kb+@*aKo;>F`uH7@=F$fJ^h z38K1O;d93KlY0AX-CZlYsF_{|bEG3oDeC=Iwk~%s`Terhy&$Id&L>TE=I#WULg0EB zDHJms$SpbRug=VQTaY zOWx&+i^oJ=%v-4AjN8P?#&FA#O!w}_puOtf#6-Q3Y-?q7TI9|@RHv0qc*bj`feH+3 zd{+(1MJVZy9M?R1jug$5Tu+htEcpZ{cP**BL694=-824m=a+G+8VHoE>=ED#ziMvz z51pmEa8a!Nslo4UDtv}1ZvKG;_!Qyl&Bw@gE-xppT_Y=VT*6JQNm+Tu0diQOE;p$a zG-@hxS!5?e;|DJnxK+tbYRNAjFWdK|nf?*n(xoLn1|X8$9N&6n)EC4h<6Vb?*Q=50Um! zaL3P1msDY{o=KLcFq`jZGKAa?MroFo8y#Z%T4Wu?ZpEk^P&+MdkiXU^jRjm2`)>yF z+wi7XO6BdJD1-c`5WT^fG{WREMN{Hrzy0C>r|rg|A#69*^5>oeBYi*N>}`RgG11Q~ zt?em712i?=h#_d!FX!4LXVWJ1ukzot9MMH%688{Hj{ndT(Aax3j+SzBI#ghf7jH~%B3!1B);)7P zDna=(@;GhwJ9RO~8S_G7lkF9$P8=>tgp6oEYeezht&h$)g zX+xye*2^tpJQ9Gvy5Zj4@^>1Adz!b6eIOu68zF~BKb;EDj-9qPv^PB(7hvx`ZLO_? ze)%Yzl3>~Ow`62cgmm#8-?*6a_4?pkd`AhM6IWD@s%<}^{wj9g^WMKX?fV{1fG+!U z^!ki5-nr`a4v&9qSG)}=s;#}*9cQ$vxU-#elP!0o0yyN|%7cabjjzE>Zmy+X+tB4{ z%(zvn`+hDfN+EAY$|uDkZY}d2r;7pmS#n&`c}+<9vz2VoBqg|g8T4DFBPvkM>Jaakj)#O z4;uUjM!`YlsA?%$I~widjTfLHOnLhtBo!E~5&R~z%0-IPw+E^~-OG$iYpC*$bufJH zgyAl|DOAM;1~vCbcnj2KT_RBj#woSEN?ehU{;Pcf6{1@5i$OtLi6Wa4MfJR#_HQj} zg2=bda7ZY+pP-QK-({0^IDIU&56sTtIzfuWdfz6^a6MPR?Myqh3gk$4S59(fwcE=OI%s2b62=^ zi}^Q==*_=}0YB58H@Zqdvg!!yO3RY`lt2e$DacpYU>Hw$oZ*a>-ZL)RUSAj78@zrr z$Fu(8gSI8t-_yyLAGgbBiB$B#1o@by#xznz_#C$|b0z(e)#f9edBbO4mpf}}qdojV zn?kN{P(hG4#K>D4_d^s0ayNdVIpdxn-bdS=DKbOvemw^2jTxh-{Xe~4@`ekpSxRz# zKvzeQn{xMxmDkV#KgXDue7bHr8}qYYh^|f5SOURTIhU8f%7m&_$X~D`mu&?Ri<5-Z zQftM_2oC8Z_J4heDguWgywzebVF?VOX?3bCffn}x+9%;7tZ!)H3!l~JB?u%Eh|KCD z0MU}$u)*FbrUPOFzC_`}rREQgKS`tbngVk;K2CZ{T`?Pi{lCEX&T5!~GGf}JNy98K zYfc>Sa8jOrLTl051ElWH2=JZ&ti@Dec(QwX_zK%9K)YuB2&Dy~S#=_n0JWnb4l)czzEr0tOJd)}S$!J%&f6 zr6kL!vI+Kdtg`YVm$BJQcPVhRfE<41IPN@{cMg?5$7Py$CNDWvR?>v&ilrvG)@h7J zbN9n>>Ro=VfK|_}v>++eN_%S;r+R^I6e&yJgs)Hj@`E?3L7fzm;QmLlOIE#Zu)Bl`3o~bD+4{-6l}^4+0wT-M=h}QtBT-< zA51p9y#J_HUbo~31RZczm-LSHNUikSc9d)}(h*sen7*o%ils;S?$KXTM~O79*9kt z9^|4f7j02HWd8xv)+Bkh8nq?VDrW(<8hg&Jl?kR7!Pt0RSTDnlTNEAd+%F66P^ZTc za`Cd(oPDo$;Euf9p# zSIk>yjpo3U6j%x@TP382V!K6fnd$0B)*^!_RThgd11V;eIfHhKU{+&_?d<%A?)B^*p1A0!WZVngnh=Q5WuGw!zK$$rNoM0!bKO~o)ro3W&LMdB z!!q1Yyr7Uc77hS|<$7k)`z+r$3$)gAiLPDs_}*Pjk2W8Qng3*95Gul$cEelrI+&2! zoTW9VU zgW+DO?fo*BR(WqyxhmQyFlYVml2oe{7xwpFg{AR!1L_uh<+|EMHyIPxJjcqq3fM8)^#g;CJ1q>pK!ov? zyz(|7F!8VZgq6wUNhx(i8_3!Jxq^L!X;JV#^+_Pf{WkC=Dzyt$AQ`v5tmLPdY8cv& zB*tM9QRUDbWjcF=&;S8i&_sg*FE;gJYirttF7KN<4pz(+3;;AR1t})T=vE6?DzN zeqiFlV7sekUifo!jnLbV7KA?J{qjR;iA;|QY1#D>hin_9HpP^ehO!$ho5!zIkBMgh zESt0B!b;j6s6T8|R+jl?c}JqD@6M`rrhj{Tmp`FGwTs6a41;&~^n5$V9WTXgfBP@b zxM_9czpvXTj)6sLy@-~h8^(&|w}XDjzRm=`QIQXq5~~!hqFAtp-vfmmg?{Ezka^YeS#A^;l{2`KX%d2=egW))<2XLyME~Y`DbEIXmh-)RVHNi$I4N< zp?3TIAHtP&K`8kk?8#3hem5_ni4zQpYkI;<2^&~?q;nq?T4~N6!5w~1(Dn410D*a`2YC6d+Z|OU<-}f zkoAu*_WEkN5H?3#xD43_cvkrzSC{0&1{KW<--y(Fi0*E}av>7|X6-j>^bNz~X-IXr zd19LQm0_!iq!X;3A0p$iD7)R(*Q;kPZFbH9G42wZu^98eJks?mOpmWN|1~LZtET?M9**$3+b8a`tOMp#T2Jd%Q;;TMa4C3AACbw<~83O-O z@3q(ba|MhklHft9J$ofP?XI4lvPVUTt}Y%}sES(x@9KJ~RmJV<+6dB)(?+LuEq9eU zqvBTATdEP->+7@2(3Wb`yWr*7)iUs^)xzvD*Xe-T>cTRS#ae;KByQMEcvt9fRZ z3W3i6y1+>ppUU>9K;Y&BBBO6W)3~59kWY#@+CXsZ7Dz1{VJo%0y`2ye=J?GE)G=n8 zD0K3o)bi-sZSB>+KmoX5l)d(7SC>GmePxf-W@jCS$UbJJ)+KfhtcZ;=6TL6wMwwzi z|E8}7Ekh#Oke+cHUC_}|t~x#0Z7;_~V8vYrd8=4T*T}LqN%8$))+*wy-Dm3gKZAl@ z%vgrt&%yY9y+D}=;src1(*aqrf5j;3!F|N)zA!9mDdI3uWV3RzO#JyGdaM-w zoIv_CeEKd(Rssh(o>o&A7BmukI3dlyDCI(ER4e`h5ZJAx@e6YSk|7;sv?OL&N!J*6 zba|6qX9PjL&&II==M-sX8X8Q4JDVz85`w}?+^=z|T~Ct{S- zF1_mKEgWPl0L+6|QhgS2*;3~xfIs+oL~j8<>Q3y}~#l=KZ0$9-LBx+j|-u2uWz$2)2Hu zuoE(pt&-k1w{=ZPxRtP2tB$#*ea083X*#R)2AX*j-A8jj^}C>)G2cJAe!Fu#F+xDE z{8KYZNM3S6_3#fq2ql*T`UVs`SMr6W_PfF15#t^e1*Z3jx{UtI+hFyozKa+5u9yM6 z>z|w=&jnn24SMj53N<<6{-v|0@B%>SKbw{0w`eYX{ZH+kg`nrC`mx2R2K@!UAQ7u8De=V? zmFTnAl}wM;qri8gF9j#UOKIReo^YD`*JHT6LcR9P%w;wlQbib7-!w*54nV$j{S(0I ziww#D1z}WWpG%qOd{Wnby>G^Gp(}w;Gh{Ichg312psU+?uN3yGZw89M;LA?2Cikj4 ziVsMp_x#-_m>gca?e2!RWA-X-m~yXiTkPJ&Yn&`0GNEZozAk=$KI9Dt<&8dGl1hLD zsP}8L3v21w<%?VC{V-Pwo62(r-?eM^=@1#IYDyrJ0vn8ceIo07F>Q2!ZtHzeY%Tyt0W@$2mv^w>#C{P ziVKtuxR!3a43GF#H*|%M6D@DgQivkKCaVGB?q=9U+yn$^^1?4&(gD->!RMBJ_Si`x z3KnVT3<#Q`-JI2Ir>ytiLWnr-;Go>ii+8=}qh*fE+UClOufIbWI4-RLg0{(KcHtGfA z<+14#!28wVJu#2&%7H+C1z;aKJzpaJvw{7|wLSYINU_pZ$py|Sv@;TR&#e)gdo*vt z`r~Zxf-t0A5+dHtQ~nDB&7bI1GKbI&b2Hrsc_T=P4iqR>Q>fr)%BQq&`FLm8DYPMs zqGFzW0TNFnoWxlpEd2FXK(Z56s-m4~b`HoNdz1@vE;3`vg65O8Dk@6st^(B%9Z*(7 zjVFoI1T%6myLQCEC)YvqujX5^EK00~Qk9X@fSPBXh<9!YQ3PkS7Mv}>e)LQtwsBz5 z6ql2N0IaR}v7Mo`H?J@Ekpv+-j$#uKjaUtn1IeeHd?iUXtl5hu+%f_X1z#bv=;>3- zk|>B*d)I-z<2ErHvB&|71#Ve!!0}*!cc(|hrN2I{Gz*qpEHm!A0zi>{{=_}vop^*r z*g(DJ;~RM&JwZd_gX^*>Dnx%WX+>a;8S zp~sXRHH?!(vf7H9S4F(Gdme1mAps$t^hl=wGl| zmV{x!?f;Mj-S}xYM}BnX%*fYSdI-LqzOJaUb-(Q^n2zNg!4m3 z+f<(*NfYYqOWV}cF@kvlrV8zhRc=dB^*zNCJ_l zkkhW_*>St2qWmK)!BZBc@OZ$s3$$P1nsjv7Kty9bZ%U)6_H`n@oNr7{^gdMrnM(0R zJ8IBu!SRZ|O655Z2E1WGu=B<`{mlBDhBag)fk3=(NWP9eZ5?QvEF`_EcZ6xDA#_`R zqCzO$juNl`Sm)xrpiYtde?-=05~MNrUqAwFTh%ZymMLYAl_R~H`W(~nm zUm!r&6T>ca%&)XlOjHS?ChVhTgxrtlm*)LZc=ara?l<2b3W? zi*TBtoicnnt){GLZL}s!2dQkFap3rc>3BOQb1Pl~Z%N;^px56tr|{^4WNfvF#5Zxn zCwO_dK@WDLQ&rTd>}XvE@#pfNLY7jLQoXlhe@h{{hTl$yHY)DvFAV&hL#h*8xTH3-3GWpU@7W-g~E# zG@oG1rG9VatTQa-m^8kUcd4qrh-VuAeBOTSHP*%v;hX;Pzyxp3WY;5x^pHllfR!zhP<(GhIKY;>myefR-Zi^%F7(og+ zPk5Tbu~LW^(V|unS7hHgNNM=+*3s6?5CTwtPVm^#HrH@;RMr8FerLRf{oxLmgg`Z5 z^s`D++Z;8vM#tA%pX`siX(hFO>KPPex{+HGiHs^P6#CY>Ox8lq>~^`Ly*a%X-%IEv z&gM%<>bZVdg&%a$B`Kn-;6a&#U)|#!zFR7V;-roC=j|GG$FFFb((I{*LbxhJN|o{R ze`zPwg44U6Du5-CI(dG_pE&{%DjB>3Eq2SH2M)OE-#)w_&>VX5^zlnt%P_L=o6=c; zg7mDCp+*B?!)nbW1p!&XTEJ2B&ai`}eN`Kl;Hokfdp_qS_ks5#Bj6nn;&&#JsdI8V zQ=QZ-HHVnb7157iBD0B>q{^ELMTz`ct|c@wQrjXbN4=l|%ou=YmqOZ1Z0ivSv?BAY9J$^B$MoCWcv$eR2 z$i6H+>}@go!>KVc&lR^0qa5Kno+QnjLs_Fv^kak!zX;f-CGgx2F}fR%o;ITYo#qjoZ@--{dF~W*#9cTC z3$h&KV?LUSxt$%mk$-1Y)VjzYBF2!Ir0GnBVk2<8@UWS^4LDl3#iz=n3lsKj*wPsI z%JULS_u*%{jq%ukK=x5_tI)M|nGN14Z|&%BRyM+J-{dq9anh?~T@BN8e`q(wM%UT7 z!cxo7!*meE(S0IJpbACtw4PZ=fUEO8PdZdDs6C1PWN&X_@Dq@Aai975a(xC1F7${` zwtJ?vF*<1m5-TPNFXpwvhGsz}e4E`$Q&62WmvNZvpjAy0mjCpZn`&`p;yUpxdo#5t}+{~5q) zk~|(EQ7%1Gi9q2vE19s3-+43-DL^5tX5ffMwvC*yzKKWz!R$_o+(B{i$j8nS=tN72 ze8bTU3H4_?7%nesGsW*YCqHHd2DZnzc5t9>1$q~cHJ~~YdEw23^|Er|0lK5*)EgT+ zts8p;zKO<|n_H98r}1I|*sXKBACH2>RmH|bFj_=k_xl3YP^fB>wcD$xBixIql6GO( zvDYsS4um-97PxuN_~C6XlW*d~dPnOQ1N=VuD_*8P4!ipG_XPJG3F8?AsS7BJ_I!Mr zQ1Dg2fJkO)y*dbV)oD*El_BL>ol__s-C%0(D;r9`%aBfWGBEV>$_UZW7BMQ<*k^>| zaY14v1s7k=%SBBwQ6VElH{kLM3p`dG?BGrYV>R3XTYm|&>T@QqaAN}6qkdSwjK}a^ zDl5^HX^WHA_a;q;z4#s}vsUNNRD zFy<8E>L+xNCY3=~Y9NnIu0N^U!B&M}qYp~-q$(uDYdUB+#reNx^s$DF5KF9{lutBX#0HfK*ZJ<6I+ts3b)mG=3V+jYc;^i&7TAMf4~7!;Y4Zvb1LsVo zR%L9ysPj!eh$_87cYBRNpSG?Q9d|d{rWELW8*@pfKhL;>0VNJ z2`WKCkt@3Zq<}&)N^oFAKB-49D$e!dCf!{5x`5rFuP%xh;B){tDD_O=x?q?7UfHxsxEL@9*j6ZK(SleqBfZ+eug3z!dp+T8PK8Cr&i zRtDcPz?!Z~OBZ8NIFu@d-tofJK&9-$qdy>qM;?93l;a=CW4eiv9s)fV1g#{^ek`hi zgYgH+MlmYb*!I8AfdP)%(rJq=wDlZc4H=j=G_G*Rc1?9+Of(+_QZ@Wq=x6g?^rHle zt7d$VetCsxsOiSVGOkzA*?R=W#>GR_NQ@FFnO~R%*HYdn0nF>0J zxPlVF!aJw!OCRB}W3CU=d4?PCBI++wx%H`UrI)Hy4RsCLeH$!EC0YJ+@=#j}KrGo1 zne6yIF8iX}&zQ;I6Es1e>ClQMw+f=6r(|{Zm1U_clGU9%{9hRouA)?Gq}4sD?Mo7u zsV=R%->@-jYz%Aqo#4u>BI!})XJRWX|GD-jUrjT`<94%96NTj%Yre#TfrqZwmPN68 zz8!LHseCiZYVPU>FC5C)R57Mq!jYs)N(jGKE@ZP~z?-tm!kYg2sxn_%c>ebv?blkFzA%;~nmlm%=tDb8iBJ zkRN6R^5vnFa{4R}d3ydna+MX{m)CVm;{-yrR>I*;u(B>KMJ8WfUZAcgM zxFm8XQ`n3LgqV-)6P}aUT|mG*jlV2Lo=eNv!DF(L>M6O4R1#-T9`!K)m81UgBg<^h z^}sYrRn{!*d$FLSYStVqYvDz}Yh>tMk;1w1aJoydqiB-xjY#xBnsDJ@g(D+P=k_U& zGq?iF1afs++JuQ>g`k2bFQ`&_Uz)&`TE0-G;zEpJ8cRYD;&lmecCGRXy~5l73SM{rdR<2+CEFhl<5|kO0Hbfai}iL}C>|AQD)$=S3hDd|Ij0 z3wIM{c|E0DZfkW+{aCkrH@=$?dXgaJdZ=3iClwm(4sLmS!{dON?YjC$lh5VJW)y5b5Z=lg2&XlC*=qK+RHE9qy!;lINboB@$A3@~p*ieIR^xNOn& zq)iiC%v1vw3I93=frRK4L@Cdw8o$ju+>~_cHQ;{~oohVP@BhblHalY&hK-qFX0$Xc zW=?Y+Gv{L=RB{+mV$O%f&zUHPQt3=dN@a+27A2)Z>4=gJUrPD=-@EQyH$Kt~E9qF+bUPl9u*$^%$&1b;);@v5|Qb3zb^$Jel| zN`dr%^0Kso%&Jjl+9bV^w2G5r`V30{O_uybN|^N9<1bbneIp}VxI%qDv6D2zPh^85 zn)Mt_B&F)#f88eO*Q&Wjc@#UEP++_N%x(qGhKfL3OaJ?z{K0ic5{)^k4Wm!WZPYOd zQ_nwh8>0OP8wP+_4DG}JFtU@zM;Az?r#LkqUf{sG5Q;g|^Bq(V_C`fTQMr|>{f6|~ z>yd>cTnr{KH<_-BUoq9~4z_U$!sFBhaOO71!GnnnD_;0mzhU?V>OLrNzaxqc{Hkmc ziqi;>H9=piw8Zz|pfA@3!mVTWhRZ*usuQUVQ(Ut!yko4s|Gqt#cuEFeV1>ies(^ne z4x!4h#@PSC^dS^esO}TD3H$bk+_bEVn0cLcxRMu;w^P99hO>TPPk-kA_dd~0LR>knE!Z|_`aObB^vH6*6LyLB8n zvNb3~tY}?i2DI`#@7+;dABU|i8%Viod0p=8!Pmvi_M4gJ3}SQT6aQ8cjnq%2JTFGr;_nBSb+e3?FTkXobS`z>lgL{*^rX-BI!Pt4YrlXcCH=q)WOarQd1?i$6e09kEFeVM()EUNlX5 z_KsPhDd3iZWZZWcQ`0Ol4~jU08|3pZCXKt;+)MDDhqM?e%18`HWTTPe{P)dGDI}=7 z79vSHV|H0D5_Z6rfssNEb}TL>;r1Z}1_U?2;$CIk#{63;1?fAZ)aEg3nPC$;u$A1& zF1-JhPz~y}@E(o3pJCsYj-qJ-#cHoe&-VF2Hp9$5-5vELZL{z{3RZsp2FNXPRG~i! z)>A2($6Riaf1zJk8GPl>-NsY3nBD^^@ZDDKy-=|kqO}_4^}n_w-8Ulk@#toSM-c^Q z*BhCf!R7vWP;nqjF}r!`A5^^Un2Kv!)K`I*=(z`m%V*zVc_r1UkU)i@p?aTnY9@y; z7XHwy2ComVrfP_ck@YZjIA8?|8~7+UvITUwDC)JHd-SjH|2L9K*EJF1$&h^~qn! zK+8-_7n`df3ojGmHI2qNH~xJjy$17g0AZHd;~(KsxMyl}MGW}PsWC{Q zE$ohLZ+yJ=362q5NlaB1lj#nqT`J86-WjQ3+D|yNn zl{xq!SeKNFYib>xkWOt{1|R)6$=nl%nLk%o4Ir!ES66W5mw2z%Uk>s)>y8 z;Rc9Er7gJ;`m0RxrM)_&Q*aU1W$#iRqBc6YU<^Aoxvi&ZO96R>B%paCHa&4l~M^j zCBf%=fQq`Pk;$(85yx>`l~Ykp$~|||rp$A_EO#;#zloYTr;9z6juM{%1df)BhM4=u zNNOpJ^cm5m!TiKv1dgPd_Y-J59Pat?mqf)n#7UmIwMB~ccmIg+fiNB&XCsYJ^es8f zV|#Odqf{({*NDQ592O4;d*ScVdW_1O=h!T$S=XZ{YpN2i!% zBk6sRjdJf31`$U{xvbvaUd^y@v)*gW+`sQlzr@7H!=Yk2Osp?Q$o-3(ssy_m3U3e- zqZIFq9`{ZM;o&4AA>S_B1rPm z?|aNO`|^!utI3j&;TgM@WG;HyK^)>&FDyJb(@E7YZ&e1s$KQGT|UNmh$%7Q znRi=3j?fLMaV}K3!1u5hwLy>f@ute=VXi(*cJ)W<;-$b2rlQ=- zT%DgWKVaOu_M5}9QJN3$MGP9aN`Ct!ffQm4RlaJW#)>3A2vr(6cMG4xs|`0 z@G!^c^J~D{E6-Mruzd|8@NK19+do}~coo-LA9CtfnjZFss@0KTZNzS>?S5+Z9~I%7 z_W!}Wk@NYU8M$fId*;g0cRTl|q=7f7$|g6EcTRufiu~56WEbPdz(sQZu0y@Apijz* zJg1$YhvfyT@(ood(>Z7iT0!y99lX7p$_rIn;Fb5j>fUNjniuQMh=;am<-*nAt@&8U zLpNfCD$VbxYFFjs4)~31k5|&AI2Voin(mGaP=WB~(kw3P>jBG?J?hj5urF@9{>N=m zwXkKsyu+F7OuWj#7e+zuzYut6j@&Jk$*9A?1dbG=IeM$UcJnLT3=LDQxG%jdzmVQ{ z8l#ewBxm|Z6e@B9ne_GY!zcRl>m0E)r&~U(360Z^xBO>k(Y$F1N7M7emLXW;hXPnWBq8Zgj zOWkJ1U%IjMK0?l$qosA*g2tkagUR#b4QymdPMIZS(e^*IJQjdSRTIl0EWZN#1e~VH zYV!+P+KE*s=H2D@%UfffZ4^qy+F!~%y|f6c$Qnz*err_D1!UEQbNi*cl2f9iVZwaB z8qyVQ5$V26{ISyp=^fE|>ScZtk2;X53@aG*l=IrDk>Ppm@oRY(2f!Y_p>IxyTIuvi zeTFTCQ{IweZJna=_r1A?^?{gd-pFYqX+I+ByG`|WMNZT{S58fNq#DNiXfsS@T zcL-n8U+;&pbyye@lc+*}t4HC`ZltL%a<$h$d9q_2@L5|Iht(|Hmm~5uP;B8%mF!lM zdGRXyw;%H3jMu>NlQ=O}&nmcGH-YKqm~+hbSFlAl&`pXNfFB8zDF&Z+>-DdH zYcL=HNS+>flS`6Q9T#-iYBWF)9XW`Oa1DR-KnG|^zbNh!#%64#H(yEgh zFZSM7fgulWR|=UBh%jgxNy%1G70_2`u!-^C+{Ff}5wnM7oDl7m3iLY9q@u}0aYK;8_j>^z+(0s*r+EprvBH_)g!wu8KnWVf%I~t zt7`iC4+2v%`}+FK+y^LVZNFw`s-j70ynN|V0bwQ-Dv~q6ALfI$D;*0zi`;Zv*H>vR zsRB+!*kpvVof1S#I?(S5%cl~TH*89HTa}`Vc^_L5Zb&noDkb+=z{|;Cin=JM&cKm! z4oSzexo-!xA>0${)9^^dPvg7F2ZZJI!?mRW`0Mje1qzm8XlW+eKEo6x7amIr=!}QA zeG23=7A!WK zDlaBVu^yVsdK4)Z+rmqTD`ygZ>%dK^|22bWo;_J5igzw~b5Rn#_x?~|2hK0#)YB=;sb#-8F@P$Q8t9(UE|h9=ES^7l86_ zkqG%z9o%prPphLI)cYh_73Ma1kA2@Ut~~V^r+H-fwx1HDCUh3-tIlV{K>aD#bA7eWtRGz5nzMKl$!spznsTo*H z2XUn;upflI4u)eT@)dOG(lW^(BQtFkh|ByZxfX{1)F3M|Jwz+nL~7oh;D*d3_qbJd z;`iUwi6fV-Om2X*Jp&2czPY;ix7lN*c&N#n$#Pt=)c=#k)dR4Py9xS9zB&>7j4$<< zlI`9e?kj<1DT#51>MkarI3SZV7hVNSjZ#uowP5?E5KLMITh~o1(CJ zylNj$|JJ;8cxU`3Rr)44JX~p!HN}&rVm=?5-(K~}`SWuFYEak9;cK8o3T_Y;&{*&i zHP6&PONNg9yT}1RB5tqZwq1?zc$+hfHEOW8@-dOjb!B2=qvkR6keX7X1_PZZ1H<5a z5=DML*L^-n|F^8ao*h|qy({ONd(gaVKeJ{T{u~sjw5_ciO-_;tBQ z#J+v&q;qJ|{j3lBSULFT=En))e{t-YdZk zZ}CE@SspGpq7I!qU|EmWuovjY9e6;#BAh?Ewfu5>B)^Y;kKcQVU~KJ?RGDmR3Zwv4 zf!g6moe`e+e1Qn}@FzfNtv}I1fpi%BTpYK1A0TI~Gz2Kr1I&8m6R;VgD~i{O9Q>Rz z0kso>uRmNiq#Nn~;p7}!(hZt?Pn5v@u6=<=J(Kp37RHt@lAg5t_;l1hzg*rXw{3mk zTH@JcHDNvPjN9f8DQ+47ESDGgerq z6*)@f4gGuAPC0QeO~C(Oh>E=H&9)A6sUxfXofj}Dq=^0aVlD!x^X9lMJIuzGUQ(o~ z!M3nSe>6Su;8wYGE~g1Z|B;)AQT>D)*ko?4PCA7nuaMsk$7U~dY`U(4(j`1nMNK{m zF`J4p6!y#bDBL}Kh9?xfli=ZrCD!yQpt><6Z+kw)U&eiBCv>K5o&$hkJ;y7-sQQP> z?_QrEGSLESf%#s zz2P3%NC#ifm@kG|cJ`QE4nA%PC~V^uvuts-N1Clrw@RLqk&>-?NR~FA2dI~k%_52b z%nY{(xyMIZpYRR>g+3!OJS_8u3mLdG4M#ybxP4RPEqY6tgM=*C@@lxp&X{znPd7r zdEN)V$cwZ~*=n1z<=^-3-|bRN`6)&YDSDnM+8vJU>YIn|#I(T&lyduSfp&X)Z|NL% zO7d5{1k9~~_~J?i#peLI&Xs-4Jk*#iN<9FkHV3!ch=0jjYHSAxz+??fi;qURu$)qu z*GAwtGFmXKolgdLy>!M`J-oHk_rX1)0qGvWubvdRid)CErV?H!Yu-g|W0j^11l6f^ z2hI5A&9_g9ccT_AB5lnb9d$dVRxIky-RPL>AF|fmO`VjWv3*o~*q?X>OeH&saGPMW!f`{frxX)S-<*s9fIh z6n}?3iAN+!@fq2{jj*|s&xO&qg#>m!a$ZT;BkcJE{-88vP+u{J7QAB#(+wYO7kt=vfllNgq8Bu%;uzPS?(4Khe|?VIV~ZMVnMi zjGhwIL^-;^gfx{SeE}Kp6cDJbmx{cF=R}PaEsZ?o(Yr6 z&uG;p}7nlV^`mw{Yz?8WdeGQttIbaV+2F{{Z<- zze&Rq9VOKt%6fEfc`wK<;_#N%V}ovbkAiJ*1e|^_L2^ROGw+47exrWl`DujiHdjKu zxva;&#G%Mz2qNQ_YY@+sPemvZV>xoRntLG7Lz>Kg^PTec9dh1t>LGTZn@`)s8joY)HG(YO0FsGpD(s97QPa#i0=id;}-@K&X`s#wBVm z2HDTO!96FCh85Vlda@M&C}c+jZ4KpGl{ba0S|T02rPzobZpOpKrV<`qqA%H1lebw1 zo_>fk8xm2|IA?k|qx(BOZWX?YBPjmwP97mLNB(LG1L#a9dpY^A!;?Ja%Wy&)M}T;v@DW!;6p8p}aR_0;r7M}`mFaZ_U| zvN5`pdWFqI&~fsEDluco%Lg>aXu04#0qxN^-<*@2Y|5iXLLjMsA3uJmRRX<>Pc0_ozs#e7q|z(efHPDz1y9Kbg>6Nxh(N)?@)e%OTiq`<3Lm7M;M?9$Nx~y7H-FL zE;-CtM_u-|SMtruw|pT^wN9HncM2&Le4ZzIE7Q6RVgf&pw`TW08!pWwepz!>J==cv z2x=2RhG_na$u-!6TU_n+iOZ{BU%*up^Ywz%o~S#&wNmVofmOGqjSD9tiSz`9{DZuQ!EwG(Oz zAU&XxVb#4IorhWd8b)BwbnkU~0?1eqoXmhMpuMk}7%(-GgPZPcGKHGKHf`a*Tb5%P zZq`U=JnBd`hi{Q5)!a*FBP@&*90p%oY9w2+@o_DB*c1j-QtcUV6dJDA9Mm5Y6c##H zsU&K)NA224Vgj-bcDaHg2&5iBKjYK%z_$&hq2L#MJ*FUklSC)Yu{~SfLG_|tc9za@Fufgj%N|$;4a2E6MPbh&V5SGB|MjAd z_;|eReI)Dz7J|_&eQucZBS9k;Kd-YC8FXxGe5&H6@NH8F_E1^}I(A0SdMBrWZ-$7Sb|P6`aku&q5%wbF*E@ zfAdV-_KH@AstR446}Z%c9>EOGI7gF=avnpQbEdHy@Z zDS0IFor|F#8jUSo33J9Oq(Vyq73LxDages1^^!b#(fBR)W`A7&PMuo?r$Uee=ZbYn zej;b|GUq*w!enkH-$y!BWFGs8Z%W#GH`lQo6!*~gy1P%nP1xVG#vaz~2lDhe`>EPZ z%l9qcP_`1N#09zT`?RTIFGJgo^i7XwbVM9O3QJ>NKyJ(R4KSMua0k!pWfao96Fy(n zBRd!i)hk^$HxnPk&)WWXy$p60crHOVn;;|kp=77~y(kCaMn8vl&dQVKmwo^b2aVo0 z_^S=$v`jIX3*9eNK$Qv5X;JH0J7cGx8$sV(wofkiIZ>gY|qy@XT_q^8QG%5;(n~OQ8(xKah)Z zbHs+w=nWIWhxj-AY4b^jRcP;>Fa5jaoULfPd1Z? zaLxusGJ9iIe;Y1D4u!M6mz#1M@6{GEhMiTU#f%m!g!&^;t^a-8C%Z_YF4abbld-vD ztxte6EOfTRC?czwH+%%wOnTCcGd~n{8{TPr81C#!i8`5Iu!~?wD+0bhP?O+lP{bA* z7WpBlM&1L_O-Onf(vLTi_UJ)y=MTJg0+8HxfHaoV3#i0?+6~;kKP_U`lX4b@b+^5w zEcy)DnimXl#tQ=C3>NhnI~T3=NRhntBHMo`5MrGG49{+JwJSvvE`FOXSMVMEfNlSN zDdODOBXvj8e;2Fz|M3*xmH+$O7HF{8Z&)v{2bt*_?aSP!W^DS5izv)uk~o76Xz z=2-MMY{{+~0-+3Bb0c-kuMXj4Y6Y%lhejkaM64r%tLr?N)T2V*X|wJ=<5=|jk@PlY z7XLC*SVDYFaF+NkosOGadKo064A1N``k%8H*&_atW4P|upj03?{--3&@#ElcFK04C zcuz+cQhn`^ZNsv!fL0IkKcfkbMu;kApG0Lz_Rr?_&jS4IBPuK{iPpa;izVG|%ViK$ z^I2o(#|LR#x&Q*HH^!(5H*&?7O-y$dj*VJIE#o-^ZVo7BnMmOVKJARGBtfR}@nkZ& z#EbmmX^{M9OM%En(lO#4M&L*M+Eq+MnjYC5BOch#zBtDUafUYBGMs7%VTOYGdCv}J z41en3{}yz(w2 zX3l1>zxB>F2ndCDu=NW2c0r0ZY}<6}?kbJ%f&(gS6xI8#B;<0rz8%Gmbe{s9 z%Dt)Jg`s<02lnsb_am=gOI%@P6wr{Q~e?n5n1dJUXgDLuO}h^?^A z;l^CLDSo{BDv4xJPf~-!Kf5PXIKvzq`j5WZ#aw=2kLmH6J^Ds=@6T0|ET|$ID8vu* zPUHgd=VKEIt~Rs{19!eYoXb(K9`A6~0}`6H$7pEUftUW=yvle3KeTJ3IST`)&4Zau zjgDfDRe_#~VDe=`xzCh?SSI~rwBz@vAXOkVIY9a|{6shMuv}(Qu7>!?E(5Hp3g*v? zMRQIl^J%~VdY~oi<<&V)sXJTX#bRZ`)6^30p43bS4IX(Q@SgvdaTJp{TRq1OVha74 z6tS^0=z~tb+XLp5|D8K>M})WmUGsAWAJ+LkF~@EPWBY#4TC-{7lW4YH&k4h*_4Ed_ zC{lchlTHyRdW&Y5`IV@kL_vul@roxkr}vOZF{$Luq!)2Es8GXDpnni+&(wa9A#muC}r4{(fOTMY`;3rf)20`7y9k8P&HdcSiXabvGenfw7f zVASTpQehAnKoQF6L>z``2ls=OV3}R0Qe}kCM(3Zob;-__0eX{atn3mhTX1;1s@(o@ zw-7hxrs^#f(!8}02bp7{5+U7Q{Q=%F*Dy9vmKnwZ<%V6{Y*SyDV%`F7ZgS?sKeogG zN71O=o5xf2=b6tFUN$=bOd2Oq`npg5fky|DtTIVSb6uG6&%7N~rNXPzT3e&ko9FM6 z1XN+gk+a9!JncQdn=7Ai;(^sE5WP3T?<^=42J4Y~NuV>I_1SFrge!E==t+0<>X;id zMs}gw7Kt1$0M1sc1wIZ@S*h~mRk0O%n*1o8JqkzZFYP??Bc5Z-%d=w)0Xla4{ zuDE`s^`!{4lD7mM;f*)0gYe4;-4rEuUBUbACn*>>HAS9Ds{s9#kat?^v9%F;PZ!56 zpyD(pSd!H;ZmV*(Q;S*P?l2h1qsfFfP+M=>%^jk{%{@8!yA2Hqte18-h@Q(`$|sZ9 zA7VK8E$7(AzT6KJBq_EnI;cu$=dA2*bQK5xz1Ko7IAY-dYrR{Pu<`@DZqcI4!x{Ga#N2kz@ByW zVb>07S+V?OPTqZq{LkFDD1=xxf}3r>t$h;>V=mvh;`O zu+ahiUsMHkmD$g!WbJJtP0W(ZyWzpTq@;Iw1cehcNAE!)Nwh6#1cgst#pE3e3DBS% zIq~?ZXF8SeEk@rhCX1$K7*`wuIm&~*lpC1(Xx^)3L>AC`Q=2#^_o=WY*EZlxM^&pZ$i$QUujt>=#HC7Oe8-JP;1{_DYxHho#zVHc zjR`i-+E!sC>a8D5(Di35>tn~nsBo@yC$Kbl(|DlL$Ul;C0S>`P)B(67OcaK}`jp$9 zE%f)l4Fo_Z<61sLM$%J%$Y@-by|AC57dSONwQLGz@Q zu|qjl9t~B|T&TS~KlcPQ7G0*G;5LZ(0lMqtR=d}~Nrx@Z|AX-ox+;9(4X^xmdlqvJ zb1#TkwspXh?H2M}`${@h_PPI(tKYKsX*oMe5v~#XX5#em1?0M3&hbdfbWWX%=YI5p zg0~XPoob=Xvf22MGgq8+cHbT5>PASue^Op(v*L2705Iqp%FG!;g+gd-ICOCRJSloB z{QlHhkz6}B_P|l_4O)pSS#VfOG4I;`8;vF%ZkPTCdx7-(0Szbjx!Ns%mu0F-F`?y( zVdPIG9^`XR8~DYwr;*#pp4siJuJ~6urj@Aht@m#=ukHtuH;_>?o<%a$>>_{D^{N1X&_rxnbsgi}13GBDO2#2SQo>De!Dqp#&G> zPygH{U*t&A%ewvq?vKi*8IN)vjH4MC6Re@ii0)}zmb$*9L%PlA>z-3hI-nAv;dQXS z0s1vjS5EA*B+797nZ(3-DdcP$La%pMx_n3J zfWTRZ#Ww+#`x9+B9XKtU{{G%W=$?+x0|>LJ;8tobG^yM#=0;v(Xx}&BiGRS)Y41vX z4Ni0($bVg;CE52&oCqj2H~=q=9RfWqzHQeVG71PKqu-e7?Ad7?D_S3L2j-)COogNj7LGndRio;`f}k_hl~ z06G`tHq&hTQ@+HPqF*+koFQ6L&G=}z*&7>DvhHPZRg(pjBN}+M_(eea`~Bo)u|LAX z3tM@vC+9EzcT6XWa$I-1Rrbd4f*t7<^Gk~h#8E>z>vheVy7UGOdXM!fuEd%G8KHSS z17AxMG~P)&ek!*Q97-V}s>ywh*CdcGN!deNH_fFe)Q_7=Z-gM_&~uP(&>C=bCm4D( zo|H5rpJN+6JACEzKX*YLk0hLJv)IPzRqS(d8f)FL)a}w6L*+u#G3l?>X>x}08w(5_ z$v`8u(lJbI3D2xRp#&;64ZjIvyO=E`Y%yS2$C<}5dmo50n|)G3@r+)qaGl5-s|kBY z??v0=4SSIm(Nvak3r# zl}y(Q4fx&91&9amA=9J9J9v4fB|?>2;|C{owt~Tv{fO&6Z+qa2a?G9OCsA;maqRS^ z*9*-i&uI0ECt|#M73V#wx>Oo-;%_Q8D=A5nC7Zc3M8#{0ynWZU{{w%KqyALsZX6_8 zxu9eVX1%|P|K)GKF)7U=t~P$yJ^AgSOB?L%nyUukGK~|p&TGgEt%6sQK6KG(-?AilOu;ZIAEfYN-?i$X`ZA{5a2`CA2 zyPCyM?&JG^_SM5WiAwyN;t%o(HF=A%aGd2oQyh%ySk4_dDc~uu`5kwMx?FX4=puFg z*)sS>3-I)Ss=v4AN|?>j)4?p7?+nk$JNd(-W*Y!XxCB7xB><4aOG{>X-vKBS=7G2U zLN6-J2Qs`+gg?vwQ~6A$w*4B$YEXvIDMwG~#?71&4TU%e2^jblJzZU0VWXxpdW(Am z2Hs~FRfkIyne1TH-8-&wiqt`_KwLSIkjPIlF31r(D1VbazUA<-b`);XBRO*QrXIq1 z-sDI&tAh86!%Jhix``5MuND@c-+d)U^}f45S2M`MuBU_K@Di`W>3^rsCoTz{{BwIU zD%rYRB(W&XPg6D{Q*=G*c_?SU4ez<1e{qh3*qeo*?b^Tjt1>K|2n4vj7?tjkQ&AMW zgPqFWbgn9m6RzSrsw9`U2h#TGpTFBbq-Xg{u|eI%)J^&$5zRtxuW)o;cz);yCGm^X z+k@$7>F<2g5~Po`KO?F9X3b-(@kEtO?Qly&vQ5b4PsjavIeeo@?uYPe>Bt?Ez(b(c zNnIQK&)Gv!hnaPq*gf=!I-nrjB!&?OPoeQ&O^w;&-5h7H>CJ}vEXoZ_h%pBwbtNi_ ziA+(NVUv6h>)cZ(xTF0!&Hc8zsQ+z}^><+t{HF2$dy*1GXj7cginybH`^kI8utIS; z8I%2cAw}Z1+YW#V`Wow5?E`ls3~r&AQ0iR3RM2bn!!#Fk98tlAKqVBuFl~0fKNpoi z1IH?#1Qr2J$XZAW~np8)TUVWmZbVRcXyEyI8~-8yd+1N#5AwPi>4-^tYf|P`#%qG}D9^E_7ym z%;x7j)@^A0uGz-^LtMtyJ`#m?DfaMtRk~qEHL7EX*=IKb5z@1mEInOSJvwvNKzENN zi#>agGihA6g~QF?bj+Z%u&(k9Ym+^|@G}>m<;x7OENpgDhx&Q+MM}p#h}{&4Xn1lV8?;Is0R; zKF!Y^Je%DZ5Q>9FCQXKZh>SGRzV1oG%S2(+Ye&_#l+yjyZD-KN&(?)}s!Jz*T4iJx zB#YJ&r+a%Hk6wi;j@;@aXz;L0L>kS!wC1p%%qW$9#RioOfmbE;OU*yv%j)X?EATJj6X+Uk?n2e!&Q{b7N05Oz#C+Xj?= z0kBQ1IRhtg0E=O7JU^*Vi@;w<7O%zD*-9$=L{(Xz)bJ2czA1=p_$nx0-Jm_s5~@-K z^*4)>GXbdeCH!-vx{m01#ZM=I1m&65)i9@Rf~@QDxJl^jwVNfrGP=syUu2R8U&pjt zY%q~Fu^0Wi!y>5&r5-tV^^Vq(5w_O<>(`qPla@kqdprdCefR&lEJcBS!Ygb>FaK!^G{st#dnPT~Xxae^ ztx@ZEYh9(1A}$34x7Vu3T8x2c^kiirXe{?}7L874GROaa#bUELCAFkL&KNgqu(>a0 zN2%5{pDgtOocd_(Le8{-_&PXE+8n|=vL>BqaEF0jL(Sc9PpWt_FMD|Xpj~8|2Z@bL z8W(;Sm2y&Z>qQ%mk@GC*AS>^QH5#se z6T7^SvcrwCBVTGPM-$N-%ynSec{FMDu^1= z2o1~9n&9Q=9I3A-#oLR{9Tc=WGruEv{AcubW|sSHNn~uMfdLb$&n$T`8_({$XF<8C)`=TrKqCi&RaW@7t(~{1LJ&63ADXL0S(5 z(V7zaP*hx4OAK`@g;`{(!YtC3#k_{MXQAlyrVm@?#uy9?lWEp)^h3HCi^VcGza#+y z*Ja6fiVJM8SFSwJU~(`zn^5}t?sf&xFgqNRS;2v_vxK$+*Kl}ZG%0G*Hf=~pr9?>e z(~;UqVqO z8MZlomxOjGlGsKXJYZn7Ed4px$wvk67gBtSPKqeLe?VtGW~Z;`iSTkvo7>dj`^qJz z3Mia^u)Cq5c6R4S%Oq{ZiV84N>(J{Q!66>`W>4V-PoAVU{Fo=TO2%N z;(E@5UJF*a&puA?I&m+bD>!U#ufxxQyN~nT+*k^=;>R79y48RS_!6^D0=(5t-_4Au zI~ZTOOQ_?LYRB_S!?B3azk5HYlWZY!! zi3t{~uMC=(_ItWf)~$1Ju$iuNt0-@p?rsaZU{0gSllypm4!*X+hR{vu)gmi0IqK*) z@odf2vZZ~7S{g>S=gX@`X|xCzipCFbG!vt-!dDaCt&P`SqaD}p|BBb@#x33ojP`CzFE`t2=^O>Da&wFH{4Y1`e}zp&l&qw##xL7X@;ovqH(N@eUTb**<=C z3#@v2hpl~t9t6}d-sP3hj50PpcRcn1gYm<} z;KY$#x93~_`sZywy=Ubm&NG0Cc&O5$JTnvSOCJ4T^wntb0-s*S3sZ zn@6I=)Q^vNCYGZjm~_5?3b+W zf(Tlkp<~<%;{N$RMzx4L-S)Q8ubi#jWQu#FbHZ$hE$1XZfW%M8MI49-9XkNESV5HK z{UD?uxWBi|dS04Pq3KX8i4^A;re65%I<70%m(=!;Y+DG4Tbp-(2Fa^^{K!o1L@3<3 z=)dRl3e{R`I68!vyJ`kMVCVXKq^s0xZ^PcDW{2)FR+91;q&^fVW1I zVgz;KJV(ebyaD`k1b%a3UK1E9Q%Zmpz6Vrap@g4rjK(KJ!2%p~lb98gdzMX+=yLSe z_WB@BCkd9sE*3nL*umm@yM+S@w{axa$dn3!6f=i9$s_G@2s#wXo_I;f{_S)gt}3><36wWS~A>idzE>o;XnM zG7QuHBvt=nR~||*Juziz-9lQ-nE>96?ob8xk3HZsNpBi;SU{7m#|-qBL2K!~{!e}{ z6wTwb)e`lOXS**@u(y7EwI-Fq+p+&5|2Nt{4_R~m6ydv4`xRo<8udc>@P4YZK##yd z)UUhvVE*Y?#TZ|Z!IKV-=e`By-jhcMwxg_2yS03B-%Vyq>=Ay=CCN5uzYK9By|+>-rlXbln%VI-iQOET*aY<&_CJ3IZfKqNvP z4~a@NL^&cMP-eDaFFC-b?InWB3W!o5N(Tc|z?Mz9&&>Jc2l2CP!6$}(dbWp$$Q9q? z0h)R&(>%a}EQH+|;gY>!qXMlqXtcTiE^HLptu)erh#WtwJ-Dbh3T)5|&beEnn@K9+ zz`xPadtylzuy#vF-;fNmn|5X|EUSfC{~$mz_rnfNOKEr47`l%QGxT`3(Z3c}Nj5{2Cw)T~Dyo(Zw|T|~K-yuAc~8N8ch+KrLXqDGuS z>s~?K`0yo4OCP~=ds&s^x4=T}RlK2qvmL#P5P}?=Q&IGtsvMVBE!Mp!8^1yyjmwR0bM)6e+3V{{3<$MD{h9c3iV$NM!KW0Z+~$)Wk1> z-H=z*i7DXfQw^-S0nrFV4(zoe0UCHleakc)zdM|n@o%w3*<3|R+Gb8JP@dnphe(js zO3srSUDvd-(p~TZc|o^KckR0G^&ml`yf z5>?5*yF{(%8{FHEYaexwx&p<;< z?nR?D9^#KiR1F?-!q?SpeID&8n?3vwLmh?pdC-y%fJXhXaUEZBFt!s5T(onF022Ul zE{sfm7R!mDBm*d*IJuN>MR~Urz$e;@Xo*}Pt>~;61l9dx?(GLNy?aIdPlDWqv|ruk z34m`mjZzwryVsm2K1P&vbAA&~%A|9Bg%xf-Fv8-?R-d<@s0`^721n3 zI~!lX#1WxN*n+0i&1ssMNJH`nypb>=3DKJKt*^MeM({A^Qrpe0HT~z97Ov){CR;??gFB-~Vi3XNuIaS8GA9 zjPzC&G$d3qkEjcOcJAQsbqBERN>9SS({Qk3ZONF(@^CSH<(2uFzb8=X`$hr520dDb zft-dImqM`vjTUuwzUi6`FF82v9LHW+@Db`~=N$calNGfgp)R-ZX$|P;UrTuzCW{<( z+;O&;y6VTCjcLQ&ijM(nSmS@6-MiaU#NVGb&A(Zj(oGMFf!+BLtyOO`2coj~A)MgN z?w?LX!BWDqGG;-|zLXAbRaJFB?yDfzwqYtJC;n`07k1^5^uLmWR@_XxHg9j6fstAz zesHka7w1u>?AWL<)B@yny{=ASH;e%ebkiJ)DiN8uPwR#~EP?nqzq-mScz&d# z{8Zh@JB2#&xk0foN!)S)6Id$tL3e|D-r8?d)p6?uO%Kh)K2g@a2wU z3`^HKB7=Y;@qlMadM!FgUR&H$G{t#rnQ3?!wiDe|<9B z^cOhkQ{W`0^WF($WYE2Muqol)ZWC)H3-v`;Ps)B#1l2zHb`tTGWK0@mZHXf8+a7WL zVxURb=Q(S#{~OII!nwv!#(sZG(ss%h9%3_yv`HvHGR5cRg1kGnYfB$K_|Q6(>_#DZ zxXl&-U#ub@$z^0BUR>#*Tu#~z3~ZP7xes)}8WKJ_lqj}LG7RYZG2)JJ8^dBe1HOC1 zNngF$-IG5+TYog^GMmV6XQ}{gewEbARv~QLtM-p3FeJK0MSfX|&?j)%zBI;(bBv=I z@zMH6*#O(CsNTn;g@fKc}E=Ul7?_B4{!Ucs~rky?h?pl6(#@Ix2dN0@KNuJEs=WpZO03ko} zYGUR7`-QDV+V%a#MYXx#$4=taQ|)~^-g}zF>ro9`ES_m}hRluFRC^Bu2crr1I8Iah zDhTp54?CQO3AfKSWes451@D_8vRgMv*y9$CrzCOM9g&Wfkn3k!WIYwF8YG9>0Ed$r z1G!7+H)SJ=_Q2N-b7w=+Fi5Q4FhMWopcJe6(;W=}6Q+%Fh#F}-OW@yR2D1DPoElBP z6@s1CZ>@B0z>R%`y5%!`gI0 z5ZITM%kDOt6jdx=PG2Ihp#_idXb)4CYuSGyu3*Lmr%{8UBJ|k?;wgKHGBoJ?Sxng{ zB@XTw9P}hTJ2V8#On?L(OS?T;gD{4py7WFnbXO1imKhj~8|tBFH+tMKSVL5~gW<_J zS5VLbXedOP6kF1Rz(CTp-Rbt(p#JgseDe$}f#(XDLSr=RAHDkmRe^2Stk)PFC+Na8 z>m!8PbDViJp%|0S=ng#~+lcjEk(YF{a}P9R$WGTD>3=XnBDM3w5c1{FiRX1FOH0n5u!E4ZlnHK7KaY*{xUS zx+?~nBs3i9Z|r;B9ubD$fp_>*X>TEQ`EXCmuevt1k4 zV7X+%i?eJ#vX^^8Gx7756_5{MfkCVsB_||c1upH`S1)NIR97yp-_AFMBsd_Chf>X` zW}$brDm@@6SS@(Q!IK2W{{2iprZb5&m+vE9V8LZ(XlORiX0DU4_{Wg2qcJW?2d(b= z^vnjk-ZkZ$LQ%N}ho&n44`Xaf${IqDiDIU#q45bx==^=9EzgHjIT~{Hp%s!yiXk!- ze~|%c84@$)Y+p8>b!J?tv|syPj6NU;e08HlT+%E61e-OR76w0m71+2&Y_x4RFvqA! z=kT5ZnO@e;CKB15V+iGsKY~R&Y87Z%R&>z|bedW-&AoJf&!;f*2Da@Cu^O<^=hW}h zK=pi7t9EKya6L9IO_B3v2_X0B1Y+Yo)#g^~4D%Qvm*SxFB}B#4EM}{%1|fkw_tsGd zCBkN0Ry7QIn{~uH(}nF`!pjtJUJ-FIr6R>EzjKTZ=X!5ApB+GSYh$dlcGQ-9#`~Wa zd0ffuH~CdV9M` zKP-4W*lVwKRI$16U)Z=x%Wc&Xc54D?1fg@cF+J#97^E)XKm{X?s+HCf`I5Lny@uzL z(`_{4{t28{_*u4x6ElrIfBZ%^EGiEJkJ$e96AUU#9Zh3N&U9)5KB1e_=}%nRARD%i z{uE_P0axDn#LV)X6gCT-9RTL-4;7FbDO(=QI37dx;2{?-;+_>)%(Jk9*q>RN8!*Cc zS~=No;(4&Vw}Jv2!hH(X;oilGjmR;;G9 zOe%*3NkKhryNXV4U|p?NZe%LkYB8zFs)o3L&J4o<9_1)Z6!2S`#XoGW$wZKPl4<^+ zB$Bv-CE@2_GhS+9RU1?IYx?6YR_4|KGlA#PWRDGM!;vpe zKt7i8@MIgsy#H?z$qjntd2I%593&DeZILd;OJt!tvnRd5Qwr%|eexy~t!pS&o!49qHgq@P?el zR3WrvSB%dN*}hbM(ab+PS+`J)V18Fq|M`*-?hpk2&2RnFPz8VLdyfJ3N(?}3Gzwv+ zVsF{=6rhWiCrnvZUt=&u`vIFnw2mlW&3Z!xEx1sJCf|Kt)2uNQYP$F-Wo-#E zUsIpgdxtn6d+}psGqGstBPO?-Kbo31CK33p%uS$rq$#&Oy!Qg$M&Pux>k)7yFf8WA z`t&vD>NKT2DV7ruWgvBgOBqf~hXdL`*C0OqJ^!^1wHX&)`HzY8V>Xmz?ccsGs!vWz<_t<*5?yKslR78oo_IXpr`u3v zzG2x`cJ_}liX1U>GF5m#9P}{7^*Xf62WB78n`s_0VK_Be?I8m)9re*E{oo(VSL`X7 zqiU{__{o<%l*K0I0fuCTv~OF{49j6CnFfGpgwTGuymWXzdBlug`~ zoYJ5q3UKGGpTF$rQ!G;x%h7TRvoDNaF{_e65T)~a$^aOczeUGt*x!rqi))MH5RGBE zFO+tbZ>3Gt1iI2+0-iq@l>0RflJe2YsuKfZbI zjy5XaZu2snOVQoFBXr=7J&WStFeFysdM5 z$e<`4@$Xp-FQyjlu{;#(dedWzM5r%o@0gRu{}F?TC_mc`v~c8^a}QRe1!4mkI1k5& zUE^PZv@}B~76ofz_tKbGZAE^5wOmS9)Y~8IJy%`@gr#WF3LyiEzR$yZvcuc1{I6VlCm{P(nMLOK^J)Tl>41=OoWG9=3f6)2o&69V(@scB>DBrA+|!pt{F{+jnex z4ZQ>ps@h~&dwaAN39$-)fEYix*RQg=*JztfCm|A=uSGNR+lOgj0gj7RYi`S%Gv$JX zGZ+o)9Mvb|VQn=|oRiuk2d=gW@r+c@Th&c6jKmzqe#^O`%jjw6zzJ>r)w7>E*Ho{`GRf z2La@}H{B8wp~`QqJ)_{Q%i#tqmREyJu+o`uP7)R)2%YaXChx^lD{tvrq8*Z|UHb3D ze_9#7kRTECr@?4;_Q>g1MOLzw@8ayh+&BTQ;%3s163e+M4@0QNdKedDSHkP$caP5@ z?|pDJaUA*_C8U5cvdBvhn!^}#&vOml#d+oG7o79?afgkrc@ivA z^**0Goj=+iINsJn`xJ?;g@lHmmeG*|hO}iLXCV@#VK^dOHx6^;WczDos-w?x@IHi1 zoTb)JUlQ8_MO2V&+k9c~UgV*;#`PRt^D=Fm_C(jGp}oMbwIF8gjEv|y?)X%D}TTwwO$lt%;@6MHsH|RG7mmk@?8QTmek^WZ!q=lB_)>@ONrpWY#x%~C{~KP&v@rZf&F{1{OUD!7?b zO|L9+_y=nAuWHvNX+8bkH?auz^+1se6sWP^k-#N;tcWP+aa6yn;yRBx@@4mlwb&+E zvL=uPf6_8D#E4S$Be+JzIx9e3QfU9 z8Fs&Ks%0DmBZbLa-r$#tC*#+7^cu(K<_Uw4rYX{nY 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�aVmU0_d$fKJKb~} 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 #p`uw(ib~fUj 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�JD{K+Fcj4zmF<8xXSrv7t5~ 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Ν^4Yzc)I_Yff#Wqgg z19}{%;wn8FlzBn~Mw&#s8N*NJjYr^eUSEvPpEBgKQWE-*&h?0F-28byW9=JEi3wAN z^u`PXe(^Uo_1t;D^zaGi%@f4FT^F6S`}?)z8LsLl~l7y3zFKHhsM z%Wv!5$GLli!~Hk=U&_l*#ts%FvnIc@6g9RKL}*{xV^^?k-l6pZ{%fG-5Dqrs1j+P% zD6)~0$$FGj6b*gozoQ_^v;SdK#;M43F`xvusf{%d}7h0TA$V02cUs5ZKWfAYRfm zk(Jp2LWe{A+w zaT2K7?z7cyq7z8CkqUyoj=5yVyl=G-UR6I2V6Wd_$y3Y*T-HxpG6XUEWeHvBdqhYe zl0%asq@|||hI*;kzU*IDfN|g!fYgTDeFX}~wUP3x_SR=ZsE7~q$43898*Uf@@^&|3 zQXS3$s&4Bgw6eEFcKb1uKg$pA%2P|+;r2Z3>}r%AW?Yn!O|ZX5Icd4zg8e$>tNx#G zR#-#)?VS{GGMZU|hcO$w52j>K&mQuG@4GWh{SNU``k z0Z9y+TrW@Zj9fX5Uy2ZNGv8e)@S6`Y^mc!N2(#MiEoZ=6Iu{yBwt$)p%|rjZT!&FY zgsl=iM;w(}uWP-lOf-y1m7hc-qM?HGRhI9m$JhPB;t<6*WRo$t$3xSdCv2IofP&DW3+H&FwujK$o0@(l%mD@A-v)#| z1zVH+d=Et++**TB-@=e|XsI7#sQ*N@K~1}vJ?U2ufUg-nVT<7}u7}-JS}e5fD-F{V z5qQQ{VHO4a#cp1rgAwv9D>Yb&wU`b3W)&R8H_x-$fv^FDz^(OseM=0Yw;dlkal*to zTsd41lKeFpVUbZ~0wdXCmcy0ZLYH94+R^9Z+@P|_gwW93RszqpvTr1})#-Sw1F==q z|A0Ii5fS4}FU6!_xa&zVE~S3%xKi8DqzIR8-n;{?xJCf$7PQieD?tHBzHr?Elz`;_ zc97u^CO0A@Z4oKbf_9fu!X&#CgajC^C*>AvV}^EunPh^%n!p*D^rgQG2|?;0>&9|P;u_x%vOw$YA)BhaON z(|38YKXPMerJy?9o{U_u)3{MI?|jUV{#Zr5cRph%R@-EHfqR^>{KI5A(SeMCv-DUN zh+m3Bw`rwtb}85)JtpIvjC|07A}}KW?W^w@nI?b+*7tCtmaLwL*Ahri%KQ#ac|@GYHlGIlzNQUL9%3Z*P`IHgsMEUv@5EWC%af`RT==TRElxQ zP3J>83AVHtj?_k^w4gA9GiBE$jOnZ~=#q*V>UWQOhgguRLG;S@s+x56!? z5JLc73ADXcxXQH``rJbzlrePtzB7ke?6JB#DyePwZD+ZD9=#F2d^A!xKxcSC;*}%= z?`jIS6#{8COje@P)d@T5C57=`? zf4U&Nmp9*WqqMWQ9`yb0u0Ei%o;@8KTwpmZ%t6u{uc_aOlhUh8kQ%(1sI4UIsq`Mi z`3}d1=JtSY>mKJ_-NkDVbPp|74}EeVbfv~?@)~}*$D1HL2U9^$!N+SdGluppGkEPx zu!zZ^2?R#37(Wu!Xkteb=vBH5g6xU}zf?WYQIrx~kc|SPWI_)g`Kp2^+i0fh#rq)=#QPqt{(@B$K%?kn+r!zEF{Ub-;?d8O_x>Lg4|PsNp767 zN?GgUNw%rg&0>d|Of3ib)y!mgGwAHe$_T4L=Gch}S~A=z0yIcFslrKoF+>=SG=#=# z&m4s;fevgqB3DTQxxs@+X`yl}uU&CfgXThSaCC}mhPpum%!S31`%>_N)f6PcWH1rD zA}3edE5RB!8G|fdJJ?0I6h;}?L%hE69}-*wudFZiHh9&f>ZMvB{02b<8L6pypnjFi z+_D}}!DeRe@F#~#r^WSSI8Ys5r^Ukgfmy>!&%tBNY*5{1!(*PHwqtVb0}I;3dLOA{ z?MLwfbF(zHG(&{jE7*k_Qf5`4xTwL&)O=7}A!s-#PqvFM1f>n9D|hihc_%6=od(mr zL2-pi73of(yc4PQU1GpYYCRY}PB1g%GBn+BzF!fvK`<|lQBIr;0yx$+P8@N`a8iI7 z+IQ*tKshv-w9@1Iw@U>nDer}X z>_QZD&nI6TThQ=!Nxlw#tD$nhHkGY~5xPzJB+B0@{Y(|=S7Ln4%{KOnv=~)Rvur7>%pS+2r zqJym>E`UcuHKnZMLlDs=;AsFhOA8@Bp@>5n*FdFL`W7}H@-1Bhg-M247cQBEgGXM~ z`JK5vjf?=d6MNW1lnu#6dVs{e^~=GBN2RG_DHo#xGUOYkku1=JLQYyc<>^QkZI;ALTuSF%R1TutV=_hS=5P2fI082YU-d_1k4dEHNldQ3 zOP}5`kb?VfKO#D@Q~KoLIk}~JN?c634z7O(hr(klC+r#+uWOalJO}MQGfi2CzKO8O zoz0;@y`aX)Wba;ZQQxZ`K4Sf}nuRCp-9fkS1_L*Xs|<2ex>XMcmKr*bQ=U0s(WnHL>tb^_Mf9bWNk~UCmbB&4mpDPJX=rEDiAW>k1pz&#LX? zmM|eHz1c zAgmYRoU}1q?TCCXn6yr=%7U?}Sd+#B)6m>T@YdBm{-Z<Vsya7WU8=$-Z&?VJm}CAB>jjvU9NglviCgL=AFtz&WL7~JQ+{OWEO zg0&NRgoXz*tDK8^dZi4#YEO4C8Bg5-aU~SG)|0>o|H4UTIizO!zN%DM1+1_rFVtjw zhV}H6fa#2uSA*^_dvQiLQKDFIs}E~2-}6W=Y!PF)}o$m3#&DQmOm*Esw zSHaM^Xn9>2LBbkdqhZZ;lJr0v7({lL<%Nofle+_vIHBRk*l8 zuE>EsOdwe?#XTL$1M`KvzMef9t_0!2l9V3adN&Syh4yBr`|y`PS3R%N@HX7OQ_SAo zyOY988On?@cJ+u0p*zBNe3bwSbcfXFk;IZbnOoN~PNt5*VxQ_H*bLWn)zssMT3Cf% z++AJQ(AV&nA}*70En^aJog=sNw9rdQMwrF}zcIORq`ofQ4_0=3hOaF08=r@Mlh)Uj zScZOch5g`Wz8@0QDpz@++cCS`Yu%t>G+4g`fj#>Q^^W}FZ5^*yA_-sp@Da6M1btDg zRV^y2DDURbucr#M~JVB%gc7}v+Y-g#ZkcMoJ( zA2aMk;(#NKVOH?Kg8opSspo{?2c!V|-CeSax4cnsgGP7f?z-W519yWd{R*c2>&30N z#a_=}{205@gJ~pQsP6hbL*Lt=%-yM>=fkA!;12KLv4+8BJLF zrqbg3V^GPB=msGkH+FfGL$C-NLA}`OjzJw5hhn+)s&kDQjebK}@-ui|l^1W9?63sP z@9urRtRVB8#88#c7v5k$RkiQ^Ioq2LL_RyAO`gBB3t)py%qBw^$`R`s$pO6h1p?@B zQ_oUC(<+WwAT!^z(2Rr*Ibyv@&9sU_34u40-<<6P@62gt;fn_adUb+Xo#vV55oy5{ z{vx|t>@Kf%wb-`k;k~#znQrXcb}gA-5(xta;8xk3BnMw_z?0fS z^ldc>z%IMoTD`?@Lt?XSX};rLO3Mpw5>0XJqI2EtoYP6UnafkOZ&g-q1vb0Tk#7N z_t}5o`v>K1=pHoAcFJv>@BR#^8C*9Ul?&sA!&Q6a%qqqQ&R2Ea6WkEpzRkc_SX+1F z!pGmt&jiGoXdS?zjK!-RjLtmqwwCjk8#~YCd%Hi73U#@5FgP&m z5A4NxrQtq*lDy4JmqaezD+m7Lstl@rX*wdjK0alr)1}i2@0YWqf2X{EC$C`ml;HKa zZw~{~bmAgsVJU!w5-&Ekb@673Q?So@;&i4tyjypp5{-0ZS!ipMjV@)nYzV7PPuAM230 zJML!YWt1-Z3UsW8TYJ>_46aVMQxKXX9!Ofnu*=5;PHz%yNPMkVua7J9_3+d83CcZ%?bKSwv)EC<4lwE%J673M^K;h{zT+iyB zoI}3tbdy4%NWN^x@{3DX$0YhSFSy+q9{2Uzfe&Kd-Lc2t8(MRt(384n&!`kEUJCLj z@IZtLVr?}E=$j7*FqMgqc!_9)s)l$TkJH5)nTpr+lFTeA)*zwM#*Sw1xI@gN?{sx@ z7xO&#czOGXdA|D(_#gb={GS;V9CAp^3k#1p93|$lqGLEmxZ--Taq$U>VqQ}6(UjDA z>*UH~X&J{e#h|S06DM>z9TZb^yQ8zKyJ%iuedlh^J!`P` z{ocO*2Vh;n!+}SS2L&K>X!yxfF=1r%+1NN(c7Oh2ViG(s*!rKBQ`6#oDX(6?nRzQ_ z%+B%O3BX->?>`7Wicb{&1TF;^d&<9G7QecKjeT1ZeqR<}y(>R{{t~CZSJ(a%r+?P} zZp;Q{xQj4}Gl=%r7T;M3Le9kcT&_J-3nPm`Nx6#B!L_9tO0rT!ik7Z13X|SRd!;3X zFf^u{>jK6a;vbVmot?iM8o;CFzLb`NXD!U-PO}V*wDUn!UY@Da7o28|d-W17Z-%rF~Vh5s7sPHF&{%|i*`Y}t7i3QP@ zInjp+(VIchgorH?-4%_si3aOLz2&02MWVK>Zz)SxW9Q4G=88h5^8VM-SeDn*bdTN? z*S1)C1KaUx6i^;wQxHhW@ikBPA|^(HAP?gxIzG@**9*{crDy;|jJdIru`X%}GG7a> zhn{;g@pSManCtCqYpAONDUqc`d0FXVULrdx#9z$w^`JZ2P}t?Qkwm<1qR4$IJbX@L zW0NQ_aLm`g*TW0#S_L>3QGl$9SmR#)CJ`}27w@H^ic&)IV*RtCeAyR2rF$d>y0U#8!@U5CIk74$#Axh_n&6#PWq=&F$eZ!+X{6pwozF@cq*OvT??-z+wYGS{<@twNs_^_PVlA=}auax5` zg(j)V;pSkidpv_8hRS;~MFFB?N4@t1h-jh{AKpFP&WEPyw^^6(OD7ztmedY^cWaH9AQu80W9(X|>ah!Wu) zi5e7LC8X>?i4F1j-IvG}3|uy^%271`KvI&!NQrBdsnTOBj>oM)qUxd)4g8!_)HA>T zE+7E*K9Q`Qg_?^$!f7rdrshEQBKtlH6}rZ-=E z@Gq>>wH%@)9!DbJ>K~ON9^<_7hPzLLew5d~`4Zx)>+h(waM>}w-B5$R#GS@p_fF5U z*lw()gx5gnz8q+}ay@`AtMmye^^IK_e(_5Ag1=Yb6{M?|2ZIjmuGwC2-8`mAW9!kf zS_npImFnpGSJZ10?m-+Yb*)RW$qOg4z3^YE@J|O?61BcWGEiZb}83Xpu z)2X?(!HKHQhdFhfv88qXIpy@kJYsk{i>>6ULFlu2@8~ZJfKLTdeNy-v`e#m?7aAY1 z-TjZOO1ms|GAku=cHAO1$|S_x+~3#N%Q0(2_p#PDjT;XnlwoSLLtE?r6A`LX3QA*h z@}kl`6U$AY?UieF z*YA$*cgeH%7(FVsik(v$a>qz@hT$8V^*Yg!Z{u!$y7sztzPm@5qm7%2bH`_*l*V2q z%)lPi`FsC~R1zjMyzb%y>xh8`=7A~Z(i0Pm3$^gN^pkk4&^ygXF`{174=GVmXnJJC z#o6KsU&!E(&W7xAZxxHCg6i~GZlER||Kqs94z zo&somnW$_iuggo!iSn)L)q|q-4&U7D_fKw3FMxL{^5dz`FEKyvGuVNENGey4wSmW- z^o^^aNuEoyb#_dwk`jlEL&zIQ$LAa)8+hLH5DlhJ6u%nx;1Sgx)5&^mb1w4R`9*=s zuZXXlPllrR$epx7;|>8|nH#%oNPCH6VJncQ1Yf=%b36^)@aP5i+i%MWSfhU3QFng# z?hHa7qxSLJl(coM&?i8L@afp=A>qMQtos2Rp?V@ANnpne`~gAWJ;(3wE&Rs%MIGNq z6!y|Q5pCKREK$D1MP|W5*sr^~9=wxODk-Q}FvLn6Q{bZ!l#Fu){A)v5V;&wHEWgmk zOv9)*Byie`hL}C~YIGc6hAvHdlMvOK?H>OU*n|-(sdDcv1rNkjHWPYIm;Map4RevO zgH_~0&J0n2H#8q4y1|@4xfT-%%g5FC)SD1d$C6La;U!C%s&ZYB zqWPyQ9|`DteXo!`=soSaW2La08GDtPUKoKRRnQvHPGa0cAIw@wDAuC$_654nwihg6 z{0L}f0bRH|Ko~^1?b$n}W6((t`4wtC`z*Q_BQsUXBoaOghVn>xhRi_|8~{%&HVpLd z6fBmcQc9Av;sDT=R~#5BLV^kRmdvl%V(o&UNmW@PNe7O%W-`g;9k( zI!2|bee@hpyHy{(p(iwh{Jxh(!N(Za8bc_5(2ZT)r6xFO89X2QMrKtR9(?`Tg+Rm# zL9q7wH^51b3ln%s`gf<`(j&|`Gtl{EY->|?+GNVC9cG*dgoJQ{M>?Ade!G4Cfmkg8 zSM9ZqPzU~{Ewkp5Tb)01s3Dx5re8mIe@Cn_=Tu4V%g(plsO?L_n@`-W< z2&U9x_H$g7Z}RLUFN=G>J_M+SAUAi@Ldyi1mx4mlc`zVSd&!ZNn~q~eCv9XWBa^iv z{ZDy>q#D9T~*g?d9w{@a=Tw-dL9$q*K+GUoZDen7Vkrw;7u{o2T%A8~4J~ z{NoZ)sS}z_U?s+J5G5snLRID9C>Wee3uD(TtEz>Y&@r@Y;FL2oIwsVc6MCd+-eVv) zA^woWp^ykwl50$5x*XSgucIweE_~Ok zfx=+ag~M+K=ph&b=@gvHYdzDQ`A9`U2JWVT@~UF}KN2zEPa29H>p)y5Ziu|inoKq| zBW$zYZ^CSVb}s%6x}>zSXS3=E-lK@sL1$kvu=**%3+~%VF2B%uLhZF)^#_?7Kda5g zs#N_{Bp(Kjs%CD9t{{Y5u!pJ@M>Wh5>`0wE-s8UDJ`Cx-V~53i#%00Zn4U5~I=3G| znS4O|>$Tu~t~}C)#?I(mX{D>}R6o@|ov9iPt=aR#wFRP7vvDzytU4W0F*fpbWRD+9 z5-BUssQkj-en&y*ChhaiS8IMrE-^Q26RbcQo)6UhU_0*hKIIrHA4ze%98-z-{9-c% zOR|9OqZuljt=pL0xXLQpRUwllU9Y-7)Zq?XIX5HvhePTDM15cG$$xpmiy89O5@j`3 z%~zWtM$?^~e#ql?WymJlxZ9hakLwx3@>>kmYjy2ZK=bcK3adb{;f^&0eE zdxG3_eJlM4{Zjow{oe+717Cw2gHD45yc&Kd{wTfyKVv9vWs@Obc*O9E;pA4tR;#U1 zTQ6-LH-Z^i8buhD8;u#mjKvtTaisAj2 zCX>)f_(s$t9w4404iYy=mLwLbhV*8e;@M5!sW>W=T218x+CUI+ z1$ZwTUBAa7GjDPoFts+PN$uof&3-C9s71% z+%fB{;~ehX;4GwD(o^VtE|M;;F6UgP#G35fvNL4o^_{|9lwHSm4YHhZ|dGb zFD0))uNz*!ydAs?yx;6I+!rs#?0evY@?rQ~_xbJXwwe& zA2H@Y?SY^EJN!%hKQb&CCmAmf;twVr91c(qU0q%n{_99 zOLj)~hZFP@4Ppr= z<&G-`SI%EWTur$8sm7;f@S4@N>$PHxdTn0q-@2GOe*ND1f$J3TI1bGl1r4x<_=eAo zjK=Y%UcUz-r>3rE)8@-9sx7vy@vZ;fWQu+G=F3~Iw;r@vwKccvw^!U&x_!C>+L7EL z?2PE-cP;%VY8ijty{r4d9h*D1?-K6T^=S8$kKZe}FEb~N6y49hFX}zo`_p@|KfGTY z@I(&+AG~?!_i%Dx&%m=su8)QuJ3oFnXg}CHWH;0^ynVP^jCo@9r1L5HX~&4=$n8V4^dI+sp1<7pa(ap}#h(tHUU8L)y=E*YgTK6|M~tm|0i)>YQ5~l=&I=N*1vZ)TsLM! z(ISz{fz#0R+C6Irpr>&OF%;bb36=O$O7 z|5UroL0_ENdIXMY?>L#{d}o_&rEx@*#Pxxf;n36=c@L#1wQAmD87C!a{qto-JA!c= zH<9F3({S#gQ0S>h;7+0$iDdo`rz>Ikz^1@^Oev&OQA-0l+M+E=|09xCK3lc|Sg`*V zadZEZPcrYuG!pqQxsy9Sbx1pEMjw;dyKa-aAwbf-D{_8zV=F4`t4gEj`eZYjsdmBiz2>>~U!wi5rTyxX9cUN)ixaTBXy-;do%hl#TH-{h zE9IO6w1LJeJA!G^?TUwKod?dXt&9O&nzx$VQCgNYFO#MeC<2UpPNQ_kIhq>UUw}82 z{yW57o!0#g;FkD|*&F--j;ie@Ykd9#^uvktoXZll$uFm`G%(+b>bhlV%LKW{TP`Tw zx!ob2CNy;xqA1$gQR!IP)~miLDAr%mReE^T z8j}4BDCo+*U`_>_tU5f?QEVwi`yQybXHx>2<{Qo`a&yDbWQ_mb6B|N3ila4`eiDuB z#?$;={Z=xMl4xTy4|PB8rqFs7HLTM9h)(CE(uB2owV!s=X;FfRRIDd0=k>1p_t!?* zzBK8FO&LtudArfxaGH|B^4fe=I)~;S`Z1CAW8bd~TG7W(S}G0eIW$Td4;9r!>E(+= zeTB-ufqCZYSXq(?NZ}qcrYmlIo&4U0DxF*ZL>V_PM{5=5Xos@?Qi~WgQO7Gg})wam~o2iYJxBpGreyzf>?u+_z&IDm+BB7PJ}m{xo@$p?TeVshCk}n=BaLHuHZ}YifKSyEVt1#_ zK5GRqkUps9_)Kvf7A*vDYBo!)TN?o~77ZlVR&zJwL>P<%A(;T4qhOuL5@7ZKyc{jO zOV(YWItNI|QJ?8YfLp5o1GRD{Mq~wuEDrIcAA^b7CU@S+^p2eLr#CO*I&z?f00~rp zXGqiY96Woh%UTOyrt1Kln<&cspcaskt_kdz|1`q`^VPi%-d3cjwSw8c!Mo+Lb%5G9 znCA~dl{+7FhXBGst@J@TLDqYyVs9%{kurbQlBxiZKnU&AOeA-(ce5v$kNr0NjXBsa z4RF8$sCi<>;AJGh8`PS2F9CHQ1U9!0YQ+vFP{2m);lTKy)^h!wa6?XjZtpbWx0UFC+?&c3lgStxt@o!HiY7E}fLJvy1k1%S04Rv4~IxnB> zz=!&fq_lz#$5Q~Bl<2V3ui39_BnLD5ZDuHv;x-M`J(PkEZyUnQNk^^|bw>0+?|}<3 z_{a;r(wakw`1ddz3J{4+?f{%_{naUkSq`byQ`2Z|`ev0gxrb{6{T z%UdlK-;!g#@aOCeya$Q)IX=!AZpU_g%b^*dk0knUOcqJfqLy98KQ1U9rGaZS=RFt1bEU+`5RJ z?nC>!=|1U#(GXyBrrmq?tip}S#p9nkMZ-Qb`)LTH4|CxRYVRTU(nkaA4f;lpZ;3!m zzHz3gi5ghurjox)h-1-$&SHBziK|r?K70HFgdUu6mPlFw*tGSpvKt?~Ao=0rBhAnp z&ud}S442+xTdaPA_7#wh(A4v4+lu-}0Si~ym{R;fTg2zUfQg9O@{!;l2dGkEe;G z2|fkuqoPUXIN&FmCj|!doadpAc1Up&X^IE*Hryzu?3o|O14W{tIqF?JO7?cG93+`` z9$tU_P}FlE?Rpgjen@Ua<@)OSr}P9gz&lE_ltO-5_J9lY7T+K`JYT@?dD!5eI zR^a-t&q*(U#s4Jlu7j-xy}1)5y;eQ1-g=By;1#D=DmtoFHdmBial-EF7LqpLv+n2+ z7VZ?u7M_S157N2mi97%2CDpe4d4T~CkwJSa`|3}gF@aQR&z%D5AG>A~frBgMBGPeM zgY=be61@H%-&zCg-NfL6T%KkfS64 z0Tl&70Wski26G&BP!SUb909YAI%W|A7{)Q7rJsY2BK-=SxijDQ?zi50cis2nt#wcL zu2cJ*oz6a|PMxl>+xwFjyKV8zMpW@&U{e{zA zH7=+RgN-NVNGrR}D3A;buN%kHy@03_=lUoC&&M@*yTH9a6aKXa*2CA;wN>crFovxm zkCSKmg#?79rge@qw1_9#Ht<&Kw*>IKZ((QH?i%m{s{LqwCod~r)8-XJZx{>z^v+{`+ej3EIWvd$V`gD$0`kDmc^mAm(~!a`M{Q`Yy-K zGxWy&!NY5?RTX-7{mN2(i>9yWGZRQ$Hcc-QjdZK_g%;@J#kX?xw9AK!93L0AXY1*q zfZL&TeYhx@I1HE{NY%6MBljojx#UeJ`Hv4=o1o{36bbsZeQOu+r+EB2Uf;Ao<6h*l zcj0IC;-<$?m|kk(JkmA$kV~*${swGu4Ad(C6EL;NS8qH#Bqf14d-eLhO!@*a>koIm z2G|oZP<;z*PSIXcdl$yc#AtM^G1J=t zcQKSwmEI8`Vf7`;jAlk`+gz#M1=#)o`*^oN?+$Qq#^uY65jcz$3w_hP@hlF%V-;2J z1Dr>Ted|u+>*Ha40Dy;#7cLk>p^YA(J_K0WtAwMPhP=Y7pI*2CKl#uGLs{^s@6 z`1ENsumSNUcG2#8#RDted%u7B(m+~8+FWzlYa;O4^AGuqAo3P+{Jea!Q_86+q>U1Z`Nf0MDPx-+n8~ zYcBA0nVCuC9D%V$c;WnVBPe2>aPg`KY6;>c8BZ@5Ig)eM8m}f`jau4f|Ng1bRp!{s z@md0$jFLZ;%y?g&udv;Q_4*F@yhtfiMFx&6ZC8okJ@s4#_?q+_v)`z*!6SeRCW+2e~w?+$d%U{^TOFjRH^!x$1SY*Yq z9=FfLb0=^#616$ihG^S+nC;mL?3<`Vd(8xI_xbS89(#bjlkAV#Hs-S?t!>>Ym#3)lJ%uBuwdXZ~Cs;eby9@wHLRzNBs2J*RQC)W379E zqOoqCd$!B{!dhqB`|*Wd3x4^$_QaF*ZWB&;^~A|MB2IX9(&9YAT3ahbVZFyma)9m`C=de6Vw73QVS!S*yDVczN|rUXBEV1_8n0UrwRei3p|;y8*;`#C zrUS+4$);8~_K?9pPVscR9gt#i;bi8sXxW3XoVyii%%Tzv`s?TWiI-k~OpaNcT%Ht` zn=YM4?Jb(h%3QW-$6_DvxMaT{uikP#wL738(6e_B6F1?eAv)V%zGr@FZr1Y1$MXE0 z-r@GqA^8QdIhX3!_wjy-E&4e!dU5+L--*BY1+}-oD=%_QF3L)NcY4bsi?{LR+4fir zdEw^Y4smJXC5H}&9?npfC+B~cC{4~uKRCE)+7829>vHjn-^|hY7xOC~s-ac44`c*f zA8OoRwzem`3%~hCYDLtOzO>tCvD1piFYlzPe2XfnS?N(79jwX0IRJ_NWqxyA@6zxH z(%DT7E{)x|oUq1^J-Fq9IHv`$^}*>C368bO_4eUKj51tjL{~sJq$)1h7=9SvRWms_ zGJag{VbmhJN;ikH zj4W5cvHM)yLxVJNqBM2KMmTydKcr~aQgQL4urTkp^9&wJ#onuPz0V{^7N<^|o@u$1 zk}6-i3OLuh*QPg#KyxWbt8Dj`mCY!sogKwAmKLY?7b@osmlo$1 zqo+IizS129`MHHtC!Qk@{ZN!qWa(nlV#j;3BV$*5*k1SisV>*kr_Ofr^XI3Qaf&D| zi&~C+h{J z{)oNfMn89)Ceza+PM~uJZxyskk zF^S{ddcl4<8XcBu0b-)gHxf2sasiG3zh7HJQbz&Y;LU(Vvo9E{n1|VEAmXFuvPhdS zYLp-tC1^$o6exiKC76p6U{C@)N_iEoP=ZY;fdM5zMRQQmT2yot zDta?2+7}f)4;4*BMYI0@p!*A~P-y{wAQLEux{SuJ`9Kj+Oe+D*fKosKga8FVDv)ij z0Kx`;>i-vz3#6q&GXN`9d`v_x4c(=YE7*22f^Y7%heIJ537W;-=E-bEdx2S6`czC(yW1<# z`01fDqK3kX)q7qST1zgy#>48;__XQS*}M&F&eB~5YfEC{3Uk~t0)FZVY*~g$z8dJ$ zVkN9VhA3Tm|lR;7U8w2dzzZzb|z>hN<)O@iBKwPCS#EVUxLW zUyQ>K6W=?QMa|8QJhZ*Nbb4@d#FxlYOvjVntjg&{)BXh(PYN0=diz4j>2ap7_5^AV z6Eh4_-+=8qn{y}I+XfiiBB=(~U*n_`?CeS8xE}3=b2gEh6jI8d-2|T=r54(BPG|UX zh>72!8((SgtuaL9I<@-ZnSHdQF{W(K#1Ic3|NS^HbLA;!$)YW*FSy`p2r)%&IUFCh z%Cp!U_uBP~S61<)rhYvB`&Aajos4Gt_SJ^%0o z$fBbA$(3onr!1rIfv#DkcD9lCJU8FQFQjx@Nq25(r?8P>%9vJKBu4{xY$G8DuBo1MJhV;|J^c6occ2ySmVaeyt^i|}nG6clUyJ|CU2wZLF@@vlA zj=p^kPsgu$b#8H;eQ1==GdaaKFnWz4ar z?p>YsAze|jEo1IaSD#EqU*B5-$$oCy_vLhI+K-3dcs_Tq(z3KhFGFnmb zi)7iR!epNos@UTq&-nzE>29J^pkMT+(*f&8F{V|SsA(IrRBnh;)2rb z>r^OG+JYnD^&58U{``7VE6h-IV}t4}yzV&#V}lX#M$ z(0zgJ`Dkc_u31cE5QD7O`iGKeOc$gGN7LxTm5!m|qgU5WJi@N6M}(NjWJC!vu47=N z5!1d2xs7FO5U~2oVI$anh{*VkBCWv9gb)L$GNA!nq#UC@$t*H{G!h7WmIFgbL#l8b z9!Eqo5eOKj*AwxC(YtPUtth8~SWN_rXJYOt4mhGb`(hkbqjsapDPO+gjdC{;#D&yE z4%w1Ggqc*;?7A&WtDWlYJ0X{;HsWnRDgD_p$l!zU zKmv;uYCSnR8W}Q9t^Nq`tSt&4yf=&QlbviDq)@1m>ZIOZ4u6bCL?9{XF-by2CSzDX zv#EI2db4`8VRlriRBuKxg#-|h2@%ZsI%g?U$I|d@YN+Jv5z8WF1M&w)|;F(zo@pKO|ovh=U825fX$|2BQ zEh%_To{FsCNfMI84E*KdmPaI}SVZRtogGPs$~%X(G_o0wNv4a{Je(jNspxYhv1>^` z;!U2hh=ujN9?#iCt^7ukA36e8#) z&FC$u@YSlQDvKI%_sY6MnOjP7^hZdE#h@+N|SPI{_fE*ueLXl)nC>4MXika6u3qX~&mwX>%>Kr}0s2>Ajq_=yil zSr)Zjd1qL<74|=dfwKn~*8EFNwOua{i=NM$iFjZr1d4$xua&TITw`^QEBm-^TcXfHLA>BgSI-%WOL9bg5}c+@4vuP$!(o@_eZlMIGjbL3jNsgEW6 z^c)!gj|Xy7%C`qNQRnP&G+LG~zHCE8j;}y~WEy`T%TA#=cgC2_wPu~RLYQFGRSP=n zbgZoZgyj?-a|6aYTuuX~C=88N# zu0-&kj5!h+O@4rOo-4s`#ix0b5fQF?r2lHG>v@lOGb9N_7Gc;(L0}?xIppB@w5c3&b$r(qpFt0q-?>vb~b~J^ALXtpo z(FuKWD&rUZt3@)QlC9*ipU?sb#vy1@M67o58dZ%vnsSv!PH{uZL65ulobwVq5iiCI zoPr>kotN|*?(j`})hy*c{0{rIp`kJK(!lc-HQ2SC5Svn3w#(~;$ zC!G08;@>YqogTnUJ`x_gofOdksqX-yDl1X#@VuH8)@2Z zj~FB!bZ--U!T1Sjw8#!P@B}Ic1zM$ht(6na~2QY+qUU-y}7}4H?tTSi22oMdKoP zLCW%VRhl} z`NolMf$sJdMzPymrV8ZSUc2u4VbW)))^;}i* z_9zvCc=vwo0$IT;4PvA-UzDvck+HqTzb)vwb2^bcZfFArT7qTG!5~KV)WiBDL<)!T z@yK(!=ocz!CSz~cA4Y12i3^v6gK$(FOi^dAgd5`%ws9Za8;qa$2v;Mu0IVR>iFE6W zX)zaKjZ$ZWw=1MA`<;Q*po^QQAdCb+76}QIK%W#?7&D%3@QQ#(o_Sq94|(wP^Q4i{ zlwd>pnjN$A!$UA}HU&Fuzf5{~gEdGJ!H;xcbjv%+cYBQFj52ys;OD%Nv<7^-MqZs z_%__?Cg|oC!*->r`fQn&vaJzF67&ePYuBDgXo4TLW zA)tKX6T%=4@4x37BArQ)oP_u#*j+IVkDqWyaG?8%P6Vv zUHJT6qtWn|M5j<7GQ^=+DFh56i7Q5qdTsp((#f;1VIrjhGk0JujS6$<#I*Ql-LHr6 zpV0`?N=h3Zg00T)7Gsu$o<~who|oYpw_~+!1Q?RQBqsHB2cTnF?c{U04tQem>yIFr z%z*CVLcKi7TV!H9-41^0e;3sr{g`c z1!UYhQxRF%qD@wuzAXPtrHHP-H(`@VaKe{6M%NU2coq@@2D0XnDPm5%DWEv=)deG0 zsJtMxnJM4Kl*^wa?Az5}%Ox_Hc;;hBN#o%tZ>bifVu&SycoOaHLZfsEh7nC14=H3& zxAZ1@)kfF(*xxR_hG1!*bn{YjZREhGSj#oOE1OTWl6NHaBq+O?iimjM3$SB_uLx#H8Rd&igoXj_!d{t<$CI6`a$^E*WT1qUV~Ey z^!w;)D|m*$Bnxik|La(>Z>R}I0&rV5&h;SipD={NT$|pG?~f)4orqNO1oRh5B|9Xp zr1OOl6OGIPwIRA~c@@R3VUkP*Wb7KBwcY3H2A=GB zQWeHx)2PQ4IG%1tO%5jDZV zk{A}S_p=*c#T>Rr8nMNcXOGw3ZJPu2!n})+3~mti-K$-={FurIEEZ7tL=wN{VZ$Ur zD!~-v#lmnLojgf1Ig`>_9<`CC)C-v3@kR_gTRQ?-!u^_ficMIS&B|lj*0^3>W8bpt z%2UHkGc?q=^W3jfceCwXi7Hs!+~P=sU_t7g9coXE?qx*UWS$ak)M)ramvIRsp zqW%8q70)q}HY$;iyO06Sa`_(Hbe~KJzgV?)HwN zg2_Y$(VuAh;_Xjaw=rf=luV2j5lvUj-Z5}w=S>ozBC!+RE%q4zK?94l?{m>F6DV11 zjG2d-HDUw85-aoxyI%sZ0>ljTQHm)tI)z3+1L8Bgjn|+;_KYQ=ERI|*rSPI}xP)fh z?07GuatOS$Lt84UOj%}-FppzS<1fOixQ<)yO}vMg5GFF$keRjenQT1YK2gASdi8n> zui0*#dP|!qWW-FGqiF(D_{emS!`g^N z@;VM*F2Y>^R}*Rexig{HNC`+nq*IDx!2D-d68VHVUek1D#vTvSB}y7%3)(YWO+_ck zYWzQcwh#jdgzu5Lm<-{1#{n1EW!^1gG6IPpwH{{c*lbe1 zR-_J|#*&%T@7Vn0P#+cBCO4HhpOnOpcg~rtXEAtM zV(fR~Q!xIBp3fs~nV8p{&ds7Wt7Rxv?jf$a^ihzbE;!7esnY~cebgtmq;t0X)c zL+h+fZEmX)k~lc%H_Rd!;_jlUeid!&HoWgXR?e4cS9`sYV`gE2}BtEMDl2 z{xB`WWNB_gIx_8b^CSbcw)zZ|%y7rBY@`Cd)3*1Z^PRfzD#rL>n)bRYl~G?0cV4ut zn!*tIE1xSbO}j&}c}^#BG&n~ie|VNBL*+vG-V_RE^io-oYaKZ{3(B31jdjK$E>mus zLz^Ju>K*xl2`xlQ`N;v%#uNg>%l8LY^U8y=^T?7Vyr-(b{8uNI++=r)`49RiNLGd# zyf@`QT(y-tc53V-C2K>L!%E&4i}_R2Tvx}&vka`YFc4Q=_f-mcTWTmblM!>!!j>{g ze5w}T#=n^*pbfyy8;1@(mUA;;J~>cL;~k;OXd{DXq*^&?r(8)3aZ=mbT2b~6IJLyy zq#Lv|ZS*pF<+=Frmhr)20p9ZRDPz|w8riE=PB(Qk^?_B<7IY0;?n9rn>(#IRiomp9 z&9j{rN3Kd0v@7DrEZuhC3!D0hI@44v^e@Zq$Yn{$R9VNtd#aE0F{`S%UOxPR zlR~^YMHInFpF(F`U6WcKaMqefDnl1{o?Q$Y#Af>!73MvRa_8O`LnIl0g)B&TSari8 z`~6NwT0)L%RNOnGvH~XY`SE5FwZ^?x8JqarqTAq92#^NEcf=<+&)T7@B!#t?mNSG2 zB5kFLVXZc*o7>e>Y?s7){~=P$vkocAGdsuc9bsC!LU$O`*O8P?+7vV=J?b)K@tM(H#6}vGG?8*SN-el<|bbM285MDT$k*tX6-C zU0_>Rnvc&j5u@_PoD`AJLapz4+dULE*OtG*N#0~ir%RH|LxZ{=SEr_0@MOFRB2Tkb zOtnNpk6+vU2$@A4T;_jDIE|EENj^J@GH&YJLLi6st zJ*pWAqq98Zd~r+sy)fWpd8~ke37GUQa>5c=EGt_lpx8;tDG9C&2l-Ab9#z02f|4OA z6MfsJSNr&nu1`q}@jAM>56zr|$H(x>Hid2q&3GgC6*~*c4T?k>>BBb2tjBwiXuoYh z5{=8Um?N#N-cibSl@Un>i7>3(<3{ZgqfSf=7cR52I*~_96V$3W#-VCxnx&o+XHvzv z934NXAi4e|T`!O4n$NZD729~Pn7QEa#H%!&{tg}a8hDC(# z`q#GFfSvfsBkX6EB$^FAb0NNVA`Y0e%Gn{$xN$pw8ed~2wkEcUWXmL7i90E^kI++` zI$D;{OJmPF&SJCrp7JGr#*W!NKAaU?3yFbA85f*|pH6LnBB*#WVmm1~nw3CPnJ;-S zFgv)miSuEDQefJ`o~O%icar-1IY(WKBylP}K+dt|g6FHKFk@g=3LaiTBxGSnwwz%Q zZNprVogm_grO>Nqjo(JP-OjZ8WDO&KfXTAzk^bK=vs6=f$X8G`838~wAy-u^r1;LP z=;^edU$RMitXRC#v;{E*V_()T-ko6e#+f>kqKc`NW7cvbR6ORh{@*RzS<;QM-w7Le zwA~vllA=A_p#fkd-#ou$N%ggb@>bjKWtVF$Z07iBc>@)*P7-WL)PpQ;hz)bRY=AV} z)kaZ$r(?2cK_(U|1JmkZ*|{_u3r2D93L9slPRMSbdTMPYUn)(0Grm1|sY#OI9eLz4 z5EM@lG<#baOp`M7lICs4;?u{M);0jTF@%bTIg&@iN=$}g`MlU6bERL;^c!JUjxIa5*> z;eWsg=RIr78W7eSwqejIFOoQWyP z`{baA8Z?Giwm>tOVpF6CV}0|iMl(^IwZhdvX5!dzbi5iGoyxXi%~ClSQyBvWh1*X7B-Mb z!JoK$KSQ;!Q@6m?gpgivcM(;8sCaEgHGN#k&sF|aK|70NhN&;PEo zT&Oi3UVB0TyDO0Ol@SP~H5%(yM&rc>Mj_O0qwxzE2nS0CkolPF-3SEQA;-Z|9+ZH>GKW{dW^%Y>04z(vsZ*rbBDM9=30 zeSI6rOaSO)GE)wbImkxR*ho(hnMw^aUc>tn867 zZGZ-j`;0i_%rr^_0r|5RB_H2Rzzq3$+P=|vJK8<_NqC|Uv6k2DM(#JR5(%TS5sMpL zvI&;`By*%2bARSk3`Ma>j+x(f%dhMhMQ<^mbT17_J=qqt!aK%ZF92&yCWx^y{GKIk2S_!3q@TkQk8J?zH@Ipr_cKee<;a*ABlsbA2$1 zd1@8c%l*T?p2?|mt`a#)dd*4Pp`tIOW*H=rHEJ<4kyc#L9sH-Rog-97Y8G27+nVOV zlX3$hE~^s(8_JpUE3yQKJw_IWTJ~WI{W@q8VQ!}8=?06o@25YjAua+IgUm-JuhxNBMJ^KzkX@}l6?w_9BaqJIc7jA!f z1_HxBUpkM7Aor39H%n9Zw3ek>js)d7W8#lEBp{_=S@doE)>zd5zWq8@W>0ze)i~sF zq8IQ7v;^-oV?-BiyF{K}o zf#t2}owNvO3p5|<9{;;h_G&yb4d=PHX-5Nv9np#1+BMG;ya&r|>rx_`5vynyYSc6- z5!&Ke)2dP-=)Ibzql=h0hI(!Q&DTf5@v0A>%zUFX^3#5|kSO1-dzxXcQwlOmliVf> zjiH;Kh!tTkeKA_vX~H-{C<2@_?yx=xLliod;KgK5ScfiXCWUEu)GNw2QBhkRbm(6h zT60Qoz}CVkuZ)J@ka1vIO#9&j$u_rx;>9KZFXv?1m~5vuHBN1B&)ihfFEBY4Z2F4 zwO&2L|EBJ84RH!Sxr&)Qe7LKVz?tjLdx@;Z9@cq!P5653^Qq$X8EIhl#ep!IPUjf< zC2l)r9ci)IuUhc9vHu`XN6}fEP?n|(EOVTAn_P??1$`-Oetju?iqiugJN<}Clo%`L zPk4EfX7Hu?4g=9eAUuMP$Ga?c51ALi-C%S4kMQ$25?jiJOs7LUp2f$n$x{KQg^H31 zQR$N)D zd(Ylurin>vlG%LYF4BqU>jev8PAYM>Ie)Rmwc5b2l&WS?2+vv z4iO%#K6iFwJ==xNsF#Wb0t=i3h+4~wE7*7O`|p$m8a!OhFTS=xkYz!&uH~jlc~Taw z=CCo=_4f^U6WFYMGa0#2VYq3LNc`~e>~>@Z_V;W>RbhOz}~p&c@+bq)h1FJvaNp)2v%o}Y1!2t+TrVCQT*SsqS>$U?I@#E1|6 zoL|+jXUy=;H|E}SFH&-iHGSL?%*58&d(W@r5W$Ks$+N^}NDEN}v*>)RITSthW4l~x zPT`z|9A;k)^Zio%5iSSAs@Nj-gYZPG1vDaT5`_=Meu5myJqHF8*Pg=N1-Ua{ga(Q#Z7ayzH9Gg5Ec>ye1e|-)9XpEWpWkU z?CWGr2j7;)CL;rw?XHB5U%<4egS{K(wSHnbuCpOI?12v`ru(3C@MH;0f>o zc=CfUM*dTH8oUoKfUm)O;0y3-_!hKYA2;~5qI*2N4BiSC!6CdI_3v8GUVcH1xQoe&$R6%GCdGD`TaC2BoLd9yWj1IA)Ovv*r*Lel2TnB znO^CFEjZYQsl{E#MOfc@8tIvWMcgM7;fB$3Pcm z={X5vPeaU_5sdkQ14SDfNbpPUUB+7G2h6N|JuzZJtWxBSoHTa$_FGvg92PvKH#Eh8 zJ*NJCY{aG8c=*N|Z?OJ_qg52L2ORGhuqX%Lbn{ask^#o+J1;8k)0_9<6E__;Xp5UF z@!Ku1grrSHZ{K3*bAlP!SRyZH80KJFz^SxDLz}85uYsqcb0T^*(qe#){#CUCxlcr< zfLp0mwC|}`NDL9O0!(2YU*n6zTMcDgIczg*kzxkJo{CMMu@|vC&Q&DgldtkkLt(cI zo*Ej^K^ieuus1OHK6JNdA(KsMHgvLdC%Sr>pHx}5LL4CBUqbpj`iqeh7{Vd5P#Q52 zXXOs9*~Ei>?s_oT=MOO|iH1Nj;))HP0Q%8LR=#gH7GQ{`RAd{t$Ys7G;DX?(U8k*y z!uv=H#10MvYmsGVa{4?7Kp;X=5RSC$>tFo#EOAZ^MM$BC-L5@ISb?5%t8Zm&>d??> z41!$8H6u?jxGBV3a}#Se+r)8!aeWAN!`m>bF~tLU045jr^bgFcayqiMz!GrK zh*k+>6;`I5&-b2{9<1!hYxeRq13 zP$RHlDpb@+OPWpCA0Le!HOuV?O6rEFIPuSPIl;WzQ%VhTM4ykTK5)!hGMSlUt`6{a zkV}3M%4nK(W+^l)p#rEv-}q@imQE}o zl^LNBDA*0QQ=(P2;ijH`n*C=!3uZNFQe5+$^9m`p=tW^4;&`MIycH;zZkH3Lrr4^- z%S2LtRlmv;^R??MG8ym7F}di%a+UUxs@+*EYVe5!c9YR1gw71KbV>KJLO|6X&~fK; zrEB1-G#m4qBV8x+*O*S1C_>Y0JuD+RjA}w7>gLYEnTRv1&0$;^{)s3aK9^*EvpEi* z`~!oq@1f~pSPpM@+Vj;N5Bs=~2xd5mBSTHA%3Wh3vXa15xkbk=$75EBXN1QJZBR8MwF{3nZOLZJ$}}vXzkM?+^KmXVL04+``Fjc z_^eO({3Aiey(f(fQhQ+F!tk)8c!CnXJr|#f*X4EM-O&zSSk{Udfv}`q z3LJ!Zv#Sj(vb7m5?tEg+hDn2u`Xk7y6n`behbH~}WrxY^d-(u<>22FBvGDly2at$V z4Qq-ekK+1d1bd0y3{S}0k6|-e7A{7fG|UnkkGFHMw%BK^&S`*~epUp{**Lp#Zi*r0 zdt28db;-yM8Vf%SZ&;Fm26_oL9MIB|r{N3ntH#5<>18##+q-Sz_HR#0TqP$JUzmvC zPvG_4c;!w><|q88iAQ7_2(bV)iYS;Zy@5B5bUAtPTJrJNQ-bx#EDY>MrGYXnN5LML z{4Xe2pl~Icl9?hs?lrmi6ywB1n&_BGzSxb@gr!(Qs|3upl9%;ek1Z^`I02U8MS>vn z4paFGu*kMK=OUK}nHD-AQQ({#3)@?Aaxfl*JO=vM_92oA{xt3vYNRc!3djjHG#qLh zHp+C8IthBq3vTCa!hdgt+3b#wCX?!qq-jTzM3C>M^q?G*w>##f?EH*xpm;q%SVy)# zu}yM+o_XXh*3v9{`a`hl#Zs_qh+Zo)5s5e6>%2;0_(MdRl*OZPj#jvAV#GS4e}&-v zdX`&2u%(5GNr)LkOprlJ=X)-b)fr+TxeS?x;Zma@%8xslpLiY=5fO1bMt3<7mk=Fn z&Fkh>mTTgp7<_7X0;Ww#KTD#ux}BIVHF8N1!OoOkX&|^k6fRv&ijc3I2_9R-p{#tG zevsr_r0Jt*!zgRY@E-7!iPP_Y8N}Plq#kTF#a=SR$N17()GWFk#fi;ZeJnW>*9+5l z4{X=^m7Lf=*=&WNM@f4qvc;VM+9S&(Hnh{KR#}fy=-D=Upo zI;k!k7Mo5^y8#*#W*ZmvMrNAw&-&v7(-1$bdW$zQ1#4CJxa+aml$etbL6#j7i^DrL zZtR>QKQ6ga$9L4=tv>oM^^C%OiZ3VIN2IhLIAozcw)?f2d;HDd&^3~cr%bJm(6^S< z668tz_xA>gLMFvd&IYJ5=wK_P?>f{uqU5WU9G-24m?xs>0GU#*46^9`>1~nB)=_8e zE@N9rB~+|bIO|rYrH%trBmz7%BU&x?Z#^*d)LdXD!|8+;($jC=93JHH%squl3g6t( z($zGMBVq1XGf@3jq1B07g!Wn!omLC!wfAsc{Ti)AEr}L7Qz&*My~fG?Fcn{`aI`_p zY!WqQqpP8jslF@MG5q<|6W` zMYE$0?(kk$!2yx;*hwGY4k(Ob`YvN3A_13tVn`q~7JqE-ai3katFM5_Q?HiYQUnmZ z-iB#gw=Qg=Kr6_>NtWIHF}Ma(lu_TjeECg13SP2i#f? zKBG|-FxJY$Q}2((ywQEUS0er*cV&xo1Oolm!&&JK>%py0emQpL#6p7df}xM39qc>Q zPhf1;3QP%hil3Pj=Y0o)XViV1UT#fLka%2Oq~JvzC;#vTo!gCNt&0bGtQIi30EM`t zZx#97kAvERg2KVVM4oUVb<>gh@9Erq>QZN(bFXQqbUk#YZ>`$e>Cn0kyw=j%oFPc1 z*NtBsHI;r`IZ`l-gtXY;kSY>3tUDOPx?gzCD}PV{HV;(3cyMh}AJN>Df{U@*w;(_e z2{Od4<(0%XP&W=y^GQ-2JcwVDFX<%<2Il6tc;w(G1C_0?ahLxzDc5D#3VEC~U?0RW>U7s$s%R?d{ zkV>0(EyKJpp9z42uRtle{JR(KXBSsW1zJQn$Y&g$+BzxDLk-M#Y+U|kMd z-)AA-Sd#fHg0|%8j17l$9iG0Gzjg?losZwVLV0I4*If&Qe5Vcp03gbUZh)F9H36j0 zzY79D$_c*QgiGLu> z3#hiY(}{m-D}7qmgPHpk_+m8HUBfKq*w%d=E~hnNd{L`Irhk0lJGxN8)SbjFfa}iT z>R#X~-hscoutH0xEckfT%O3!m;2^8#o?ykt8gJJ0_Y2{sWL#Fyb3#uyqyDt{r@M?Mn2cLIPsd2r0^q-*>dq02tIE**m8dvEciH^~ea(Dr@?iB9Tqg1=@lN10oW$~0@yKa;ity6ivtzd$u7Gmp9k7-2}VqZO0 z(|ZOOSqo9sm{*S%wpF2WQ1$5ZR-FI??Y-DMTug%T*5U>}2JJ;fbxn%4Ix#9XYP2vh zb+Ny?CJ%5sJ^gdt>zWsf+wPBccJOG2|1TzlBxWEhMT~9#i~Rq)*>Gb5@c zXucE8X=wf&%~2NtS`erapm`3OquE&c#)kn$$8aJ_hdOVx68(Nl-|9hti0%}lMY~bA z=imY0>9>l%$bf&+wVb`1Zs@LLgD|3Ah5()Cw8h{_&K|G>uz{>J}j^8Qobf8c-H z`;GZO@IO_4t3$V?a{&PTjzJHqy_*gI1ET<=fZ_va-2i%`8!a2H8-=-lg2P61j^%%w zYZWM+0R`~Sv(wng4aG;(=sf&)s`+*%s;L3cJrvLny3^g=jpm~z=xSkf!7w@(k71*8 z@+fK?laChtkAyP-jWqy3X#AD{oIhbHO4^9hjwS@~CycT#{f7QeaNNJuqbD*H#~GHQ zlz*oGRow6&{QiFy{3Xx-&w{`4Zw3Fr#|r+5j~4tJ?*AV+s`H=qqbB?VA4UIxkD-6# z-_ZYo|B3!T;s5EcF&y>xU;gt)^^f}hzu;(p{aZcG9~dR2qda2?ZQ#G9syP6*#S;KW z7gpVW!hiq(2LJ$zplp8wjP8S@vZ_&#qX44-q5z5Kt%>W8;2w4Tb@td={Z}%{WJtHXeup zlF@Ad5RLAI0Fgihiie=vKy-gJ{e*ivGAau_)BqU${FRSYq8l6vqtnqp(;M`71W|cq zKnWUxpb*GLrR4(oXwZTX6w5+G768D-F&Z7<21^jg#RIb)OKG0{q^~$-eQ!i5ao(M^HCc{lMgCw25QNuhI~{~45~c~)m|~C;Tui; znC*YHbu>srDUd$qgJP627v(4%lQkB;;s3+7SOE~)mcQ(XN9`y@?ud*sbhUR+B+q{ z)Ugo*0CA|lM|-?v%*G(pj<7$iO-D-$QL1nK<_VBcd69qGGdf~N`}i;ajbh(M!QcI6 zgUZcEKcl~|C@62#7;iaBIqF4UU^?2ee3S-2r52Cr`Zi*wqjaOX|ML4NUlB?-9W5>U zxAFGB!|(0@`A^&a*72XC;qS39dQ^@2Bp0;~04xAJ${RCgeL7k;>XCo8>z}lv^B{=M zBBQ?l%WHoh=>VV}nVg181LYFq|LQj(MiAzFV6Od$;SoJwL!eLQG3q0;EDptF|gh ziPS$DB~+Deq-2ASU^$Ka~l`2TC(Bs4}bS&6@tQ)eXz0+Y=38 zSv0C!$IwvB4@divjJ~cCFMQNvpY+}8U?GS9oEj<@AEy3JqaT@;cNYD`w7g%Te`i`= z)Xqin--eqI^UCEe(7%OY8V}RfFx}A7-O-Y)sjg^FCZ)ZnFVV$PO51iIcZc$WJuTf- z(b*zN$4Dowe$Vj68b$aZPZw*eF`f2UUp&Dr&(9k+d3uo*^+wxvQbmoA^yquentV28 z1m9UPM^mQjvti;nKOK*6Y24AVc`G$sXM}(0qt!{}^7996PF{3HgT;F}VWgVI-sELtDw;N%xbk zX)lo(ok}+6l#Kl`Cik~0X}cVgNB$g>?kh3rycUzb-AcAnckVBhY}~J8^mt4fe;bqA z*Oth2>b>wzOww0lvT}uzp{$aIxRTZ(B`>;4cHgI@;*&}~K1azbhn1YWiEZyI`7!dz zYf3KeQ_@V|F5@U|RPwoEXMHC?_%<#qa+JAe!=*#@2A(XkMeQqUZSo4 z{xv3X>Mo-!omcV(Wr>4IzWuI}-_!oF3o*HQUCG;flsq>^J;tNo$)>Y zo0zO$pybVyB{Bjp!sSKyk1;frDS2%}iTKS*ZpUB7#hAPdd+GBeWB4w9SWJIM@Xyoz zO77aJWF)2JPsp>=$fTD^50L*X>EB3K8A`s-nC?3ilXW*^G6d)FFQ zZ7=|NcoYu933vvchqG`V-h`|00mSx{%iXXTDqtn7hfR=%JWRkh;AuDwKZgr&1>OU- zw_IkzJh&Gs;Q?3&eUJhV_QT`w6r6$|!5MfJF2g(UKKuvn950voP!6@w03Glk7?6cM zFaby5TW|_a!&!J4UWd2f8oUP|g4$OuvtTYPfofO-N!SL1;KLy}0nfru;5E1cH{f4z z$A0DkmOw2epcA&k!!QKnFagKmoA3-g2QR@ZZ~^`RH{hQj2k;}zfin0a)I$(z2#siG}W$FA#xN5QkO~ht_afsg*k7=X%aAE9C)c zkX5o;*6;?Pktiv_X{CioXf6M%&?f7NI@^iuI*AfD2Is6^BGW$E#5?vavQ@UpcKI@= zuAM|h{ltMfVjT$LVoM$-mbHmKvq8i>M0}MaPRw(L8Ie&U=smJm#$})Emjm($???{H zV=^I!STw-{l1M4tqq#q za9hS0G~-p(M!XAdxFn&PRqBNtZX{4tk=xAVkMlQ^&qFH zbr~G_5{^3-2>KC*ZLXOXE3j9r0sRoP?8cOD4V$f4TvWn_!lI-xW^ytKwRX$R81!lL z%{jZ!b|`wz03Q&gq-dR%ZHtqhov@9J+tJ&T2xQan5{~WUb*l**E$VPm*%1^uSu>HJ zQtIT)$*L&9(CjJR(N~2&Y~|q|sDW<^(>tjbn<=<1YRDa%EC}UB-w*7>lR;TvnRcr+ zYTBF*?1X0;zS)!Sk1lOC2aLS!PpY=*7wv8~i<-W;nIbP^WzAmV>*+dYhg&Ofu(X@4 zoY5cnIa*=8PO+<)9(?{Y>=uNtM(vj(kfxMVuH zgV{!LgLFBr-Wpav^}6!8T}7qa9JKgCrdM1NQHHjN%8|;Qq$8i1l7XggLwe?-I%EZQ zPR%PAHKkHvbkQX}#;~5GbGLWAv7jNeZZk9; zd6UQawNPN~DemjAHJyZ$rA0j{I*pvIWj!f6EtuUix#JM0Sb-Kbgp~ITeLT^Zntrq` z5GVHmO3`NIV(Z2G6q(^LkculGIT}#UuDsvuj0Bw&o?*AF{$6u58b&1Qy@5%Qs&9?R o(XB90*;TEclW7^%TS`wxu$l-)x;Pt>JXAZnd|bbH`#=2t2dN0!C;$Ke diff --git a/3rdparty/SiftGPU/bin/DevIL64.dll b/3rdparty/SiftGPU/bin/DevIL64.dll deleted file mode 100644 index 082f696508a95c7112e98bb8ca4af986bd357be3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2300928 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
+e_)p{Tw^MbI>vjB7 z4E}9}_tdf^lx+1i;6EF%5>>mO4_y`SNXTXyT_(y$!YoP&;G2o9h z_>TrW#DL$?;MoS8Y`}lf;42OICsY=a!Fd{7WWb*rFpvL9agHC_V_F3GI*MUZEl1AEDrydV@d5r9skRlblKjG!$*dLpR4ho z8T^xR_)q}+jT#>|_&ek9B^Q7%)%ZsY{<1jy)6bE=pc(||J# z_%;nrFkrUjfO{G+ z>k*+{VZgiB=)ONtgZ&2lPX*SMI|qfNTH_Nf)8OAR(bV}JF&nGI$holF7#n{_Hq>OB47kjh|`oC&uCbE^6;zboyr-e6KkC znaIhJ&oq9p!T)=;s)Rb(sFYcLFbg@gf)UU2lkgy?E6hklmI!8LmFEDf_J}NBCD@(MJe8yHGyI#HX{LJw$xccQ~x2Z_6+u+p#Oiq<#`k znu?mc*QkTTv;{yM+AFmO=-@brKX}-j%u|(e447}t&CQC z;c}|lFRl^q;bJ-8YCn1$SeEmx_H@*FvYcND72hg@o%=?z-Cy;hauspJ>ikphgU)X$0sQQkZPP633 z()3a;^5r`B25Npts9GGbp2(RanW*_-MBGIsvZv%?UKaEJcIHaOUf7OeYd`t3J|%ZM zt<@OOn8j04_TP^+Qn8J`c1v)wwGkTt-0;giE1Y~>X*j2UDR;!`+Jh%e6pL*;$@4_X z^TrbEL)-sLH}d>bNzK0{&kzi7IWgDI16Y#HQZ8*xcmAsEBEKGxNKfR{|3>zep2lPuqeVRA)S&Xc;uuvo}PYo^>!XunnB;&ql(YZC2Nl3~jRpfYa| zI}_9K63I{MMd@hTp)r=pCZE#P9CEiP&fZ!!SC$8|zD7?Q2mY zsp~CvFj8u{e>@k{ck8if7)Zeoda7^lP9N;c8|`#l*hxH|+8ph|#`_E^y2&o_j^vOL1a3*axo`7nHeXmdPb+lZ8`t7PgRuV-i{#0dTwdA1K`9-m}%vJe@%0XFD<%4PviqFFL zQd7T?g>&PwP|%r$+*lSoZ%G~!RUUY5@EkHx7MF=lDia^ZXX3S3CLW01-kF1$H~&`Z z|5F7tNa6`9@p@MQX@6YyzpQ`~E0AHIJVdO%Wj#ftR}&_NZpe_EhGoh^gly|hfqmm% zx>xJtL+;4IzgH65+PTOfD-Ek0DkHQh3q@n+Rg@b?uC#yp2@$+Gsa4dAvTv8*Ih-ZJt+J>eiQ3 z>?o~R8}wpt?nqIBW|dkSN-b{S?(;P`OJE9LeIhnlxSPkg_kFRmHQ@Yrz`5G*T~WD~ z6rVeXLBV^GdMb?b$AeV|R|h1yy*@f_Ut7jN>w`e3oEw7E7@?{ie&ZR!*vIbYixOx|}TFRYgOIHT&-BJ08T$r6!>|E>juCIK9s>+r- z!1P!wU_aSepBuAd^*KctYP!_ttS+-lY;H+Xa|_c#mv<}W$sx<_BH=Ez$~3dp+Gc-v zUVNqYxk=UAClh0pdeP6Fl{$~Xg(AES@_$#U59chPN~Cg&!v1uuDO0=tyj4+Wv9-d_ zJj1#h>?E>{y-kc1X8Pe$>i=U2@lmn0uh`lNmx^J-9Qc$*Xy>;}svT4Ivw4bv`SCR7 z)1j>VpoKXY+D2q95s&>?u#}lhS(p&ffbA@R+H6xPb{t;Ck70~lhqU7Lm|i^9Pu1oY zhp!&yfapjdn1i03GZfE=V&~VEFUKn3^{&#Kksd4zjqo{J)XR>oKIclGbB)#1;2J

b#dA*?yAxtV07C+l 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

LppjwW#H)RXy}}ysC#U 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 ~j=_X9o)G9k<_@^>v5d(Oir_Ut1aXOGC= z_HV|X1^7h%Zqh@<62kH&Jp1mH&x8CsKo5xd?zVexe)G59{*q@s_PU$Dn)>l=pE$bz=JyOg-0>Sf{)d~tv-z)f{6z=Ua&5;ei^p8^bU5Ondxd&l%Ej?Nj(w>!N(&PmvA; zPtbJpsQn}L^A_lWA0d|Z{NOVDB5x>d`1^^4e~dUFmJlWXwl>q>pM+29BTqUj%!Dw% zPPeZ&ME+{~Ne31a#eZ9ff0*?2GW_ok@jrM8`4>a{s$+ToXQ8_w_U|DbSVY)zm`9=P=Rn_uu zB^@VU#+xTSerf;PdgA>f_FjbULOTxwnRtE~PcA@rLE3B0>h8xc8^Najojq%S#_$2=T>3~>5m|y7kLU%#@`ylD%`eSeJBER@|6Y0PT2+MB)|A@!G zmh|qm>s8S*>S$B`xu1C2o34}bP13imTW_VA{xVEDuo%s6pM_uM|3^Lk4$|BD;`t^2 zMd&Vw{ru)SpRAj(D1YH*gDKnCjK4zP1-}cDKTA5Wh{gPi{BH^I-%HxpH$8Fsrl+kg z-t^XoPu%zi@66l;-24aceCJJ{`QhoC8os`drXSVF=?`mfZl$Q#(Q$S>{b zB-+#p-zqIdNq~rJ#&?h~9v9GWUzsN6n{9^y?GW;Sx-M?s$u0PTNcnLyx z!L|S45dZC@vz95$FZR6O;}?4#BJH;?n^n(`l0UU|y+i5wC`~%Bm?-%?+%o*q zU$^aCv`5CT+ein*62kU~{bxLWvHxrgzvOpe^0(Kkg`{3RJ5(hWnW@wwE#NuVyqI>3 zBE#7u?*Irs3&hD4`YG~^d9nrPg6J)Hv{O7UG%eLDGNSax$rbrS?^w^j*jXw|Xd>&P zM@!XL^GRM8{Bp+06W-Yw=A@7u!HX_w^J5;ZKp9VyKHt#Czw+r*r+{%F0G>*?h%Tvj z3BG-^o(@Ab`eluG-lP4jx=*9m1z+!;7`vtHIgb`DU-`2}UJrb}yn#>~I0K)^3iF+7 ztoGL=eA1@Ee3K!*06yu*VZL;T4;CGykM?`Ceb_fddKyTDY_(Z*|SU4(FG!{IFk~eDTeKCqK@9S1)0?;d*g$rEd0kehB--`8zBx?b#wU=|j?1CHVe| z>Q{z~xq&=qJ>3p3vDj7iXyLI%^19&bi<2k31@i2Q7#oEq`i4AOcnp=iF8KQ5i zdM7>FR7{ygMwH$-xgvY|OPq~*vIXaY=w0w=srEkpP26Jn9iPaE(iy&@w@Z=77wvG53cq>lLV?(=BlJV*Ii zWJKwWlPh|s$#ce&EjSmXyt5wde2iX^5v4ayuEW6y zZx&lmd$I-Rf|R%9(ay!_6&X=_xwV$E{`_Eb5wanMwH$-xuSO;K04*e z7Mu%G-f53^Hb$?=h|(J;SL9D)Yq~qWy!{?6%X3tDMMjj~IJu%XN1l_OY{9u8Zd3!wC0MAk76&X=_A|Q9<Da$cWM#Cs$+_$usTA7Mu&B_pC=7=#AD} zWpBhF6IF@+72o=m4#eDkf&#Dv_E9OGT_mMc#hjMI9XCR5`2Hh$rnC3+lxBK6Pkpczh?dC*eYrkA0#m%DyQ5&JH=#i95d$o&{u{^=x$cB$jqm z_GlM*j%r6DBdQ(6$rahb-?0B%ztnF+6IuH_+L-q&bjj<2-;Uzs39pRZapSbm_aJjN zq(|@)#GdmW?V|TAbjj<2Z%>>&;XNhg->|g&qD$KKK96>a=bxwDh_0x16Qy5llQ2%6 zvS)+An!nO6E_$@|#^`$|ks;-g;QJ*`uJDz}vk!SuLG+G$w39rC=TyljGNSax$(1|{ z5?0^qz%B^19%cGftk)!yFPfUugPQIse+U)DL2d+;J8nXlKFwylutl zmNqECFHe*`hKq3!d()fa?H!1qO@g}|_KNN(d!y`e_7=%gim=J~w;Vz1-*S2E72Q$x zM%m-+9VgE?`oqE5I~74YcbV)J-BI>N+2icnL!RJ@czd%Ew3Fa2M|%+6QT9gJp%2|gR{37K|6n$>=oTn_D0#`?3*Rez}9$s zhazaFz+H~^AiAUMjk3qtdzw7u2%DUL)s;)jvIpGduvc_P*&Ah#v$sH=sR)~#z0(o2 zi(H&)Pls(SgedNhrwbX~s-dqIjG`P#*U(p?9ZbLTmVMwZ zhrOaZ%HAk@oV_XX6eDbM_Ld@OsqL4?UeO(8ZL?5wx?H$zIVNWp9){&b};p(gX4K4n)u~Z#<`&-t(*Dv*~Z#;Z|^_^Z4%t&Xb+-0 z%HAk@oV};WQ;M+3`L`TF>)(BO>=oTn_D0#`>>Vf1IQqlE**g_MJ9nAv72Q$xM%m-+ z+e4n<#&~N+2iaD$TJ&Zlk@LF1Z~$%m&abw9c6EnJkIUbH3PIsA@;lOeWA@b$*Y6}|#` z$2{4Bb3ybLJlZLqqwc3gMwH$-xg!4zdCHz_r&D5)b6BRXmOa`ILGNSax$raf{ z+HZoFAhPy(v@!2l=#tk3-@kG4gttJR>5v>JL&|y9qm_9M-|;&cQRR%2D|t?%xBr&t za*E!pN8879lwOe$r8iEl=$$1`(UUDW7ewzFk2V{lS7b!#jgu?#FOsL{S@Go^@MwE@ zjw-Lnh|(J;SM>IuWB=#L7Mu%G-qRkf6r)#UMCpx_>*R;yMBNHiw{yinq9&ngZlOIl z)ToBRI@PeVZhTo>XG(RB0K-Yu34iC#^mt-@lUhHyQf2bZDl^igGQ*83GuWUq6J4c5 zUz6$^X;yv1#Dk5hZ|9nYOnKF8SE*w>(b<5E7S*{oz0gu_nr$eh#*?|ap#;Bx5h=^J zQS+c@1m59#^rcj0FZ!bFb7kypQoY#Mi+#P=*SoW4p}X8U+g_p!Yn#;Ce4|>6ytT+% zi@dcvjirg62GujtqI!m#RnK6P>Y1>4;_TEmG~uI0%10R|knh{>>~ZnBq*`}Usmx?T zb&jo2H3z5KgCNb@1aEiB`#*BroyKMr%V#HSDF1bkcS+xw_n7wo)SfSe(&8ONg5&Ttc+Lg`O zxT;RA8tGK420PTMCusi;@A%s4Roi3D3ytOa*<`79ye7ADXa&BmlIik9>2@?1N400C zD;z%+dAePv7WohFk^-A&%mFDf5h@@ufbo- zcrpmjss^pw2m^m_U^eSI(U*QL@``MkN+;q{szqJ2h{X}LbE`((RXdrzoOIz6tue*11% zA?vsBJhL>dZDC!){L)FeS${3gQBijKIx^DkEB$V~GVeOO{MgND634CA9KY6K*Cr0< z%IMNjwmDi{?Az`?$MHM-Q{j!`3ES-Ch4IU+H>1kw@-IWnT*EqhWDDc(X2#!5sxPrl zEzbC+&y|_iGdr7AkiUX)cMIe0X2#u3Do6yCaZlC{3-DcXE_OD#IBrhYHaFAWSQ8Jj zCYJSZdrGx47qzplZJ%f>26Tn%x&GwZQXm@%jsEDw#?P+Jw}2R zYlr(7JJ+pPo9JDU9yfJ2n-0~9Z;Q-1%%Q88t5;2Ql-p*l|6=9ceq3K4zfpiV6nwZ{(ZD#(=XQ2FM8=0Yv>o9OZvqrY43VGX042lUUXEgm!tZf z_6PIy2;==Q>ye#Z<&N35QfQrD-5#UlENL?ldu(0T4XR;+y_=i6n*|M`?JmEjbtl2UdIWI}-^Aaf!U31BPJHvlYOD0z7{a7)oytI6Vz3KEv z+2rF~8}@N=&ryBA=XJ6o_%c_pAI^7i9z!3d4fj0JHY@&Th*X*Nb*i1cL_6a^`xEA@ zWK!^EUppXtG9E$$p9m@J)C>&>=!Cq))t=gRLRqIFAFfXiD=|s1xUr3f~5n8nQ z{|x!^U9W)r&YPT{HodG*|6dFFy}Kf5^{2JEZOB=D@qOl(_e#XVTP9v1JOmeK@2}PO zLj__9ZzdMr`-p{S_vu=#@A<@%el@Ys?iKR?EHeG2m;juEJZmQZx1l^2rO?u9ezOxHG5&f^^`d^j2>)X`zr;=*Z$&~7w ztXFHsSf7lnrmbdZt39;UZrxU^^2D|;JMTr<=;vQ@{uMEP`22~k6zdj%{Y)2Qf7edt zZfAo&muO{u+`{^pGm5>7yX>dDXN|JAyH0g~ne|?&WxP4pRJAtH_CIFUdO3VuWvjEx zlWF?@8hnah2l1=*snkbN>Pp5v&SE7!5SF*x+$m=@r&g#nCs(SDN%r+)wJP;MjX6VD za-MAO4D{T?oWmMFe72tHm$Qsk)=4d_lei{8i=8aQ}$nk7_^5?b-F*+Nze^6UaQzekHuZU2;Ajr7x;X)>iE! z?ik$JyQ^n2=Kein?l&28A7#w_+LAH1DD#6Wvx}p2T?*&ZU7fgCw=vf1)cacMeJ%CQ z8KYje$DScdUOB^)_#R?k{!_IxDvw^@EV-YWt@t1+k1L0Z^_bAbn9#|X&|Y zN{^=x8(l-;@h?ht6wcS@J!iHzsx{1KYnad0FrTfV&O6I;lW6o!T@HQD=eoWY`+3D@ zQT9gRJiGn+sI+~j$Hrz}>1JN(VqW16C(&M6R~DRK-(c%d(#y>=QT9aPd>do-D4C72 zFK$(x%*mbXZ#wTu>$@sFhotLNI$y8SwBhv5rczZoecArAEBA6^y5n)a(e=Kadf!I9 zUq!udQRa5e_#!^{reO~|k1Oski*)Ed@Ot5t8JM|G7=`fmaLtf{~T|7_1?NNc1XJ%+^9MdtJUHg95Lb0v??ZBx@wbZh6yBYQE2oclGF=>PZz*>D zRJ9JLE$#Hqfb={fcU|^=HP zUgl)ZI>t?1l&A}{Uuvapc~|HGGp~ftx|WnB%6CyXZO>ENgID@qlpKe1_32{2y#90C z`0DaqYQLoEp?l7l^W3U&#o6iG;&kdUr*Y}L(Nv?Fm`j=_Qsaru4Qey{y>$z{<<+x2 zrS9>rT<1{7fSL22qvwxch4de6le!O&IpOuW_Cv(ktZ$#Sd5I2Zw~PHe{&Q5BeY(pp ze60PR@;rZK1MB2&)=OQimpav&C(LR|);%=e>h+7SFUlTgLuKx!9{jmGYCMW+1GWsA zO-as+cW_?3U9~5!RE@SjWm&_Sy~>r!?N#D%`kju4!#n#>xm;PC?_4>;^&C$7{_vlj zJRkSdK06ztWI0?oj?wi;<|dJSfO^+==-d;s_UdEp6?<10W!I%}KHb`6_Rm+bf9|jBp9f?Q z5jD1%Je70swzS+YbtpL}Key1K&x>#Wj`MqWShDe}pg^v7-f&sll%PeES4mG_{PH%ELcAoA|CxZ}iP-;_-+XWx*OmwVjU zS0G-n@;WT8k67gGw&_TpM)~Ag+kKW*AC&)Jyr2;K!(p7Wvdocp(E z|4xFJ^85j@giqPD@Jsj>vB>~0RQijw$}5+;$2y$i}lD}{XYGGqlpa3&#mKm({Hs>i>up;2CA^ndWIkfkVOz$0->t3V?8dz7FYjB3Z9JkGN7f;Tix%g66BiHthw<6{7mZJXADQ%fh(+f)n-+NzK0quse8#4~Ni25#vrYSD zEr-gk)AG*v$STegyHxtiO$!a>)NEa;Mz>F)yHF|rWJLMFWgR2&z)zNzUs+sSxgCB1 zyXE<#Pnz=W__0ZsiKYCT|J$TRo`hy%v2CMG-$*Rwe~wMZo@um(zGDfe_5P@h`^7Z( zi`+A^2hKde`_{URwbRDhXk%&B^MGw*vR9L_k&#o{PU;7=vk079e`I!W#>L(0qQ_U} zWM3ff7f4=tzrgui$}P|LzPeABP0HuqIVgb3l6K*?R8qflkaY;+CzA#zetneZONGNL zle!M(h$Z}TZBpz1Cb5JM)FrhY-y@cAKQcwO=#kKf4Px_+#1ej)SnT~Hn-2SBAi`II ztN4m@imhXHs`oMP>jgVMX+C6&f1IBl`i-T2It?xhj|-=omilRz#YtIuc)nCPO?iZO z-j=JO!T9Oz#1eKj8b2K-mT-Ys_(hL|=Mam{uea&@iN#Ofx9Ljxx2M!P?f~1KXqZiv z>c(qxHAB))_Ti%m_(kTamZZ8!K6&2RY~=qMI7vG>ZG3PP*moKk>q_YK_No+W6ZAh(3@_)^Z&QB6cXzH#mhm#o>w{773J#od7y`kutwR+~Z8`&3# zrR{X?Fm*9VEd6VkcoVjWUw(yH!eL_JeLb<%+Z3_zK48;7u=KS(hW{DFlK*wY!uNZ` zl5dV!*9mEp;l34gI5{rHPd|N2(^o7Kd{z+i+ zW!Ia0zeX(KB(dcCBC+s&f2-m9Phye3=}NHvJ zk^cotzhLR@v?ZbMC6;o$oLKlqZ5w+lv4js03!lIC@4vu&(OIL{Hg2CMI2WWG0^`5Q z{|+UV|M4bwd?nhcyhA1Oh$`_oc)`7mSi&ir7Jdm|A{Ke)Y}&VFNNi~~XBX0EB<+H; z<6_8;o~xtnIB)g-(CYawn-+cvn~6o{fK6AO&A4@)i}g4Y^X;KK$B8*h&vcvj`zvRJ zdOaDsH*k9VGZp{Y>M-Xy>p0Kh{cq0A`B-hYT!Io`bUYnUJnetBB|aDJo#IF zYJFRcYMX3Rslom#e1bE9uKZTc3^$X9(Qc$)6@E3T?g4h+O}-4@Fp#eao)om2!7Hlh z3E7M3ywpc7sb=>l)x6|GzdU~jn6+tn{_;WAC^jw6w~Z#%yCi+OMx7pOQuULKnx}$K ztx&1?HCj$3FKv9|9PbSCo^Lw84!ioQbxEG#pvs=iwmP-#!fO1q8GHFnBK&k(`-%T# zf1z2adH#d^T&q$uX(sSC{Lrq7cO=z6-I-LkztNPZD!rpg?I7P~@&)AUAzzw&sZhSm z);hIySoUY#=Pq~+uLnNC%WQ`KoHSBNLExsh06ufRJtExcn>&ly6^8_*qtUoYQJ z@Xc^)ki975S?N_t)qPh|_5U`u$af|oT%wb2U1w@kJ?|9N+=AR^*Q;me%^h6Ja6S4` z`o2K-y;mpI>wvt-5+74XLmn>CF7HnCP+zM|eTi)3Y8fJ1`udj{qoeRLcDwPor>yoPHr17adzW`8@)_r50rdM$38rp6zK3-duH*;m3x^iX>eP$yz^1tfs z*pgsVB~j&9r|>%UW-;R@Jeut-{NAieFOfkNo_J z_FC1B&l-oVzuIb58-8h$HY5JHDy6Q<_p41K+wu1{{JmBCTi^Szf9i&}1s~Qs-@5wm zu7&3N2;Y!a=o$Fkr22iJ>CMLes$8ZyMfIh&QvTPHmd$Vcd{@yhfM%4E|p)yA=Af;=S7O zdy?v}fpb=NRa%#&8M)Pbnf_Yl#}>6_xI@)EX2v3U|3T_@`uCD*>+dI3@BhFzeRZmD zrb(@aHwCXx(=xDO4YI>BvTskSGw(>MF_E#UL2a6(jAQfznF|<;Q;fwvPiAAC+L-6J z5NIE6E-?1kat=4DsdqA81Jfc?)_>@(4dD~%TJBJoEA9A}c|p5+!G%V3-F%a}X0BOn zpJ`EBY44jk`&!Q(`&#bU8+i9X`frx9Co=L|1>8x|o?2~t(shvk^F%QC!9+vi&k~v! z*Nd-$`)KpP$XiT1lIOdCAxXa8$)P>^xXk7x zWuTuAi~n0H{+BXH{`7m2>KB1?lD~%E$jPs&!s$N67<&ct!$#&f*S3l_fAPIZ^%bD! zf4cmw%Ex8;`7IrMxQRX@?{>R7%aK1Jwqb7zzYSDB$b4|X`s#F2y%;#`$qYQ)ax$e% zWqAMieM$BHO8!h0E^|1g4%3fb!Mwbgy^CA($@r;#`e&>u-k(&Dd-=yb+@Mare{cUPLGG0X9TNrz{pt}#bJ;>}tW}1H2#MqtK#r-J1C$gU36OnJpm&n!M z|FiCubpUJEHr+SOx^-wSsooB#w^2SlPTa42T;}>3b^YLJl}Wr$kC7~4EAsW8XtGz8 zznWA(2KI=IR=$4%S3Aaf$-^h)-R#6}(`JUc)zHOS_1yDy>Xx(YrOq&xoT^tFPVyUL z>~U7H*J)>M+hY5$*28!*`whl3U|RI7uA$vGvYu(E!t1iz@n-1nlIm5!qr$J-ShMnR ziB{G`tcRp7^t*n0&9@M8_LSk=?aF)|zxluzDD4m)8TS`y{N5RDQtskp%_Ot|@+G!5 z@r`8@<6krNLEog#<(+R`@9X&8vOMj9yV5p0W+&D)sdY2!C`%vs!1mz{@Nca2A8EhY ze@d!5fw_Aqi(0|lU$4_g!IcE3=lOa)-w0m?FXd)#@$>7tuyak~`Xu!$Ye3#RV6Kw& zmgt+EXPf|fe$V=DuC@x7Sj&6*Gj*(M8(F_L(6$%X_zBJ!uR`7yvW}_Y zU2XXWSANSPshTVEoy_r^!!h?y|4UMR95^LV9CK=$GI2%gb-kfJFRw)F(=LO{^U$M?PwM4s2l%w z;d@!zaNc=;THQaHR=+>9N!>8k#@Wbb=I$-b-TbBrXC7`neNpt=u}I2Qe3CWEk6GXR zzW9|kcXCAtN41PSuz_hBp@kD^hCJ%9OfW^vG|U4JJmoS#b4sHTr#DO0f)5>_=fdn z6;9eTxD@TWhIZ}K60NnWb+}D6GG0jAHham?oOV^Sx{5hsGjl{QV@wBYKz~0kHWV7T zQ){Gcy+iyUeQN`KVPgodG|#WX_oUb=?S8(m8YlY_@}|j~8s1cuU#}nM0(Ax7gKr$% zN}X)hby7xu=K2-#*gDmcQlAD2Vyj&T__#!u{7wq* zL)mde`{bN zpLl^EGM^-wfcJZF%;z_;(Zjk>B2ILa*Mt&M%(%mDi?J-MW+-d?$XA zGc{-(bFH-FH0`)ew_~3tv!zaLVZE|Jk3Z)7pB(-T%N^)Tsh0q6Sw^nU^Hk*)IGBDf zFu#mkpXaH{on4<&{|@Z_gLpsqJWo|_z?to{fqR#c>+{6Pbz`?%169)8-L9fNRIQEe zIG=f8yL#b;4)yH$PPLme!vW3=w=ov>vp3wt-Z1d?h6U=-^&cl&`pwXlDfL<4)<48A z(uZk>J}z-Xv$}zHc^&PtkMUr2Xv}glMOJ!SN_`S|P-Kyo=jSp179}ln2ImM>I2pHw zIaBcRWp<=E8*O3z(aO68UHZ4LWPK**2}{;_n`)WY<+pZe$Mhw~XU>S%GMDx;m&#gH z_Q+<-~PAp?Y|k{nHw0(ecpuJ7tA#?9;H>A-81X5 zPjF^=MZLO$I_jn``e%XSgY5P66QCse^mxwR1->f09tX|(&&QiJu3dw zYx-#^^&Oz||3{aceR8Ir8eUa}*X3YuY0hZPnL0a9UVdb*Il~5lI#R4j)CI~v&%BF_ zsa+}cZ@`quMNd_FSBu(3-mA!4IVU1-n!IwR@AD?)U5h!!7W6MU*SEiAEAoeKOsUhr zlUAQRKLnhYv|bapYI+rp`O43i(0fXLQ-S(wWWAo0u{Lxz>H5Kv*!kG`+*?--WK-%5 zz^QkOFIF%=GgnsO*dP08Jug=CBsMm3&r_?`FouWM-&*EiQVl#arH%uSiXM>>!kKej z*6p;Vnp+~uwxe0?K*vUOM6UB)*-Tj%f3w!%pKAQ}RqE{*Th(jNr`6GOZR(|G+trKC zbg1W@?o?Nu>Qb9ecJrGktJLbT9`=y-+=01oaBLO*{5rL9WRSCgYw1%v>7&<}HSA-Q z$3M%EJB84B$ii(Y^|I%)Unt@qexE8syOMKBAD@ulm81=JyY^0BaOWx0FTigvWW6By z<=%~b>*_hqak&>r4X;x^-K=4`kK`V~zcZMot?Du09>#!m^{ls+-Umo~9@v{wcK|b@ z%Z&lU)Ul6uV?YS6+bnhL?iWnEuAUD>NBPB!Ps6OA|Jb#cx++{^1ABD#aFufw^yqf8 zr&aA?{dE=ViY>NH%DHyIYZ6_xY9m!#A+z_`eFw$dg853l#8u05`6WKYA|h_!Y% z_i63JtaTZur61}x&OB1wms0lwHTOBca4@?B&wG6EdDu2S>FdQPpLNpyF{Plnc+7$$tQVyn(;a7GuBi!Ixk})bt~hxzEAsQ>Jm69a^!sl z{3>@`tb9^yrJi+wwa%Z2UzL7-F6n+rCwbrGAr)P257 zmB&-+aUgh)v0a{T`1O=p5Zh%h&wRa(`8v&fUB`S~MGyE*W#sg-#_eH^+sPU?jm)MI znFrakF>m|w&7QE$^vMg{E#*5@b|R&o3%twfm*;l?hb1k0Dtwp5S9SQMil%k5$DijN z?>WA6ooPXL2fCRDSxd+{qc7W?fh0O=>67w%eD>~Dr<+Nevn@mS(w+Pt0{ELbFHFgK zVK}YxFW#GKU+-jm()a0`>5H4_iyM(I=Tz$}?aj<(^{PC@+1T4sYWROqUOjhJ;}Yvu zvM#f8j;rr$TlGGtZ!XE4#(4mJLFTnq`c)(4SnTsHYU)%<{RHTLuc_ba^a}b|nCCL% zq)Z_ldY?*LPuo5veJ1@b+8}Vd*lGRXY-pxM#j~FLxIpui^FfHk~ z*QmG4`iON>6+Xp!Sk_El-UPoabvr>@j{0CvDMs< zt>K)jmpwbb5jV1qJ$oN}_JFhB^?D3Q-=9){1PtA8{4US;1G^}7k{%VxN5UCnsKJv;00-r=>_y$-vL{_F=+ zY7CeW{XMKv_^s|1eowV_utxc`M26oTpHZqMpQxN~E|9PH&l!tPlea_GP>XqeTB4hE zy_auR@=P=SN96Nq)%nPSeuWN&eG2;)kP-X^dp?l^sVXhsY`|Y-lgD2zo8`*`DRmZj zL}Z2h;M1b~F!Z66dL7XDKJs3}9VzqB);wpE%t4uX#;G~(B50#NUt$gG&I<|7h3ZuD z4Kgn-E#K6G>{o!i$d$J2rz6Ve)5GP0Qn<2UH~v8flER?X9Qu%i|`e4d27 zUxDrV9ADcyr_^r)QxW-mS~%ap z!>o&eMk_}Et*y&Io*x0jl25*?H75B;) z63WLXZfsUJ&aKpA^!9u$_lxu|+D#*E!i>Wh|7c2G2b4w5wGHZ8){XtFOM6*QrddzQ z`qk%2Y++5odUJ!`6Ug~I-zMGAu5P%nQC&a3i8J)gYRAkL?n19nTX@HH!|+ztZdby0 zl}hYvS356ssU7p(>dLuQY76fv$+?WY%e0y^vu=BTn;36b;}<&AujE&$7jcfZbH0;v zg)ZKo>xQ=n-VAg98mqSzd1>UeA+H^I9q@F*(}g@aYvNtT*S4$IvMxW7Z&Uj?W4?j) z`t|cI1*mA!Qzcl6*^gXYCPimHG>872B)Q>^t$Ny92AHg?R6; zQ}^TdU&HSy^7y=oD|z4K0>70_9rmzZtGZvjx_hHDmVHOeuP5<)u)SUl0^<*udXVRr@-5`;lGb|$?oh%w+Jm1@pUHadMvp7V_*dYJ zmb-$nzMgMmJe+Qy)j0FcslA(*IYWHKH%`5I-mhR^CI25n+976z-%RNiyHTECNKAA_`%$rat894 z$*Xo@2atXc+dJhvhI>VP<>Sqs&G}B=TU|Ui3E$mU{Ij06#F!)Z9$na)wr4^KjprR9 zbaV}y-+ideWjEEUn{KXGr^FWVBeoPhT%wn{VNR2~&8l}Vu54sngi4{Y4oR_Eh0@8>q&) zzNp{t^>swN!?j+Yb((iW%Dhv)@i~lhA4VVg<#`xb5dEa(`3Uf+q-9LvoTs02o;J>T z8ad~wW&W$;kv%K(W0f5JzPEYj#Mh_q=I5H3Ywg{94|9WVf7SOjt{){{(5PNOJvJ*5!3H+oodWN?U7WtxbJNUOk@4JELYj=+1cb-SHc|dUN&XVtckzdAH8u z#ed~rs#ni?3Gd;3#Q0O5Zv%QIt=nL;rdQ#x$Iqwlv*%?`LmQ)yw$SG5hv}bDHtBce zIoGAlG!NSH%e@AETNlxO#m1=v_39shIc?(#*1T;YT!=^NIK-2=rjfG__6nRWZ^$#o zbGGb%ld%gu+8+nyf6LUX>>2Tg=xR~rDjYuZ)8eBLPoj_i;W@`|-?Hvlm2acpahJr} z!<=WKXcYOtdn!6upH<;PJi0v9Jc$jg-{x5RuqIl~c}hF$r54se@~(xHBYQ`^dM_|9 zI&^VPAD7ruulCRnu4g=vwW$105H~(7knhez z_3CcPiyiV@0LCTVU8B0^xUZaPsK$$Z^Xy-|yotd^*75Q_9&0u`7t1>ha-O_iF*op? zLFHW^)-H_==>uB44BW_id@j|I;S`w?>c4 zi|a=5TdBZ0;`R0FtSyH;-vK-*Y1=QC;OKLHK7GGTpIl9!?4nOr<{)dM=o$E}di4#U z>El)|d_K;+t0M34%m1(AJQ*GCJ^RW$;l>$>#V>w*3u$@ZkTRO@JM8_@4UOss+LhdW z$^EzgE_`A+ny(AlQih@5WgP^}Ng3o_RQ6f5?A?5PVn>oa4ZqF8zO2pe!JM5ULrwAR z643dO^#fz9k4vLQ!uw~eLSBdZKum@ZcxJhe|)+K)NVtJ9Y`Z3WtyVd{(_h)3E=iXU$`4jw4m1l6iVBUp|Uss8q!u|E? ztHAq34{3S+4sc4+qiHod#$5sX=jWYSsh)AVhI=aHo@9KPOfoL5s^*t@V6LM|rv9Iy z^X;meb9MjSjLfa=>edTw>PBc!C(m~1TbU;|Gv2L7?pn^?-8-%0*p+x%qk0;1a9=*+ z-QK>W#`|-&{08$5_FY%9@7ln=YaRQp)$F^HkD2#rvLf?J*`M>?2D;Z##wG7=1l$9o zulk!2Ih*57ZJv0JcFp=t(e@N`X!fJ^>Xkrm$@nsy7FylMSSwn(JpVp${!_M&kz0j( zX-d5mJ$un3_iib4RLRieY-^R?gxpawUvx7sw8%Na%UadTXmi7?XRh$qK*KUl(%&97 zZ9L1m3cb zcZJK+q@N>q0$Yz@>;4OzQBj6q&fLezZ^euK`Ye*UZw+&w{Qm?mm1>ZZG``mGlX5QazTc4 zZ>-1F*Vd}nVt>1}S7Z!)tzLZ*NPk9r!yJ_VP3EAxtMTvXR_~bHt$ugxM)gMMuZ4d2 z%r5n^(@$5|ow|YVFrL92|2oz~1;(8>R^?5+vPr%2;-LD4^Vg}}=dM>*o_(5HKlfUG zljik$OdI-Uz4|X;SoG_;j(1)M`R>BQ>$M0zbKj)a)2Gc^j|k-<$Co|V4+fe^Ug&TUcc*6Z|7mYC-L%B)-f@4Kl_i2E03{9n!!HX zetev1Gn^GMzcvlpeUFx9--j8bA041=Ez=IB{)sgVFfBR`rPU$Em)p;-P|s&xxQTh; zYUYK2d7+(op_Vzr=TFFQp0So)%X&4#_}<3&q|bEq_o@Cl3;pKgN|pO|y?P^%`YgKS ze=VI_p&G&0vS;>biOuq@ncQV?w#GiDhw-srao7q4T7~TINvtn>)8KXYbxQpnP+c zXWn7#)%CK(Ptu1>8ydP$uV#ROzrsFuCdRn!<;;Jm#`2e7oTrgs&gIO_ophm$`(RN;jx)0O`+BCdQAdwDi9aPa?~AvaFeVx&P^4 zuF~tbVeViy(5CrzYjQZLeu=f$5Oc$7{3d6lHMhvTM|&fk?&7z;fl1LPch&GVfS2!N zeVTqZ5n1kxT#seUNwU5j;P`(ShCD&M{PeUEj_JaXqK4|Bi% zCY`xg&UNM9adu6E`Z_Qpx?~I&9_Us0>sr)x@b)2R4RX@(G|_HS@W~yQ&#T`PI$-O7 zvSs-{^&jGYmeqV-2D*{EB>jRO)cqD=;VCv08kJcuC3#q%J@^h1NCXT9ndxw+X3h70}W~`@Sx}L|=>?6=2`;bcimwwJ##`V*eMf7LCpXzrmqkI}Z$8&O@-L9TZ|Gx#f zH_`uN#|g)8W8Kc?X@9Kai@(^Q-VY3Y(fC51zYX+DTK3ee?`v4!SK&7`sZAHSGet&v zj`u#1lgcwDvmfx~C7KfEn^iqu@V-3$(%-$)KJ{29zH$5do7>dQw7X}}?zYhGHqh?m zj6lxIyJ>grjFHKQ$#-=#-^bL_{ zx&Lm-?k0Q9-WPV44NHJUZZV*sqDF{<_qdI^-Xp}iP^EE2T@`Dz)KW!VDOFU^a8c3H zHeyt?wnqJ_sHhR6qN1iXwrElRpYOcy{4(#GH-PQ`|2+TS^PK1R&Y77rGiPSb+|Hc& zcjC2Ews_4Qhx0OZ;MvVK2;8Z1$(GS5@|3cNc+XQGXOi&KK>fcFM|b(@yK1LyZ-TxU z`bQ=94K&y9U#KI=jp^rbZgHU5P7@9B4;dHYBHlXNNFR|sYZo`ESH&Isvu67da71yB zp+C>ZcbivE@n$cwMf!DNm`Hi7Vo!!SM+bZGt>L-tCY`UKi-O#V_l`*KFnM)yXF(sK z*1p`nb6i*cURkuOY`bV{TDNVXt!L)vZ|%N;Zx5Mq!OLZT7O3}$r4zV(q<(1GKzuiQg0#D~a^}u^E-f{S#`2B# z8t%m3?PK-x>NB{m9n>@Lv9-)KO*c$>N^#gRg)KK&xxZ~Xwip`Q29FBNYoe_dhIjo( zZK{j6%llW(ExZ`tGSEyqQDeO|?yDbBPP$g}eKFrd3vY>L-;&7-HmBmerYZ;@*PnFV7WcGWD>5`w;zW<^s#O14ZSh zjeWSebnQnC%hUDMf?iCf&ZBztnp~|wjMSIEG zE{@J>x3gG}@4tGP?K@I$yAL5NI6vsPCz})MY8`W0^iNba)!)@>XPX;sGjY0zgDvWj zmZ9bzcZPSkZKQQ5X+t{GI}o^a7P&78D_aHqEhDb2<+V@g`W<9WetvP|H-M7%`VGh z_#O6VpH7Gic^|~<>V_vdZCCA;V}^zFwO-nn>J0-fZq6G%iaVgVOYvLn?&ijs71NXh zIa!lCVZM+XEni)bC!#qdny$?cr~7zV&VWYL!g;^v)sxrQlkZw#Pq-8NBhIk+X?Qc@paU$E)$LYlebO-n|07qNMDep;?} zaql~etL5K~t)02!@Pz6tY9L!#S#9V1>PyV`b5{3{CBD4Kcakw?XiRN!<;@6^5nt#k-Mv`IzNA*9k@25 zw`0!_?9{YJZmwG^FZUH14#(qr4Yx#}T8cM)Dy!1DIGDd2(`ajsu;+gWyO7-NZD9T_ z`i_s;JY?NDe3p(y7y8)kjJE~G+py1wmw9#L;^dUiy_3UutG69A=StPXO6Gsp=FBbp z`KH?ZzI2<93g|}fV|>oYcCr=AMN|wDzkzZR=GZs47+u{id~J*U6EF?b!uQ8>=F%1D zF#s?Awx2UIeg?Qmdy2i-QQWs;D86eI>yfTuJtjSaHTzag*7CHJUEw%C13rIUD>?6H zzOVf!>5i{FzdWhDB4+G?H?-Ifkb|W+#{d6?`zN^jaJ#%qUJtV`L)!3Y#O=K=th)x9 zRK07sL5pO-T2pz}b-WM!m0RHAeG@ZDFo*du`fz@_W> zkh~8XekWyXO5}%+|F>^xu_<6W`H#-5w=*d#`z#|heqk$F`nsL06WfP5Q-!B)opCs> zn}P8bdmYetx33%aFauoFgk2h)JDgb0z8kWYb!}}<>)Xm}_Kglov!n4h#Mt+S@DYO$XIM}59}u^AlToV`P6`8(WheNVbWf74=D0rx4M zYCDT_`yJ_Uu`VWaPWEKnUU;JC8rnj1w5G_|-?A15hQ2HM^jE%52F{gRw9&0d;hL}w zOFZ3e2;;gtNyFMBU@b~EYdlq3QMr<}qT8RsEuD&icedEufZLQ-VD~D-$9onFx)dYt zYO$Mu`tK>fOt7dwqns(c+8X%f8GLR3<3QY2Ca-C+uK~|+w9I|~828z7tL)@c{xzJ& zKJqOc@-S{}<1X1(shy23ZlAu6rRQJYpU5Eo#rLD{ZL!Y*ecw;YHQPvC7c%&g**tez zxp zIQPVP?Y_3f9s^AKyox5_)A)zZYbyLLCA)=ueuVTtNM5fauVdun739AFedQ+%@AKpP zns_*IU5ou2P(PE@-!OM91Kh&CMRKm=bn-d-Vvni?`RC6ryWihp-vdTO2fF&c4|s;$ zjnYG`q;amT6fKMwJ-XobZ}-k(7p{uVFy}X`IJ>EEcB6AV*LJ5=Z$%sbK#P3=m`l>j zy5+a?`WCy}my39da2Yz$YVIdg(fiP_lmD{YhgvW@JA<=Uu7&I4OaIqTN+Gumt@!9+i0zo-}57TXZnpz_D1sV#scCkoL@-x#Byx<<`(-6aFx>4xjy0L zm%=CVB*ew%8%g<>(idI|@AQTjY%9n1pV_k~?H13|HL|%7k547?-r~oxq^w2Kk!|aN(Ixt!^@7NDc4n%>Vngt-U?%p-7gouj*~ry8VD0eB6$a zFQ0C)>wvLYwQcI~2<@9QYiEla8Ikt4w0l2yi4WGZ9z{QoINJ9P!^h_X*yl#BdAndP z-6P1Y&$QTQfJvpRF$Ua3LOZ6fdW$luUE)@>+q&jdSRMCphCOW=i$Io}#p4l!t274^qu*b4#bRFkHyakS3mUe8m7r8?otXdcEjL z6nYaqiJUo4w7G?|C-%I^Usk8em8F+H`*qsPH<=PjyYIf^)OJ7<#(daQz@2k-*kS=JEdUkdi=&$as1mlOhD{%*IH~(!jr=xKg z_vSYA)xFMSPW|;@pM9RY9<$A)lh&OtGG2LGOfm0^Ha*i~D}TTm;3uBy_W-T;dOA%O;uh7Rr{n8L?gI<-U0wXJ#hx8d9E z#M$d0e=Z(z4*rO}<61oCO##m7wNM^s^FwL6vo^g4leDiSFQPT{;}&}#Fzaa%FT_R8 z)-8M<9yew~PZqFmx4j4_&X8RX- z6*)Z|&UG?#TzAhYuaA4)yRj;#4SD}W2gmhW`bvE&O}*8?y&BeoZN11M@iFIaUv1p* zH1qmJc3gcJVSKym`*n+54qWlGg?1O>7PY&Kd|sHJChG&|&w8sDT{D%}&B@Rw^zJ=H zWFxYtitL$^Z9wKJ><<(No9SEJO}RF%|DpSl_rGni{XZvPS~IaOvO(62A>OTRxLcLM zyE6dRQZ>H1`N-n6joR7F@0cV14{hD0Q^y>L`|lyn&4JQ5H!f0d;W|FP1BD#+eITPF z;o9`?Y0p6CeJT_0)A4;2Fu9#vQ6A3CYl8ZGMcJ))UNY5e2azw&Ko(!fnA1@y;yZJyW$0|w^RB=zV*aGu$y_wV`MX11j1j4O8?Ik+kC!qR zD6`k`k9FxYWz3XE^)N8%x;4oR*GB#LqkJD-*=nBvN^^0(_5B6l0p-iJg*xY-!qJbzc#)G4>;ooyexlpG zy44;FTqL@k>UZd<%wjjJ%G+S*;x;nBlr8BnTx@eoZ&JlJx*eUaR+|9s6dmmY+`q!k zyqC8Ju}5*-ZP>;?!aD+oc&F!J(S>{4TIxVypU}+tmfnWoUgOo2Z@!zmA)Bo`pcnNN ztY>zU?Vj2~o84+#hqp0qZ?|>W)9#FRI9WKfrPba6bpMAh2X`T6u|ZZ%Z2fYtuzs-J z!f=svhDW(`$livtaesW7x%WfQ-TauUa*tqlXcYL8(#5UsOM%-Gx4z#5Tqn2Vf1~qH z;b_;Xc&|>7wu>XYaP4#vYxO!~0hxkY~2&3@hjch)4hJGEFOW$3zuG?PmXI zKDJ*O+pc5`8kYUY)#&W5vM{W>ON4A!d-3yy@n^T%$AHp*C*_AfA+9#>fM3p*2L7G! zFP!^{7jr|c_FLehfKG_3&7Tv#4?U;VE&*;(c;f5(a^PwopEMTXNIQ%d=Pk3w%dVC3 z?y0!F81{Inn+M(3&|QAoUhdsGKZj5HLo`-lKk%aH`PHXJ&z^c(biwd5qH}tOqVD+% zqjjTCk2(jR6Lmz-jGQeWmdl+WD3=-kpmT-91LptHswGinJOtX+>+L+t2%uQw1w?#v8Bq*vn+>nz;m-I@)SG ze@lA7SyqVm>-XCJxaWue&)5%H7@HRQ-W>WkXI+Z?S32q!q#fd-a~kX%>ilHdLQNmp z)6dp(O)jw?c3G=^3uyeEcxvwZC%ET*I?$7U3P(C&ywsSm4trwsYiHZ{0R2FEiiFSI z8zkJ^t6Qz<@>aVhNnhW4fh*+JIX?ZrgSOXNMGn&MWj8(*M*2+9sYi~4w49tk7rJ~0 zd2ss{tW{aZFOn6WN16XvW}~-zxYc&QmU-*{h(EMBXrypXj;C?49M>MhLj85W4%r8s zCpwDL%eg>+^X~<&ryTW%nfGySfEqw;jvmUi@i$=W`7QK+(Q<2sb_;RQ_L6O%?O-2q zrEQp6#Xb)9#;`k9!IoJ3=Gkgj*CXSt_CIeYy$4h_bKiUa8a{fuQ%!a9PvNSKbz!`& zcNgs4z2&;6&#$O^(riQBsiV!THCk9}w6fM{tDS#1-t{tmzoXTj1q>=p$A>h|@8{@@ z!RP{Q%hiM`-CBin=n+H~iR=_BR*yX&)m^7v4Z-bj#u%7q?^CaM^BrSb^7&s)iquK8K zYdF1CM>{Lg?i$HQ7(ddz8rtHqw7>1-Z!_~6*Z#75uzBrqQQffr_)n}o|C#a4y5Sr3 z#2D|+Xc>P}ejVt0K{WR7yle`oAX~r2x*WCBnxMzGijN8tx0c~I5 z7W}9?@5gwo_iEGr%R7nm%Z~XL&g0^@ zU46ft_)M$)66lHGRU+F$T()1jd&9`AZe*71)Gs=#tBuq2d&bb7&$Zh7fpJeigL7v& z@VRy#<>y;D^=fyv+UdZYXi2URFNJgCS{j#k4yJKq5;}EXU=OQKv=-V&8gDMlDTEtP zIMFBnikHICcEWhIG_|fFkM;dtUM}2IcI`5eZ-)Gu`C_a68kkl(dV>bPPUKq$@~wn_ zD>Bc^xG-!e<5aHkFVjAO(lY3r+{}AZ*yWsEW$UZh?Z%#GBX&H)FcEJxJO%sUPr#0+ z-av0i-ozRB3Tt=ZT+vDR8shxjo_5-nD_?R4D|I(I+kZQxtFgcXzxIiFaSDCl5%ix^ zZLZ2*#n(bHK2ok3a7WDV4p3g5MkyDiZp-CSfMGU}E- ziOli#GQBM+H#fhFy&hnso^;^dNX9L{o4&=kFaN!~*#(`|Ro=Zvex>|$HwM{Qn}-K< zBI#5j_ln5aSa#OA@o?Zft@c;I^`f2hyAbF5U2RU0(S1;UXTRHO{{_s6hSmqYdh4Ps zjdyidu*thy9lm~fcYMEtd1>otY^NbUPt}_a$@!9>U--V5{433w8QLju1$5-r_aA`~ zxs!IX2uGg@P}l%1P&Js!Qc(MI-o+UoW) z$J^1{!aOh5bLpXJs2p=YLSFxvv3A8mIYL}?7Pj>0&nME?-I+T38?N0u+q&%C&^C6^ zHnz|AkI9=c+~Wm`8`vCiJr*uiI?$MZf@ z-+nG#`&YOF!CX6ur?QOyigqx^`lx}jXsvNjatmFp03W@SH$|E2>n>pSExlSEv?o47 z8i~%8f5Woo@7u>Q*v_S1vg;+choksDod5S_d|I15jXBRg?lv4Vh3$0aQR{}Wt2fBn z=x`A4;f7zLEiFpl=Og>Sl%7;pj_pJbkG3EkvaH>gcplt@PBC+OWPfd6%aiSsh ze1VtFTlf}ibV8e*zb2hc`iAYv?ZKax0mqx%Iz~_fTy*o37jYdKl+5*?3*pW!bkf zey3z(o81PS-IB;c+VXgSi*4@a*mU+~4*$h}f3|k(D!cXi6?Wqx>|-8mwin!6X9w3Gf-kx~da^C60w#O0XZ_HJU`AFaPHhThazw)7ZyztCb7WrWdqKCQ7M*LP1uaR)| z%w@v(kqiB3&6WZTAO*;SS8RM}ym4>Qjw+(CG3I-dxB zEBK8eUiE=2-#uKwMokeLHOyuGJrnLAIeDyq-NAS}{}0i|#p|9^+UzU9-725fvYLmn zRt)j(Oq99SPR8P`jKkfG!|ND_J6TV5um>PIV~=dJj{v;|=yd8`rg&{$BEPNl!A8na z&zKa3b9yNAyyZLV-$!BndsdsB0lexV=!D_&=o~$}&AtiTDLU&4wr-yN_bO)$Q_SH< zwb##j86MVi))0nu??BFC!+?5lZzGd8w;w@$NcTW`_;ZhLvv-`sm|BGP=4RVWm@bu< zFir;{6UOb4P|o%#omCaB>00VIu`w?DfHnQuL^ioO8f~Zhac%Zd;Hc6`_LH^GoWg6p zmc?nln87XVBPa8#Ok3=Fax3?QNt*uOf)o<6vX zzP#PmRCm~_shzC7j%8nPH}YwZMHdw8g6cMOsE_5GdOPRTJMiC0o#~=f>IpLpbdv$d0{iBkKIu(M$NIy3iim8sILKS9Jm3 zL!4_j$h+DZq35~grr>3hK2P_(R>n*_^KHnri&wVU4}n9KnAiILB`_>^(k>R^NN+lb z7p{3kv+q!wJqGwvLQ~&Q18xgwF2X@Gj2E6^)b=ot_Y=_hF3{Lc{?s3d=X4VSyqkB5 zU-ZBFe)@TCpPJ32N-y3YVkX9rc)|{is))W;L$*&&lV7{RDTd=3PWFKR^3dgTO&QBSe?DjT$0dTkSkG-nL66R6W2J53e zpM?K%g$u%@#xIRm8ov@5EZ&X%3-l&wH>;qhc3Z{XZh(tUEAdtxZ?Df*taqw|Im}At zFsqou#5Q%i^q!s5^4$Vc(v4%ID$HoozPe)1a+|xafT)dmruan&FCF zIoNJj(B4j^ZSJcwPohn>s_lKMVxI!{F~VI7{;h=F51n58dx*1zIC>Aq$&qg&NA7B~ zIiDVN(ih;Ox0dX!^F@0Tb$ocV-7ce^wU4E}3g3pKKUC}wr1vY*`wr>-J86B6H20F; z4$|8|dhr{%u{@ZYX|rWNXtQfps|}j_p2K}eZuKG3(;k_^)bKZ8gN(dgM!Kz}?_>da z32DW9nXBA+jXUp{rCl%&a_1|9MVt9Wn?387tOt^GQ*POHC;TeH$M3hM!$-TCu?NMQ z+{~@E3>R!Qb5hqPkE>wQi*q>29@^YrMcyiuz0jNJ!TIrIJ!>`Q-g!C$u`FP(>o;w- z;=kJL>Q3=TJj>vc@hc1$&l{4xVn1)l_Lt<2PxwwvJT@<<@97<|2HU{gZS`o*y%V4R z969g%NNv9+yZ4mc7x4M>{|`pfhw=ZJdks#eMFo2#aBCO*a=hW3Il#HIaQbsfSJ#)v z)6G5K}H#q6Xjh6~G1N5v*>IuB&Ng+Po8xQVK z?eKOn7vBA<*^p1lrMfs;DcDzm>qOh1FF`wn586hEubn+6ZNs@Ki|0x1ot8y>V{Skk zt+`az(#isJ26V~R`#NF`h@Z+E`nmEV-~1k;%ln}82^(#O_%B*ju&YEva%~aLod5d> zGo~=AC*mqj3YUo&+x^vh?!kRJr#G3iU+CM8@AuxF`A_6-`HTo5aTi?$CM&#a&4YA?o zG4>C}Qh3cXdRxa*aot$7e5`wR#Tfe!W7w}9YaDG#r59CrCp2msb9t#Yjg>YOYzJ`B z#yBtfelM^;aqIg7z*gs`ZU)=N!KM_=IK2k)ijL=&f_)Yk zacOA}W%(HA%B0skCj7qv&ye56*wP38LhzeOuSB>;(o2PLd}|mBX+~}>-ZaMkgiCL3 zTfw@v7p&^iqn>!PJB_2f;B2rijo*&VRPY?Iv?43_8Datx_vacCnULc*c{y%-j6OF2W(b4@MEgZ(m420jUE)Q_j zTET|pc748VDV%uC8cH;3@q}YN(F4xYcJH{)WA9>?_ES~+(K&DjeK20zjX$bj-vaJ) z>DEz3@*d(wct;!Di-sF*m~nRb6nqK%Bi^RmGf&@}Ro~;>ntg4piPfJcA4`2bjyc;l z@fF(eCyk?S!HJ(~e45uOKiPQ9@H+I5?cSvO+Q8!r_FLdCmk)E__dNl91-YHUlYNA!GMN$j0p|FINEgo&(NH-1@#B*e|z}Z^+mz4qm6? zDI7cvX-Be41<$uq7aQTZ&O_b0#q<3c1^YE{ROzTq&`%}<-2bLe+Ox3B0PNnOvQcl) zRDTt2Ezlu+rcb8)f<772%IlMUt?1H$AF^fNJyft40@u6r*`vz1<#*8m<^u8;&xp4O zm!Y%NJ#p3D_@5Q*mq2N!%4_cXtYPH8=pR^N2PpqDDgSchjKbCYT-w}!2;xV(nrs)c zdo%I%ChWpHbjZ8;W8vd2`rU5IxyNRnSFq!skL*j*&AR3HXTTNm-+?`6;;tobBXLuH zYS+*X<9E3;v*T>%@CmkcaG!Pe9?zc3UZ=;@7a8SLe&Tv{HlN7r*b58xPr%M&Vm@Tu z^1Bb{kbhGDE0M?25lZ>F_THGH7j18{?eK3cywQE*1zRQ9*LewZYG7D&6_+t7#5tSS zqYLXJuR|H$(@x%K@b-~q)y zX$5abqlYk0{a2e^xSF4nC#6*Uu3p~wor>SO+JG+3n%?*=N3YAS^p0M_7z7L|y`;P$ z&X?EgKGe#)tHpM~8{N;&>c+ToyrN)x{+#(iQVxAT3AnwM&I;$B!qIo;f_QP-$s53f z$r|y1#d3gjr6V~F&2IsnyU4%42bRHyX9>QKM`v>fmGxn54`ncOuI1NY{y(z@iw-xj z=NR873h%zTIv*?8CBXH{lX$AU2DqqoSxx6f;d@?Fu+xFNg76{EwGrBlTl>5FVVQSG z^}g=ta`tRlXHpKQUvrp!g5>=(>7~@pHeKCwP@hV#kNX|p1?rDW(qn81anTsElYaE)gL}J8U`u>>y#CvIrN0EOu`rH8waM81h_N@8kcFHX8kyf>5r*}ZP z|2L?*L&xBbn9ejbp8dXp-QejEKEy=_ii~B;?M!IwpK7$-&~a~l2Xq$R2grFZVQUNX z5cHU6_u01;#HXRZ=gr6Km^bWA_ykWvoYNJ7cU$0(-X%!wzwYI&BJ#V9yloDzqKfRu=7x)*&xi z%x<9l0pp@s<{b-gvCai@F7R{mL2b^r_w4?1;`a9VRNjG)6zucBeWHEt3OkqlHq(xk z=g==Yy~$1|&pKOo_Ro?$i^kYT3w951-to}Dt?!=z`{ho?xJ5YnKp4;Ixzi@y{`z2M zY)Pxc>||Pq<*rOyR{R${og!$I%5@G4%0* zy$-m|(Kh#eHSX)=R{4pq@+(XYpNxlLTz^m6-;?zD9Qj1SUJJ~NUK={#Dlhaxyj$av zPHkS|`I!8xUgkbou-Q-Xw%7^KaqsNLbV7V+gVn#oR?}0>I|It}janNI((V3q!9EDw zrgS?POUb|PZ;P*?U(IG~zr`~CGwf$i(FYVyd{nuK(-+|VKDz83*C+A1KU=Uff!)1I z4}Eg_OcB}EOdHYqIrNX$tL#Y+X2#ny8te@680&#&+I>*AzB>!{YG7PxX^l0@_`-ZT z#7CMVRynJfYBrrqZe(s=W{mJQo5sITu>S_;MT2_M_p&c?ZXtKl{ubfLTNtl)Hj?lm zI=^D)&!by83ooigJC(Ka$*h%kvaa61S@#SLO<&xz!#3pqKDE^8Hi2F}sz(Yp-&qf>Z$ZMI_DIbUDL`FbZhn@*1) z;N9fc7@MlB(M6AP>w7(Lx7_Y78anQ?*ozABPHr_?7}xEARC%+Bu_qpX{T(~z314Sz z^bOj=Nzim_vKv#luCJ8rE3+T5FH}Ej|2#FpdH7AXYxri)#BbpY{#MHVF*ny5U>WRn zrE6z2OS}i^`+&+c@-6Jmew%Sm>1%z)*jb@W%gJx(7oFIGJ~_5c<{8JQl6FU%t-phG zGkq-9lWjAjZ=j%Lc6Y&E1MECG=3~mOdn;A!ITE%p6E-@l!Oj|OxAlYAjpeMo;U3a* zy6D_x>E(^cYb}QqE!W+#a3d|##;8*=&po1_yEZpQdzTKzpy=pall;WG1+opg z;OA_CkuT0gkg*HxYy3y-Z2?y+zGMyldV8ulspHT;dRmh`4f^Y-W9g!54yCgB_L6O1 zYCl8w7VOKweWIVpf)MBIb~69d7#N>{Cpwi$KU-=&-uE;1c!9G|N$4UQL!6thL(|LD z8N&V)81>=t3vuz77_ZIZaSOewpCj{uS;e6&`rddS`+jnFp{F{}8m)>6Nr& zPsg`o)wTV1!S(^SiXLx9?uEBq@V4!s_Qe=asgE#B-0#`bp&mlJq@p$VJI*-%hrL_T z(%A{Rtc~c()}#L#`bF@h7k${R=+bthS6kDg{XKmM7&wjiPpa6H4z^q0{4wZ&9cw++ zUEK58&DqW#?(!Um9>HE~?Cr7fAWr;-!tu2?C~NjEGj_C<*X|WXdkk=m(o(xahKIPg zzrx>9-q;;X?))4u*FPqRHw)aSxF@vQ3Dm)E-1W5MRrkbn z#v6W4WB>2;Hyr!(EmyrHFNFBTomJLkE# zz`P*DN5{9>@w2@9Ta~?C#;@U$tr<+lk2~W047C>R$H1+k|7xuPu{4cc=( z?K#B9_n`f|g4*9x+tk`Oe#26Ent;Z^V$rS?9o08{4sp>bMLPu^pGX}X51;qI=VRdW zHtJ$i!t3am2JY6y>+jayWFGn;Zx&9L(Y>pn!+B=HPx7U4H^fJ}tHqeyJ|+9W(qBYg zB<<`dagS?9SJlTSf0BVi}u^qg(Z6M_M-8nxdTv?C&ETy^A%^Tc)&w#!>1Uo`n9P~Mpx+tUF`TEZA6-UV#(1^m19AMGbw#@r zm{R=p+;!vpsDrwR@3}GtG8YKLIQzb{*sreYO%2v{*TQqR9~QA6hQ0H_Wt{cJ?KyIK zG!gqx=#zkN^Z3MWn3q?6Jvph67V z)Er&l=4M093v(i+GjUAO{srjlkJHunM~Q+QWL?AZlz-F+IrKFZ9g{n+Kl zSO4~a=h?tA`v_oXL5KIOw$R@@XuGYn5A;)z>)06$!nn5&XkThO+RJK4 z+FF0Z;_7MBiCN}VcM8`w>_}0&$}P*M>f2ZcE1fd zqC2&|{YL{8-i$BWqh?u0SIdl<74~Y|Ia9<=O?U_Vy%l@!Ja@9HMSB}-YOSZ&u?EiS z8oTy!M$y{NL`>-KSbxoNbXd(t;(ch0^AZO+{Vq0L!uC$<-h zb~Dg(PRy^Ay9b*u#M?=G)ji2h+G>e-jhT3n&ar9RYp1ZYe>3IOo?6OJb3Ec}P7>0HWh-+7u8=6K!R`f^v3^uBLzcy)EVe^ev;G#=OOD*)y4| z0AooyDffELLI~eU_!2yA988Cg^aeS7d25w54}EuKa=&1c^frb`hqgE{giHtSO6aHD z+M6Yu&hSHgB-_=r@pdl{k$JH^gznTy@aHredk+2M0PW#1F@I8SXCLY$>OUPWIv$;e z`4jBe*ZWmo=i>JXJLw@43WOP*mf4|q@ z`o{Tn?ymmO#YMXjm{WWw%dw#u;Qaaa8v2#n7dViljf@<)gn0{a@NvY2kNSQW&?mR{ zt0|w>lqvi+)+fZ>MEPU82)Z*Z-ALBZ0llapvIbX9#^X`m7z#Y zZGrw&;2$M#$8W3hp6gVrt@rnIybS^JdGybV_OHOK=&8)34JlkC8yU!*Sm#1I5pn%S z+Ls=c9W&;^%tIQJdC0&a>Hs+HJm{$%B6AyuHFttXfuFm}KOfol**fd4F0*yW=T78Y z`LH?uOqr->ZkO>5Fc?@@vq_CU%}hM@b-o9ay{j56A!NyojPOyY|o@r#~ycT86i+ppPE6)zcP3|EL$87xJ*3Jgg<% z)zn?png;yY;pA( z`n$MyP=0s6X*qJ8_H)3f&t}G|WylcZaFs~_?b2wkBX20$Yk^78-pT$vVYU*cn=tDL zQzVS+JGl9K5H>ys!*KgpJa)gaXeR@mPgK5JZR0$4ObD}@FuL2% zT?5(#drv{QXf?VqluNc9v^LMm>sW^JF63~;`Xa8==zC@R9{GJFc|V1`A5XsbkZ;|$ z);U(EXdPBQnTNiObsBJ;@}#%T$y15^G>+2lsr%61*%u+dUF0?1Q;B7LbXM8Uny+VX zjfK@vu~u$zKQyIbBdmOgFJeRqdRnU(S8J6 zBf4(fe}kwnPA4yCke4&bOCNc8WGZae4({vs_A>`P%gsS2BXjinu0city-$MPYVPo> zUC<`FZ8qSeAMZaqMKbx|iPsgnlUhw?S>?jm|N&=MWqE?7I;i9A>Xt+miv2d(vt z3ma15qGMOEr@q=YR=YU!!#?Q>?j`R_Y=ZDFx;@tx?Qel8(RE{5LkgEZx1gW($N4$> z_sprTV-L{Bqy9tOLYq#`Y?1AU2GRed?aF?T`rX1?alxM0`-}FH5AY7l`Q%4-EUW18 z440ULF{TB6(Z^aiFKV_8^BR9R>tS8C@U|7_eb*zifa^p@^MonJ`FZxOs;toizvwA? zW2(`f#JI45F{DCY^6QgWzNGiDCO$-a1|ASyx2LN;EaX0A3;o>wGUe31pU%AfnnOGv z`Eb#00`^}34e^|{eTa)>`w4k}EbISGqYbuh7?%->9ZI|ExN_??KK`u`^U*5TJ4 z><`$DMSC)ERP<#d9zM0e$9nh_`bEdLGT)RfsW#h4**oEJ2Xg?uMWK8i{RnbgAUfQm z*$)3S7wLkB9q==LUnQPR%Yv(O# zTwXkd%g~C~%8C8h1Jp;-r=0#G^`h}c=^OPi`LUvX0qA`)Y3{*REq)vDQ(YAp)0+vu zoO%qyxp`|+*SR!rgC=m~p=bu-LYmPf6}#jvvzOc%+4FC!vnO1?%zCb_x2;EzIlNC^ zJjff`vLA3($?U>($?TO8{wz>tm zwv{!{4#FM7yFV-K^6NY7(A6vL;v=i<&kn7&XC3Ucr`}t#eRq{@_nj46e;YPCqkUG_ zLw#sZso!V^%mGHx{~AQ+|6bmW116qAJ)E+FyLpUL_(^vs^b7S~e7)xGjr@Y~{+IL% z(Ni0N)!AU@q6g1pHP=_4Shm$2e;RI7{AQ@+PuA(s2AHC z$k*LdvCgFCiYo`PJEOTMW$<~M+*Y#R0oN&SNnM4wsJzU|$^6yT`P(%vYTiiOp?#?R z%?Gwl{87S(Kky_G<%N z^xTp?cb+%p7<0E(IUAee9Fny3hH`A50lS%Nnr#g@|F&`spU6VDCWL?5w+-)uJO19B zv_t-tpRq@m>;~Z6r;#6bhQJt2yb$m1r%>0csp|@L+?K2{6~E`4l06$3SN!c-XQ8t) z#ax#5P(O%`{J`Ih$@9>e)!ak2kNo+e)(yz*Ds|4d=Jtm0>pr(+F9JHBuKMJ@HQ#lQ zE!n)%Qahhjy|<JbEgtXDIekgV9`n+YO+6#7tF*h$_2q}exw58dEy(|c^Q5^EY^uDx zWcMpA#X)|AxVU~<_w_HxvYJg%XVWI%rb%>BgI(0iTea{>Iz)=Ec6W3XyGXAn*^8e^ zdTvjbeFFFw;-d@N>;l^M6X5m9@cKAt977-2N*^fP0Ue#~QWx&*7XO}0OZLatlU#i@TYvQzQ2-Xosq zq;MJf(XN8+T2%LIsrxm|31ZuVNB^p1Ujwf8`Jud$*D1Vfr|~!+;$58%20FTV`n1V= zHM*0KopaV?Yckh(*zWATioE+N)VUx3YHjA>q;r^fVL0k8?>Z;(^)|>G9;o_FMvy~TrllF_# zZ=w2qpZG56!hOUVz7{9vURbsts66{^+qIoQj<@ z-)4`PE!c5Y?v`@Svtg9=$}sDdLDnmYJ*_xjIcYz-g8t0Ih<{ug^yB+C@f)xUcZ{5F zu%pAfEVFuFBhkSc#f~C#fmMUdpOSU;lKfeU|DG3#UD(ysZG5LLnKS-h)AjWI|K9kn z(SwfJ8?ix4*;Y}u3S}$!vYlSB)90J0vt~OH9vs7dgLJ_c@gTV?6~3kWaN3Fb$G2pFUC?3|FjnrMZ@K$vgA4132ph%6(C@-K=J7fs zKIc%LM!ru@q4f>gUf_b z8ksP$9u@Y$r{cMOye5gyIsM$r{huFUw0p?j6Xfs{Vnb^FYhfn zdNmr#%RG4*W$!IW%h@Qcau4hsO_ zyXwn!d*asjXMpSE&g47g7v?*p5#~EcE1U1h#w9;xY%fmS`hEg%k=$Aj!tX}-ox*D#39nOe-8l#6q|{3*yjDGgX+>+6*_wdg)&c*3 zxg=eEzk6lb?vh*W3%ONfE!E8YSvr9!fANN~VEGjL0|A|A!*cew*c)V?U4PhI`MNvH z_B7z47sTb&_vgCG)+cvPIw}7kosdpmIwR}Kb_+0*q@(Zk8_ITP;@0=sz=Yi5tJ*O9 zNa2-U!n;&_$u8P(JvR{m%g%V|%G@U%0vuT*@6r)@c%{~#S#@3gHZ z+MaJ;LnoK*oxoj5y85m^rEHUlTi@Nl4RU+^UZqK1QhsinX?C<~@q>JX>Ez|3=d`lD z1n7Hl%nyCfoz6Wsxt*;V=1a6KFC$a_zMg1DA)ROo_OI2xs6XGnCeJF{?}6)+bo71k zqslhEL^>(|Af1p-UOGLGE87cz-j~GX(f5wW)2Hg02X(ZSrp-#7L4RJ&HXwZn++mUSXeJl>z>)%Hi7?m#ln z)7<1wba%DK=L|}xL^_gP_S~{P4=BA19@KMg&%80jMSI$859go9FwfjJ${LD!+p57P z)&{XYqG*giuWVlfE)os#Wfc9r-eoC#l%#j``JB@nEZY%Ol7jqi)ue@N+eJ&>T&97vabFJ?m7MFs`33zOcbwNW7L|tCmOC!G$-I5eSnWH zYO;%{??+R&r&6z_!FWDio9jMhId8wkHUb>|tUF21hgaUaU(0>aE6O(f@^rc({-M$p zotf7m|6dPe=!E#_oF+Sm{GUqxoqZ0U|7@LR+@fE4Q`uey^j%Co-FkzwoD|NS|K;Fz zD@@X+6eoqF9fk4Sc?fkQeXJ7o>-d(WuXHEhT((_TGS>NY>04=>IsY>WbA!S-eKY2h zYU`9$ach3+ubKF9`nrRswSLp0cM^Ku%A6ZG?UJP2&`jgZ`A-mLr^4t?6lqIOSMh3i z@goyA+9n-LZ2AUmNo5`x=N#s3Wg8W}gpVOEIthF2@N7}v)V*f#J@9e^yo}G=l66_g z)2ra=-!RUNCS?*&QaE^u|CxX(OqQpipXX^1KaZy>*T6e?_u_Av>xrhuG0N`j1dx8{ z=gJ4Kw!y=YH}2l{QsvvEcdevP7MHtbo8kX&c-aAakbExMht@q2%A6ZV_n;ELP1$lU zmCkWZrgMWueEMIeqk0|tVA(zfZ2j|v_8Q`1+h^zmk*?&gePHOElGb>GOuQPVx#Q%V2_A#OZn)zv22$BbE2j8UQKlAn74<1 zQ4jOidCsI~SFmq_EllRvEvz4Pcdd37AL!POeY9+!2F`tDLYH~D)6Il#=;zO8wn29d zY3pvhr>p!OohaKEfFq(KnLyq{T(q9Ma7kGPZeraG-0styrz};*D&_;BpG$ML(cSsc zT@UHOw=kcJZ4n@QZ!X*YK>Z<~cj=X~Cx(0r{oGwS`tTa~T!hcE<#KBa6=ftTk}-m@t08Sv&5__+t(6ySrmM-UC5zfO5iM$fnzJ!600 zY2uS*+xV%nU8DRZ@-@UoyeD-QX`M{k`$&5aX&1<66Z!G`XaoOPw!Z;pMI)Gth4={m zs}4qPFWWbPj!Oya=Hckkz`y12E%b{X)nbpD!X^hi-2*?R*Y5YDqNldlQ|Ytk3^&@T zgPFH4|F?eq%dLO3$&ROd8b6iSiO({Z0d7-XlQAU3E!D65++@*z(xz+p4U93Qxkz65 zxr3kUNGF6d>GnWxdGcS&R`(_P{AJ`#y4CQbrXyZmW}$zyk9)E6ksUW%XPe>I2Fln* zUsD$7f6kk+dn|8m*B0X!hO z?*0MyWeG34jG>>Kb3=P4dD}?7rQ;&|Lw?;p_7(apaN1u$M|Chn3lVXE!|ATPci zr|+UqeT#i3(Q$VQ(sV-qXm1mD++v>V-H@(?PQ13u)Z=%kM_@{Hv-KGGJybnTe79`B z0~%iqog(*z#Bb6K@zJg}+eN;1P`)kjTej}j!Vlf;^gJK?K6wEy5}llKhW<;H)3y71 z%60{CbO}0rf&Wr;{CThFjmRQ57vdi?lH>d?#a-18Oy!id*~mX+T`wDdmD~= zE4lLybF$oi%^DtfvFHS{JjDNRWckQ%*n0-5qI+yBXGq)+V$AMfOfE7;yLVL>qr-5~ z9`2JeM_9x8^U8bT{Po;lwo8G=*FZf3>Wo6 zhkDr#&pY9Fo-FUK=e;=KYSG=q8^46_gmz8;4%%UuXkUZvgWg8c3hQy8pn6ZQC$-o~v-Q~K zSWaDXXM9*|7-(LbPjjH7V($WmGBiW{!=dT&w+b74KvlG)yGGu4bHqw1Jmm=8Ix8TY z-lq-m50<9Vnpj=2{{;G8mrg5<&x8xo3d22ES|R_(yDIh_;7X+#@IS;qTw1oSV*P-< zKArzGo`Qzqg8Ya02TRk{%SP%2cyT7JQVLJKgzg}%5dUClMSB{N*{nfXBbHesw)Vz0 zF=jSXPg{6~c4i-1|tbnE~4UT}!_ z{jm1GlY1V>xvj{#_&xhrU$8hR`zA?!J)AJBqz8J=rNGKJS`7`dapmt+p_ z_iB1BtJr7%qGA)SY;-Q#Vj9OjUKqyp({vb@SJGSK`YCxGTv)G9|7FEq_nL~`7vwd> zrQ#CL-(^ubXNdQk!`!KPGv(C#TPg#OX?X4^hrw$127Z$R&TO>+ND zbnNvN`_LOIHX=F+ze3!D<)L(hx(Du7TuABrzkxgDcDgg10nTE#U{JEJl=fFzRn9pB z8qwxP*6P>{80GFU{i`*(GdS}m?w7rpIo_3VI{N+v&?C3jwWLubp0n*qyp(^EPDmr# z)@a-2IcFqaYspuf4)qI~`ujzZ4HaU&G;cf3_E?CQ3 zvxT_mO6-Nq@*Wp+KfNpQYUYP)=Alzn+h%OK!(94(M=SP^K>c6&GA_3e7vJw-%uD)e z^v319HLY~iMvvTv{)nHl;y9y4Bg+e2=RaZjB0@#`!0*bi20(&wE#g*eAMc%J3mByryH;fmew z;}IsrEmg0ouZbHfcHxZ`yXdXt8@Il1{s{d=ZdZS;i*b}Ij2F|FUM6oTFh>Z>)HhMF z8DKJ@ukShFdbwp|VxInuerO7>yG-mG>OJf*ex$Y36!$Af+u2K7ncVyH?{IUU^5%-I zxrOm)TxDsd|FxLS(hsK!^uHqQwUh}%`BQN)`$znKU_dn1P*2cU35_;rG!j4MFaAI` zq!GymP?h%(2C;$7y+v;$`{>6iw(sMtpG4R3mAfGUF4FyuD(kYTM4x_e;SQ_nrSB6J z`w3A0HkHlX_l>t9Ydk&pF8>-%^8?zMbWX##sk%}d)f(5ezeae)Jlo4E3nMv&3`>QPeL&JJk*@4dg=s{4IEO*5>dvRL6^`um zvTX97EB4R-Liv@p<0rOc0$g+}@3A8km$I)FmDKO57z(Yh*$Y+ z#cr*|ZM6^=^`MV6-@y3GeKqQG^_1+^abJ!4UU;XEG30afe_;P#t9__0Y5(Lkwkw70 ztCW8xd8V3m<>q(`U}A`=(g&&|qk`bW>dSM2Y<%pSm1@WZvc zjVWAoc!eDv-e8yauBVT#b$zPzm5Q}|75Sk!(9m}~Fd(<;mA149H_UoGh!=gb%|1!H zyO}n3BkfK0S6thp-AT7PdQXGBXVF+neyhmEZ!-S*v?ynYi+`b?Ix52%zp# zc*%J1vI!C5mzrah?q!Ywoc6ck;R4MRKA;)mm!diFQ}%g)DMynrfV`w}3+bfs=HlH< z+zG{1|Dr6SmBMA{EtQX>KVvWV=M{U#)hZifK646Z7m@jbF<)@@`>+#`4reaTpZ$`$ zRXpW^`H1M$aG|Y4Xmvu%+4jrOiq>+5#+;-zxeq$>tBTzN-0AWoc^u@2aA|+e>%;Kf z*n1_svsFl%jhXO~-U}q3vNO7qInq}0(&f|rFZ$wtGf()tq&&!xkpNfg&+s*zNBaI; zJ2UrtDVcMx*iHgAoER%s(?`R1HQf2#D7sDbxpl~qnjWRzO|Qy^RvY)Gbze^RtrNRW z+uFF--(>5N1+ud*oBpAl_gcPuK9YC!q+_IX+?lH8^8NIq#0GvlYi;NDar}R>FC-he zDgSI)0~_1oQB6Nf_X|jOZ8rWulhgUsyD+J{a6a9o{?TE+aCU{AUv03*!<%#A%~|l~ z40uyv+-RF3pUC(qxofvb4=Q zUGy&XBlZ$Qx9&R-M!JC^K9Zi+RIL9dy+m(o(A{eY`L$Gjk8Qx_PprFJ+ZRmosyvjs z+U*kHZsp0zCuBPF)({^(vCW9Lk9s`sKy#-q~(jfm?IphJJZ*r-@eu=5peOetB`nj-ky1yWhK*SD{~C z+@53UE5N~=xS?NO+!^A{0@vll4gK=sj_+!>tALrDxS?NO+`irIb~do?=;HE*etB`{ zh&K;hloL1f%Zqz-4`l_e%848L<;5L1j{XZw=fn;D^5WWF`Y+J>zQyGY{qo{Y5bsvt z+?=?fUtZjy9{MkEMNZt%FE4KCc=|6enG-kk%ZocnyeZ&;oVcN1Ufhv=^j~2AwTpQb z`sKy#K7sxVjON4*{qo{Y6R!$PA%3vzh7M5&@V6U z4Dn`x;heakUtZktljsM)(VV!UUtZk4lj*;}-8peXzr46}#G3~?uUlN+&@V6U(f#ya zU@#|c=$99F;1uEk<2i9dzr48i2>LJZrJT5-UtZh^;@t`~{=?$(hJJZ*hfbw#fwObs zhJJZ*OQ+F)fw7#ppcE^p|U7kA)n`Y$k$ z6F2nBi))Xf{{mO$#0~xO;!Y6nR^ZN@xS?NO+@VL)e}VcBE-r8A=i@H=FE3|p7wA%C z*NRKIv+@2^_rs;TR5*{%rt9%b{b$2x-9Fw@c<-P2cWXG-ezMaz@kHtqxaLFDNvGZ{ zT)2a&}b&f5Bzxd-s={?8~$VcMd??sO^h+|E2zQhxh-@co#O=g%3i{?SJAw z{yb#o^BHSy?9SNxO!nE9%5Q>qx;)xXVoykWN@Fi*x32+5q37Pa!mk-WosEZn@%eVz zZfd;Wo7{!&P3~Cd)sfHBgZVvJn1>V3$6xAyDgNfsUMg%tJIlkN(RRBTnEePmEOQ1F z@G$htyBlSbe2AsPC-Qa6WbA_4K>E_ACk%pla z1Irbzw&#lO;mEQf7uNGtI+gokY*fbfWPjUS%S_XB!(>1=43kaM({7`@nV5_6*TNmZ zeG8PMB6`~UTIRyk5?m?=sWLB>N47t_uwZ+*bu7S1nIr66v|$GrGK-8vf+*K$@t`h7~jjeAl3A*aB3 z-UsorJl{mz?k6p?Gk_azqRr{tg*$g4u2!}+==sprbmqH;cDRN%w`Qhudevl*mQPcA z!;GuwdX6Od7$_Ift-QIe=c+4OZ0;u;?2r784xx()=zBB&`qlESHh37f{tpEIJ`MjL zrWJmB|LlJl=1={OX${oM)KPCb!&q^C-H6K7b8lR(zJGE>u_6AaZ#P%I-}0~DANY^> zZ%@Lk=3nKH{?xquU&22mPs-N()mq&x*kudT>_ArmIo82A)j?f%>?`@Y?pd-dd2zg~ z@p%f-f5>@h5nJw z%noW{FvkR8l9^l}|z3i$_ z;cD~GFwLY7XWP+aQfK4mbMF;Uo%Qpt-=&E=)vg~7pW39WxPdWeovqOL@G$Z*m*ivm z^kp^xC?6N|uivK=cZ$vu`RL-^=Z(XgY~x@zdO{m)W7KJ#bE{@LrYn;r@}jY00-7N$ z@pR6X+3$eKPry^AGf}r2#m|E z@~D4Mo)li=9&#*&qBr+iB3I#KWPsI{OeXE&4i# z?R`%Q*VR>r4&HlRo0_2uhCCY+&$@U=iaQ?RSSh^!dETx2RGN48KT&S`82?@5csIqr zezS>N;q`0dUtiscJIv2SrVi!yKcA25GWi&SeqKB6PSQOs$;UwAR(Snh!oR-85_g!7 znM^+9_CKGG-T#>8$J}r8^DzNlZRtz=>o=3Q6<)vJ^RKT`l8;S%4}iNAxQ;a9c^~>h z0WN79BMLire`HBo_+yFE#FlsDZ2T%>Vqkhy;^ zI%E5g|BfbY2V4g@H_r%Y`hF40v5?QDpQYuJ-2Nw?2(BV8eICjG*jMuT+yh>GK97I> zE>7GEuisnv*Voa+9pjaVxxjZApH* z6L)mRDm&wTvs3Pk?1Z~`C+f~+w(~aL$h{u>q*v2NkHFtU@ctn4E8dw6^FNZ|qul=I z^L=+F-+ia$=X>VVINx*p>$i_^;;X{z_jvyGbz$PptK+b~bNwG;p0^45Ge6;t^n1;& z|7S1PHl%P-gXG#qH;&Bwn6QMehG7F-D(sI4i=X{VKsUgp!m`HSfZy&gY=BFJ{UKrT zyC@7B;8J1lAuN7Zgkb|*D(nvki{C^THo&FA&JY&AyTh;nE*19sgvGCMGT=jiONIR& zVe#t?!v?ri*zXb+zo9T}fJ=qFo3Qv@8HNpTsj%N6EPl6zVFO$$?6(Pv-)tB*z@@@| zi?H~WJ{|BOz@@_8MOge!3&RGuRM>A47QcgG*Z`Lb`whb4cU2fRz@@@|ov`>#hG7F- zDr}Xo_}v$V4REQjUn4Ajop%I$2ym&eUnMMlXNO?}oDW-@~$Y7pb2w#CiS+ z|N8xB;(kzh_@|}6unvB>@$$yzY}^M6^QZX@)auubt?bkFF6_tH&*OS7{bS6Z9{&42 z74z(+{OflK|ElMoChob!zsAFC-qY>}O}{5ef9~ThDJy+~br;`iXYWpM*YdCQzmT~9 zJ@H?ZzHeXY^cUW2$>5#6TiUzUc{`!+_6!fi>pr;`(a+=iL*T;s!4PH5o8JiUe=4V- z{&VU1&CCs!JHHwDf7pBXxG1YUe*6p!7X@Vy6cLp{Of57^9GtYx?s;jGk4kX#X11X}7(&=UX=jy7nmtDlGPPOaBUUvB|4sKTm z!l@rT0VMYh^CVC1MPbDI@OlN&j;2~=i;7z zNU};l1nLk@ejAY7P0W)#xqkpj=ON~MjUPjQV_Wx$9NV4*%&{QK<7ObazcSyg%YR<= z_xtU;bjyy$w~X;SztIav_=bCjdE(teY_S%dS$QsbY#`3J55O4*Jj>o3D)K<3d9>g(5|hD*|&AQt$dukqR3AqRfZ< znj1sKmz1v#ziE56HUy{l&o|?LK-{AP#G@@1gkEP|OcL)I@E>;i=~KSX6+0E-T92~- zoIu};N)ZXz!%WCb775r#rM=6<)1$!u5q!G{e4;6@Z#GO6ns{0Z45s~pfM=e{IS71* zRyfB9{Y9Z-G3W^{O8`&5jf0%Fmz*XR->rciN^M!f%v#K&{q0e!eMQAs9oVEe z++R!~q0~i$1?bVp{oXHt=7e}%TwW}Fnji4njp}uKP56|QL!+PzX*yMdO+TI>+ zqx79Wgou|wxA~>_59^&iV6KAA0dp02)sdY)D|$h>5K(JWJ|huk0;4VBDa4kWwQFt663ivD$}`;_OtucR&? z&;la;t2t}~ko4crysAeNW4~}A zo<}NkYfs-deS+^=gYNO5|HPxM_Tqhh&dBa(9RBf|!67s!n*DXiy-4UUp}B?gkn8TA zwG-{7=MJz%T6)%4Hk60vtkazt_&&uH%&(Jio?xPot1Gz%-T9WxXVBX+T_oqLrdKgBNS%17PKM&0TbfDAd`P_Ny32ncu>76> z?!Wvt*g{sc18tC_@?Qfa=LS;w4*@A;8e}Ms0#36KNb%J`a;n|2^nTW=d18FYT#=et zAmTJ85$zl3jr4@OwYUyZeTdIw-?u%6a}zsdzPA9$?R{AC{_}(Z^8Rxu@v0*mh{hh& z3zHfMbm19LusKwRa4M&spAlWcJjs*00Z95bGq09QMgK8p;c~d|lWDy`Gl=v%IBXS= z^xek1f4SPJTmzN&dnxNSh3{uRq&U^7Wi}n^J0DI&m`N1`8jx@A`A~2e*-4`Pq30f1 zmNWh&%XbHmWaGEXK9LJl(sJ1b0m-&h=4S$xKEu3PhbsElrJL)r9eI-L1nN2eJPs=Y zlKuwfe`)^uxw$8newSL3hjn7E7>uzhPf^MK>0?`bEqH!+sVw6qc4^l3Rt8Nzh%uVR?>jV0H2l_n86w}QZJGBjuz82VCI$Q95Yig4DZUmBhq*?MLPfo2< z6=Rio$QUpWnPCI9m(MEG=&Eu3u9AA*2a@{&sI(!=yal8*s$La?>36YyGwZSeeM@Ef z{7R|+JRs@6lKB~s`L+5xRsGOO&Ia^YWcuk9GOPed`dgWw!ubb0`|!Vi_6(-| zNDQ*~?1g%H{?`4MW@xNUfaEg+Y4)}OX}!=6q)TNdFbwDch68;-_h<6C_q#v}`yLnp z)cPbJ1Ee%*K+>@iNII_vQXU;Z%JVHCxle$~^+y>uI15PUZ~gsp44Gr~^p1x-yc=W| z&Qh90lqN^0>sj4@<#pBju}1iCPJr}1{*l!GEW(0a-+I?=Y>;O$Cf4iwK?rcXO<-O#Bm(E~^d1M|I!k1*eML=?8K=QRQe+$RU zev5ZJNH}vcKbipk9+IjebA7Cy6`ST9|IY@m;=rX$NS0Y-Ik$v|4>*! zoQCEHJP*N{Q1w3B;J;?|p#J^usfbq{)wxpd$it)t0^K-U66~JJf^h0DHa>gu5c4EY z?j<1k4m0m>hxiMfwOz;i$#(C)Z&@Y}>;k7__KU99pGNAjnEN0Xl)rHGLrNvqU=y^rf&(}b5 z-vg-*>Jdk>HkP$Buj*D&`3(@C!Ev$v_Lmh^q7!HUk)8z{RtzNlmvMN%WukidnP=wR z`77(5VYr45#kDpb*KoX_%_jFrI@uo2J<=W@(7a9Bb2E_Koj|hZN#>7oJnIj3AE#cg zc+C|&Z!hOy+U6=)RO~>M4~veL(X< zE+dfKoj|3G%pc|W?z-nQk^XxSiG%U|v8(W_Lccw46Y@%&fp;7}y9I9n%xr1L|B2SM z_&@e67yie^TJS&Ir}c(-Xdjj2RY&;~P5dx$th(SKoa+9ozzB}b}vv2RB*#YN9 zAxCi}PwrJ9>5IZ|Wyqy~d|N`r51blGS zQDFJ?^`Sx62OXEl@*V<`^DL*c4Z?R&A8eFD#~gUzKL{05QQ$Anjn}5!NdY4ca*JDy=)j!u_AG)8Z@iFIqqP^Am7J ztttuc&rHNKeY|hA`-we2V?i3#Q9TmbPV`-?EJ2t_6$F~_tdaVof0&NL)VAsm+wokH z;;#9eS_aoTAn|v9A)gocfW(tNa-)D`R|fOv0;zRNnNNYg{W#TUh{*qh+6L<__y?cB z9tiPD9Y+O*(f0=r;k%}IP8o(d$3K1d?}hQ}ZqlWDFGNfP-Nw4fKOMA|cp4{k_5=MS zAUq81x)<+FhMbyKGXUQ@of783Z=}1gUEO0P$a6dcwWrd4bJ2bUXg^b*_9Ge9QJqjd zMSj}zY|lWE$NGP^=Rq9RvJdF{kF58x@5p-BA&m6VHxwykBargwV15V3KMf?O%y)P7 zJe#$U{d6X-cNq445EH;`m2?~;9^6G%MiBlmA0+4TkUgOE4%odh7c-u=QIq^~Gprwzn&{h_$F#*6re zdio5lYbaiIlox5%{#W*W>MwL1>A0+Ke-SHW7}-ndRcv1-%kU_W+_gwcaioLXUxAd* zyUY)U9^$6~$t9-J*@YN7AJIL_(z_tkbHd$YJqzEH%8U+)MS$`K2s7+Nd3-3(89HfC z=XubXA@jEZ$*Jl4!`M`O`>$jq&XN7NZLI> z_t)~iEb<#EV+NA%9w5nd0;vr>%zw)9X{>V=km^fbJNulo4fvganeEZz8?Bx%WV#bT za$hmep0_J#Au=Xo;zSa zy#V)>bFikLjr+=6q25>OzL9;}+m3SGDD8f7gDlsd5vJ6isz-UwZNhWz96|3BQSOU4 zy#;C8*Gu`gfaLys1E(L0Z%NkT{se1w-NK&tyHGu}U#SsygSLEya?>}HYC}cLp%|>2 z2V*@D9T2bVv)ZHG11$s0l-9daBL-fs5l1QQ2z(QBONbb{HWc4a!+G3Vd_x-VMAtfF z1LQRHEt!V~TLzhW1wqjfV^gy zRt)<*#y!Xqt>`i|YD6XI3DQ-d69v$f+glfXXU2&=e%R$NublOKhpZCH8qQp&5to9* zH_&xXv^WRvp&ko+hQpqC*rRhMKu5grm(vUj6T{va;?sHYjsxgX=L8jhcD!(Z(9cJn za3-L4uIvZt^JG{ykbH}oUj|hA+0Tp@eZEFifsRl)ke2*s&C!Ux#PfLR6Nd5PpRVtC z!TWYFeg=Ks{Vox+JZlly$8{&=7%|Pl{;5&g`V)|x+a!7a^@j=aefS9jaSxG*HE2S> zx=k5FkXIdPCF(rccP(-o!c3|lupN80z3(3^97gp?qWz%{dz2LSpDD5&r5Tdf;v186 zojMOl@uZJjBanQnnZF%K*Rk!)D{GOZJ?mB*)d8(rdx!P2-%IkUqkPDQ_22faL#BfF z{)j#UG=WrS`bmb7trVuB?_25DeJ3S9MOxCE3VCu1fW$9nzPoSt`b|s+>>`~W=(DlT z%|LSZGT)tc1l~P_v+B|EeqFgfc{sh9(wvkwjR%sO(JOto9%Uae__I9mt+dDT7pcPq z)Nhx1n}Ot30I6(Sn16)h`+Js`0$KBIn9o5|zn9~S{MI|LCMKT8SD!G9FaLCX#@7h? z)>jPP$ut=6WEzCJ!5qDDm|JZ$+mTkyBk{B-eBYrIdsd6YsLT?f)0o8oZ-ghz zEw71+EYwmPP(LPI{DVHA-wNbUW0!3AIwgYb?!Odnf{YE29gbDGEZ&Y*(k)zAoGvZ)`)fL6#gK>W+-7okpI&rv_J85FNSMj zK-e8xe4j{%`~H}~v?Pn`yni0bwG!Vsi+U)+t?92%^b=0@>d%FVpiZ(yOZ%A70b!bG zT<@y}iRg`yHVyUFjaq}oI#gJnHRF8pMJ?)-a^5R@_7-jI?*Ni}5J-C5%)i3%q=%diNO_&hys|gyq&|dqdidSb>%Z%J zJ)yAfb~yvz>HR$57eUH?h#~|1{$;`rv(i>%f!batdzI6$O70b#Afr0(6+LBl!~aW8 zw?fv-c|dY2SIWAGx?J*mf#my^`CFiq+MMzs_bHI-W&~^@Hxo$qI+<6?9!u-%k{H}0 z4es-sNHgk)bXt~$iVmmL{}PbgM1)bCyyqS&;y3oa=Qcqu<>F{@@v$+YAr{QR_>FQq z)dR>oF;tA36e>zcU-4j3{N-R#cq~SoeMl$H*c*%Q9Sy;AQoLIo-+A$uO&m`8;zbD7 z)1(h-d+d&kL|oeo`RD7yUR5>xS=A5B`m)*IrcGLf#Iv-{`bg3VVh|M5-F&i$T)P#if8h{g!v z6R_O`RGkgabryr6KCXtD`B@L~zHr(9;}Aw}43P9>FrUk^q=%dfNO}E(dH?yT6ZRyI z4p)8)IcQ_gIXnti9qA^j(gxb;#`73;EkLrWy{-^x-vx*#cLzVWA$f8y0ZE^i`CxVE z^{+#GyC`rjLR+UEH_aNnEcI}xVZXkvdYkX$Q}^sQxnGslx2 za$X?$V*GwL=Xozgd;mI$yi;g~<7YyF;gNlw2?fUETc^-rdOt)QAw6NZhTxe{Kv2qb;+dnDgI-}kylF~J5U9e(4}4kG_9Ah~CmSNiG{SsojeCxyOi zAC0vPp5H8#dup@>pg7g3<%y37EKmE&;J=$NuaJ2k1(HjF9N9wh@im~(^w~}-4OnEM@ zE%^ewI(RR!T+53eWZjAvqz*l>eZSP}1(JLDIeBlq^Lfd8fK+Z(zlt)S0^~CX`Q!%V zW8i$utkVYcJ}dQP{8Q>p-Y4@J$TH)BN=DVPn6Ot(b^s3kjMkzvqwi)Q;~Np`RRP zIS~b-7Fa(}`9iPq`$`XZ{TPj?1yY*;6sJ#-eZs_O0n*jM#n{2&Zsxtr3+Phy;1~MX z7{OjDKo1ndW!z*Sxp_eHJp!bW{08%H11Yat60!aQJ_{s%>j;gYbKx%niMNcD>Fq!| zN4|pjEkJS)0ZDGgXeqY7Pk<4?B)#O%29jh5%1CYs%1C9K z2c)t+4y3x>&;08g{|S(s+OCOd^j%7P4+#B(?pbLzpz-2H8(j!FrQW{6b?^)65B*X} zE`|Xqu228+?|c6K;Qi2$7>#p0xsl#*j8o;l?0@KciIef|h=AY3VZ5V{DbL2Bhkh4F zZ9_zDK_&k(I&gkB-O{r+(Q&gZlbg}YC?G>^pa&}T0l$hJH%R}24G4rQZI}E?Amz8< z50Y=*B-b65nv~v9|ux7z5|lZ z?mY@xG4v3GmxBpLa+Q( zy|mX>i#}e}_ne`N^_ejp=|;%(mjKCqI#Tk%)|s71*So$G^${ZBt~hV32hTQ?Rj0IB zk%d006KZFwv$mMPKImTEvvzmEuR4J@ViPW|li!c*- z)wwV0yH{dEm|Au-{J-Q}ych8lklcw@Sr;qXBp<(A?v=~~l6xOW^+@@Uy8=jc^B3k{ z0+PL2-g{BY9(#JUIK3)XoQD096zrGyzYl}vS`+Gt^t!M|VzSFT>_Bn@5Jqu5y710~ z%+v5qWtwpm-3sj(?n7Uem9oc>o;TS zcpVcd##BX#QFyO||GVWoSidbFdpmhj-vJ=G#Rv+awdfN>rv}bS#Q|` z*INeYQR9CJ6X{#eRhNkInRQrSE%jSp$@zwKk)vw|tt*r<7T2@3awE)qH1IqNf<)(= z@_xm`=mSzobdV(-1|XG(Wa;{2_&46412hBmugkcLf#g;IDNVv*dHJI^PFU ze;xBL$)^D+emaos&SU=0H>AGDft2?iAjw?=+lZ%fkXs5Qo0bE~rVoHr_OF;1u#w^u zfaLnC2VwC25G0Ye59pcfs2ixdLwcJCnaC^I7lX|?20_C+lD{a~jB$u~e z+S>A*0P?e+ha@{KHw*zR}B;zgz zlDip5I_|tlmj5vz@lOJ&FZ{%O)XlO_!~-e5{uY`3N+9KREs(+m=iLD$_Yjb5cn(Ny zoCO=GjI-G$GmzqIfaH4Dx61!g+q=2#y&I(-!R6BfJy%N`r(PrD?p`BpeRHkkPXNic zfzv(&q%vue>AL{qaX)?<_Or+0y8xpDz6+q(_7J|06Z|(7-Di5#GW3V?H=uIARXyY6 zu+TND3e*==+$^6sIA7$PLB#D7uDL)!W<)6y@d?2-bHS?`N z>YKMPza2Ne@oie$>?G9G3vNH21YZ`b5y1o z@Na2T6p-3-1oM-bpA965rOYn_QXOn!emjurU@!AWfK&%xF&_>as17EuEz^M1zss0! z0Fvun_So@JxHcHEem>i8{Vdn}G?$pU?6zu|z8z?~SeD^IAh|;pX?t{qcX1CtW0~qBm0NABuQ#)7f5&dl9t`-LraO*Co(Fd0CJTITw(89h|Ql=tT(0j0%x5^MT~M1W0|X zmBUv8Np5*^AA5=>A$Kd}HO|ELcCL}OdKi6-y0tRgz-VT)F}5>yGI|(&j5@ym zH87fiZa$CUT_Nk`?3FT)r9f(rN11>2a(UmBbcKwc1*AT^nfV4%GL5Z{xp!<=ndc?#810g^5+4g!FMvhABfee7=Mk7Hv(yo=nm!|0S*8^ z^jaA|2^a}}J@aaN#?o3CXX#R~pE#;y2>MH`h&~W$qyA#!`fqPUpRJdA?gNrDG)P|6 zbE;4|n}_csjI0Xs9sst-T`TkUGKxCM>lsaq7Dg9i2cw(O3$#?rv=0KwJ;nTgfEw^$ zGap$a+j|s{+Gh@s+PjSTW}p`Q)y!`NhJo*7ejhL#{83;8Fa@?#dz{T>$OaAozm<8l zK4a5kM0zdVALzySl4R_KkH&RixZia^juj8rO~(xEZDW!n8y8H2Lb$kW$#?5i-X9UT zcF={$xxsKY#=u!p{(B&~2!xTo?)1OXCKKy)jf2iHQb$gz)cM0$)_KiPam|rP;oLt! zR6jaUEWR&FoU;YbZ`WcS)izkf)y7~Cd<^#g^&-;N|6Ck9mfoqAhIfgafoDReLw+nj zFW~hu+ah=^ujjSBiFmhMyR|<}GcD@t8M)Te1Ie8Rr1F0Uq>zbwd9C*g*G{#*`oma! z_i!AIJ-qJ%*N*P*M$*`Gv%S8*NgH$zNt+Bn3NZtz|G9wF?>d3h-@HKTXJRMb)Av_t z=iNYxoA;nxM_&XanFb(*uK|*dKLJVSi$Kbwx>M%)XSQJ{Q0WKCdHBF1JU|+wJ|KnY8m0XvAeF}kr1EtDsk|N_jXi<)qS4sX13T(unGOOeZrUXw z$~Zh1NHR-+6y6FX9k&2U=MzB6;~gN?S!BJm;Y=Xew47~R4LntK7OR}k9IU>pS=q19 zhsts5`igQzS%Ktp1F25DK(bHZcO7J}o`2tA0xJCkVM?6zeayaA-TOkmqb0;#TYfTa6;=2Iy3u}ooF zi1W*!iKnHA6q5KdLkSm$&gvB>0{Lsh#kG*V0&?13dNd;=u%~pk80wC*4Y9k1C=rW$!-gf+N>Q&ZRQ42oB4p$MtVF$qW)tBQvb05={nX9^n5Dk_|VU! z%ymHOQ?~<2=6)ch*~Rf5AnE)JNO=tP$vm@xWJ3jzY+3^(8=w0x{=ppt{?Zte_lrI6 zU+7fF&5Ke;2O)^`jN`C0AmwA_aJBwpmEV`^0@l`E)@Rx$gzKMD&;3AhVF**~>vg>d z_8uplR*q-ygEB3BM9>63rElUe2axP~fcd|1e7AkLpVD5%YiQQju?1~|=S!sLHGa;t z4q?Q1r&G>U#9|#7ST-B$uwd$4i#f34iXcwZkga5h3oQ2T$ht@ zT^@n=(GC}3ZfRSGs`nhJ(*^X-k$(KtRdEGC%5O7}%KZ6U$#>_Y>@5$1&3LyH&aSIx zK|QRSZurc+erX4i^JWF==+^ZsY!+cW-|`x%1E`xK_2JdSifd(lACPPwkr|j@tUeO; z(f6Ha2G*?`gFd8}X`Td<`#dF3hidb$MfzPx?9dGj>)LBTa%b$2*S#x&6!H|1(tZl0w4u;JV|px*#`qE-wd=37 z|Ci)qhvUq{L-I^>_Z};kPt-~q^^7JUg;;=OqYFqjb^^&pFOY21@tVK{G*nA_tAXU) zK&5SKq^>zY(p3f|T{a-;x&}zP{s@c!J`AKe?*#DY%CF2ZbT9Uh{B4}FCRE#N0p*PL zV(bL!&Xw1jxj=Ha1IhO#kSx9cGC!B!FV%1F_+b6X$MzXMAqz0>7;QkZp&dwd)Cr_I z@&c)jbhu7X9hrbsM>e44xa{vY1Ic|1r1H!mwl5 zcM9^N^7lTQpbt;(_j;d5_r_I2us+ALBEPf6)D}uQ=p`E{f3j`mq`>{A_SHSVt2V$- z`9n;RHG`)*vH692R!W%>m&-nOHjvUx2a^6lSI9IDAm!^~{u&^~N3t%;m)tfW+4c5WfVb2LGDx(>7L-~6zBJFUzr>F;-m&pF~6Oi0w$dIp)Wv>8I+RZ?+ zW)JiJdw8xObsy|Gw!d5=zSFF{+uiSZyb_N*D1Rz1|tRp+m)D}TAfaS>u%<`gkhGf~K|JdOKdrEdb=KS`M=rB*TbbD9Z2%6lI3loz>nAeH|~An8bj6xlbK zbGl=K}ijrQa|c8Ni(b zr1GU8j%=OEvfa8z(iyA4_)bc+7+?=`<9!|$8Z){GIc99o*O@DI49Qbe;&-)@@76IE zdlt&Mm>GWiIiVIh-$=SltjlLY-@?8Q*)wvc)HNa}P?xH+H^$<91gTihC1D>J?;G;c zue?-SZLHrsS?aO@sjTgcoxqNC88-}T268)rB=ZW8+Ty>=4}>hWSqi5;4M_I(v8TJ< z1_jhx2ixK}#eE1Zo@9G)3m-uF)#u9n{fo%Fq?-fsB5zH+`^ za@3#NkWUBY)sKC?VN&z{e(W*z%7@y`duE_DR48Em9S_1V`IeovgoEEFpIsEM4<$Sc49VUp=xA^>>(ymDV zbM4y6b<@#LKKH#W^H=*BmDfaPfBhVl*NgPOr@!luONEhH!OOcph#eh4?5IWh-(!dC zV`)cwKXz<+rEh(7yxX@ve81BU{X0_U@TtM;L+|Nphvz)%*@13u5IMubzH+u8a_vFn zwgi#$1(9J@_Yb(~^- z`%kex-znBNeA=n4Khr7JS9^-}wVz^rkDg+E-czhEa{8%lU&ATZxA+w6bDd&+_nl&W zo>Q#vQP@-`P*Ro8yHkxPD8rOmFTdUg%|fM?di$9PjHV-jpKK zn`Q;i-^=ms{lvS*$n@TR;`OOA-jEwyznkM-{lr_w%JiOo;>Bq)UY{3Se+S3g`iVD< zlj+_4#CthjmmgfeYrITv=_g)4LB@CX6Yu7DF*~?^%jq(`xu1A(hK%p%C%%K@ef`9n z(qwwmoZ$I;IljH0c-KUk-rG;Sev*tg6a?4r=6F{>@s=}XdQU&`B3;Jo=LXl`!SS|! z;!Osb-rY~Um*aKwg6nrpmgz11#OtTX_|AUf-5f8@>8n2+6V`9tbNBMIy~v_Ky{7Yf z_-goF@5KqXpNx;Vjyd46&i;Mw{+kTds$J-Gz_|MY#{M|14+6bJV{<9Cc#aNajE z-obc3reRPZ?7{@VAWz1(>z<43!Qbr%!@gd_o zj7J$?W_+6QQN~WjKQaE1aUshai)6lEGM-?3opCSY!;E(^ZehHU@oL7)7@dr@j2AO5 zVqCyDhcSooY{rRR=3*g zoJes|*fNLBUf1NT#_t-n6-yoBZ;+7zSz!*Ry{WR%CjP5!Xl|;x1ip!(<*N)sH|v$B~Ot3Z8q_PQdVRp4r}YAo#Jk7wY3>C4=ZJH;A>}fj*5n+mPU~uQjP|w zXmFy*&c=D|jiKf2&7hK06N6vpA@nk(ZpVbFMURA~25VcT)#j{gZZbKXRVLGUvuDjR z&6=Hi-t63bD&1^hY^|+untF+yKRXxY%gtYWp@@N&$$g@s1#waR<$$<(f-0q?*52GY zTeX3HWzoM)iwu)ZWYZg(^17yF6%BP&<+ci{RHxNm-qc*zRNX9Mk$#GQ<%@rzrX044 zN~<^sUsYXAom1pe%{ZIU|35@nn+z2DHRUypTz6Na#ak6HUWAier7#wz~qZ!$^YnM8#752(n5gTU5@WTll^jeD9 zh`v?VytKT!r3vRw?$lOOXBL@cVFLzYS4me(p{+m5dVDJ5#ItQXfn0#ikElNFPIn4u}RHdQuPS<7vxa8$gQqx8Re z@kvNI%S_RfS6KsON<+%!fG)3u&4;P|>YPa2D30{_%dKLjs=cbbsRbkV15sYl=%^`I zvKAMTtv1`@N}F>|T?6I48X?e#?3T9F)SwsCHaFLcJYgi=bxjpctBFP?dfQZCEO#!q zS&?&fb9t4s*$yA1nkpK~(Sd54t4!U|auiUi6{NqxTG8Z}wh0~1(VZCY`qH4s8pkfJ zaG?IEk2KYo^3OMpt*ouEmpknhbxwyVUkug?&-EM7is)5eX^PA-0tLmN& zxswZXXtCAVZ}eFq^Bnoy|IK!*14_kPAq$-ii|XvomWqb+n_BB=#yCu2^D#bJY;!PK z(3C&B$!W(G)k86P6*gy!-I~{oQPJiU+d|H3vD%lLt@i3>dt*gYC1P5X@%|sOzND_Q zUJ1ZiXur~6Hpc~c9&(%G7s_Qs@QW1Mv%;9=KoPAabb;C`V2stChwF14E@s76#X!te zA@i*j%dCCEPYaocDWnb;fy??tUM=#i4Y(-uPIOCXDMsU*W_vNlXoIzA=_SZT>U22iVxm@6T{GtN;B$OWH7%Z7z5ui4LfD7(?{BW5Fgfd%7b};Ra;$qy`MoYA$~4xK zvHy&?T-I+>x9R^w`;?zU6I=N?wC3vQJNN&5cj;wfz_o5JX&j`*IyqmhQ7FTp{LLz< zDMfs!(RjA1@K^T&zKSR|4Gge}RQz6}u0#Dh?cKHi`&8!6GS2$Vmnmhyikk!N`CTRa z{pJh^H&!?tbu|Iq^S3Racv7MZIp!vGuwlwt;Ou^eX|L|tvlbvdzqSt z-=K?|8qL(L5Vl1}8%X8bP*hWjhzIFG6G1tk0#FfX5$Ix2 z73fmXD$os}+d$hvkAR*AJqLOX^e)H;`Uy0+IYlIa#(^?Hd7xs@QcyE!1!x^;3+P_Z zBcNwN2SM+EJ_m)`U>j&O$NG~m({ z5eFI#ngp5-nhQD~R1I=~R)N-oHi7O1-4A*c^cv_L(5Il2pb&csexr%s4}wkuO$JQ` z<$;PoWuQt>GpH4GC1^dU1GEG5IA|Z}5a?adXP_TIgB&ShBxn-I1S$bpK@QMWpg(}_ z0PO(%1M~vuFz6WQbI`XStuqDR!+>odJ?IS3S)e@7xu7yoHOK*41zHQb33LbOFQC7J z+@M3C4?$mmLR!$Cpi!VS5dEr?e%Y=SVIo{a;9U6t>}2A5lz8LNVC<0UurE0TJH_#0 zDApx-Kb%MuBLv}E|9(?zzJA!dpkVH74YOXOnTDSc&%6?){5)I^w2H5sn#>vmMHWs&5K>WRf=F@RBOq#>`l(S{|8BY$xTrDBe@ zbre>qQnktu?O(+zt17ZB@79kBSNyY_PQ^}@S1hB}sB5z}I4B3XInuIJO`315u4rjc zTd_fIzw|`XW{kp7;(Xe!$L6fH1lzH_`8mA<=2HM3bu3ltIls+WnUAgWlICvZ-66#- z_G;Orvzu6)x(WxYDWOho<8WmaSesN;s+WvJKbzG+yD^G4x4D@%fdiOj%-Jfr97vLD zvq|M@oK4m;qLg-)!G7&a5iRxw*in{IXwN(miiY<7Vuvod#m3|V*wNXc!iyd-+Qa>fleloT?105w^@lj8hCj+SuuvgY+p>=upXqw+#L8%D1=2S0f zQ2nHW{A!q*ErqFZQun#dRjSzw7S&bNRj8v&jKYbc+tl3LYPC-R zQ)I_;rY7!VMVR3lD!Rv+x!Fb|tZpgf%y=}@%`CLm)ZyW)y}J*o0c0hFRTdM{0zCNZ z2>?g488x*kgCNCD|1fEkpO7rEnxq)}tbUQ)RTf(5NmRE5#B>WPcME>qc_DV6u|L|_ zj9q0i1>8bf^s~QIdHAX@xVciRk+l(U1Co~-iqKz*8j8ZC+1L|T8wy@OE5NCW;A^p0 z&{j7kwem)-;_VqY2=BHT{%-Bv(;Wi281Quu75LQx71y>n^P5|hOfGGybJPa*86Fh@ zL#V{srWQ^0(g1^*Doe92{KY)WDlCTP9D8%)Y;4dYk~rnDAJ!lLcevYjgu3V+?;b`W zT*hxtwi(?Y)ELRH{$}34v^p^Kih(4M9+V0i4>|)h31k3G0pWFLSUKUnZy>x@7Aq(G z?jAG?ln0s(Dge>VW+7-kXaUF!IuBF~DgiA5;c);~Py%Zxyf+JU5$Ix&1yliA3aSL* zQ3BRec;7#We&>G)2%Gs>SK(;^i0*W4pi4p6q{j*i-(&#M>kqI+kChgFp9sQ38mzbQ zEejA{-j*Mru9)`M;U-3ZzMYG?Wba3g3F z=qAw3pj((W1Gj)~1^p4UmFYI%?VvkA+dv&mcLMJM-OY3l@LtfLK--z_1O6HG7p5J+ z`#}#d{S}DU1JY{*75W?SA*P+che3a5dIY$O=~3WgOpgP1Gd%(P2h)?lJs>yJQ$V7p zLC-Ml1wPC4PvAbL{lMp#o(I0b^dgYxCD6-EuK+zvuL6kL0+c+0EymVdKXA^4D=q;`#_=(m_7s&eFXZL={S(+1k)!# zqEDGV1NxZ$3ncoS=?fsymrP#)iN0p~21s<0>02PtcTC>{iGE=E5lHkCQx{MJBG-OA zPMJ4q8OE-o%ADk>r(B0VcP zH7zC~IU*)O6lJFsWTYC?lDc;7`ew(DFYdnk_~y+YY}oMjnl*2(SaHzdc%i<2UuET< zva;Q!r8|p?9xN!>ZZzJVoxL?9V{>}?hP1SGsi~`zlUF4rwI(Du;^OLKVrrtI$|EAm zA|i?-B8rrmClBxFIsSIny9c}8+}Cw*ch@UByY_AG+OxH5_ol9$tH0T?;Q$>&wYIi4A+{FTzOJw?S278GtX=55L{u1?Q#q@`D;jxSD1&Pzzh zh>1&$ib{ysvwg#^ZEGIfylVS~)@^H=x2~$+w4!pIqil73>59st*7CyU(!82tV|ig# zXtLqC}%Z)X~8D#}&McJu&8A&TvRW>_HYwC;2DhrBBvkQtcj0I^~ z#_?TUj^l5Z9^Rez%+~Y=S0ryOi(8Wy;Yd^7h4ki|UAuR8ZQa_nVntVZd6&`Hm7I(| zg)WVef&qwejVS^%4JNFu%G9|^nV@TwDIZOS_WIz1u6_HucI@cdu%XN0=qfJm%F047 zf*Slm4Rk;OQmu^`s&aSQvU0wBET?JiTsdz2acB9yqB{rptHL2*FKy`8Gscu4~ z9!W?@z*K_Hx}q}AQI=g(l>W-*!tE=v*VUvq7mq)_ZTt)CQXgzh-c*^?T9i#VT{O&5!o>!1Mkz15~)!lDMG|Wh!y39 zj?%pPVq;BFc6k90lESRCyo~Y2^wg~JN$IHxq<*k7Zt52C~)NE)n{i{W@MD5r{zp|e%0#A))nOrM`?Y1QDtR8Sy^6DQFdNlMpjl@ zdO9W+)Ms2wOw_hQRi-ACCdU;e#pESKWyM9M#Y7}WMI=O^ z*OO~q@k+DfnVR}tm6hAe%C;7lZYV5T1?!B)nyjqS^z_2<Dn$49f)|EO|7uK}ql{aUX)?^fxjW-r0W#z@CXGf)`Mi{NTQQJGO3L*Se;@bX!?**9HhOQ6fX`lv$HOQy2~KO;$LG2CLmR88n5_v-5(l z9(&_cBc;Wa-FWq&q6crXer%)?kr@&dgIvn>vkU)SYB-^iu+P6G_uuzaX7oOz_MMg= zull#gSb5pgLt-YpvPTMzHQlKB*YSTR?=uc9z4`J|?W@MnyhXb%Tj1gNOjAeEv3Up1 z+Gl(qeaIX3Gha1+mw(6Di!xs2_#9L6;~h_07wj|c9BVpvQrWAU9!(uKK9}FmR$d;aoX$8HfFjGa{N5gBacm8r*Yv+rH~)vDKJyz4yE@P{6M&2eC#ac%6=ci)hD zz_{SmKP>qv?+B+aF}2-xSL$Ow>@)6qc*Kxz79TJceyq)mJ^Kw#zsU5-peGHtp0VGU zeZ#{gZ{2vn82gt)C$3y{l+#~e3P0oBw`-Q{H}0`6*!=9?1I8yxADDmg-nV4DsoeD0 ze>T3m;G_MnoaK(i}f|07mcx+HRgM( zUo*Pq?zw!_)Nf?G;ZoCxb${Bq>8Y2DnS~*T4n6dm@x@P{dnWgR?`6E(Zklz~JNvfm ze%biV;_p0RU%h7h)4(~WwOpkv-*JQHG$r5u_~#eg@rrTTrAdRllU_IeA?C8PcRU&D zR{EE>#nd(6%tePR9^>M!4-DlOzixcumiCy-9*L0gwpP>ptm|f3-t!nApBEjHw&r!? z?(s2EfB#doj2FvIw`lMCv$g(J;}w72mNoRxuNzVLkG0NN8Q*@H>8yyz_dopatHx_u zA6xs`-q(%AVRvtwe&tXZuU~08-T3FPKWaN*Jn>!F?us{GH$GlJ;M-PKY87#m}5wh@upR#n*6$E-!%t~o{A@*5Z}LU{MXCh6u*1#I2m8- zGAY#^b;xMAV-{NOu<^cs7D7e3j5l0q+A-j+Pea=e8E^jMOwYjR!^RA9#aS}`@T)Sn+VdG0L!i~Jhy{@6i zbnSC{_qgVEJ$c2bwmr}4ea2_1&X{@diOQLl_smc3D_mp5SD$lL>JpeEn6LmxoTNYA znrKo9(SnW#=gpfvzhbG}ymrtI&d-Fe8Fp*VU9*Q7Uu*klCdo;+Dh~+i!H#tUlyybt zdWpI(<^2JzUC3E38K}gMG{!3!CWD2iMuzLuaF>keD3;-Z!%ydM-2xeYGDqt9X_b_3 zzfU?`9I4;U;h`=WVdn5oHJs&jSyH}(!?hgl&6DBn z9By4BBUF3Va`;28!0k7PZnd@YCH&`W*^hdWun=POwcW)5%Z70zn=K>60obIA70 zJD2B#3#|xU*_so9Cv7=5J8}l#8QbDNT$(cwPu8w}tT87VPt%fKtIHXT9Uxn0O->A+ zlui44Wlk)fiJiW)B4-G8YrNuZc`C zih-dLa~SQRf4y>4PL}u+wFcSSY4`t;0l@gw7sXvyzp95E>R z`G;p#iRFy{W}G!xhTl7Q?N@)AStka>NVGG)$(XNu%zW$2Ch-7cQmo{!WBexe&NtW1 zbO`GZiANX<<0SuNT>F%(X10pS@e=Q2OdcxvTZXn>e96p}V$d*&*D;1ANdC%%KfPw2 zd8PP~@rvP+?_zW%N?tQ!$dg$!uN7A@MkY!AM#lJ&`zDQ>dA;}}|10Cv(V0i$ zXWl5DW;7>D{w>A^efmA2GdGIw7}uvre)JfLonw;zdu+xnA~#jy5yqymg>6sI_@mIC zCh-o&spBMnVB8!3+&1G5(J)@(zzGuXn=mVO-HbcMoYN(Kz_|7d$)BF~N?g&5d&TpN z%@ZXbKdJC1@(W0^tnVUxex^+NUo;z7nGQ=ZUn&wfx0 z$dLFbV@>9e1XuRo#IUm@?mO$j3lqV&o&9Y4$n3v})3e6>ux|Q2;$YTge?Bt(cF{U@ z@K@Q>ZxQj+-dKNd`UdfrY0uqw!?d+x+VqO2ew=otcxw9f8{4O^5V_e~pTBFmL;NHA zFOO`U-Y5(+)~>#KdX2b!M*3063hm76Ub%hR1;RS>sq;r?FBDJC+&%dG?D;~MbL8=o z?Ac;T&h)>n$<7gX=FGM}lbt2r&T+n~n~^St8BK4c&6pr&8%5T-8Ofs2c#Y?&8AHW- zV+(E0!cDpB?Mb($VGr^m{ekL3`2KT{`+6rH8z{em!F|2Dp7(Y}Dc3t6qxhTTb&M89 z7h?xwr;2}-^5zE_Sx<+mZ@&yTF-mLksUcbnz!?Sv?mx)TzW?bx>*OZ(k~e=P^?Mm@ z?@E3X%TMSfFSYdj|1Xvw`zz(oV|hKxcXGa#uVwz0lM+1~zL4cdvb_GT7^U7V1ELkw z4V36PCeszO{P14#ZkBUJN%_wAr2NG!AK%M<@Aop^_Jc&j`x2M1ypH949B+=6dMzrl z{6#Dut?K`g^|G8{5SQZv8D7rvkt}cfNQQd_+Z`gYox{&(d9A8nZK4x0oftS+sV6nK zl;y>*l((?_50SFJ=|7Zu^<5H8%-a|%SpFo-dpX?0@-8liVEg~T@?WyNYNz)nwx84a zxPCUWyswwMnyxoiviyl&^6pSsk2WpW=f^U?Dwh9{<#oqpxKEV}lkx@*mv{Mij==I> z4i~-ZzeDBOz@vj@eX8qd5A*7J)y+J%{}k8Byt)qUU|wBsw==JiW~d z`~Z$OGe3}d6Z3Lc2*2QDKAPh*nIFXbMCJ!GuV)@RK8hR3JUvbzH=Oxc$qF6wLzs_b zK8|_8d_40f2g&*y#{3E9hcfSF9^1K!^DsZ0`Mu01GXE&^a+g$y`=fyz?r_ z(|k>Rpu;73nvcn5uS4=Q-%=eLu9Q5@r^LIMr})6-g$HmlIsN5kihkVZ8^R10cR|J zA-#vwcl1hMJB7X?i1QnqUhTgW_G_HIsD2X_hr?|ZjY|H* zKjr80q|DFh2FP>T(^p%ol=S)ixDCM`v0~4FkY4P zQ~Sx^uP8yiRh-`N8`D>E|C_++)%h}LW7zOQ|Jr^|N2QWp`y2DuH`tZ*;y0$JvY!ffa_rB~fBokXJ$HUxQ*#Iw9gX<$ z|H6V?{o)HQ)JME5=;P7h_?dyIsC3xQEuK?CI_AyJ(>ov(sPF67g;MbcC(SkM^?`A} z$i-IL=zS_0GbA&rtPYH4&DHQXv^s4R?t?#mX+8Nv=yD%#hk@|ZQa8Rv`X?$f(r+Sv zRbvybqakFot*Wt#{CnYFyR3z-uc7ndzjj%jwH5wD@Q-b1X~aAndJ_KlM*Q|4t1Mb( zj&)iqYvDJ*A8jvLmJNH*V;-4 ztS6Z(D(frkReCxSTG6x|bH7q%N+pI+-Re=vXHTAX_SuuBoQdl1c0U{0E%oyrYFjDs z#d%7bX@6!x+VMf_~myd<=0(zdj1uHo+`Cc`L%^!dGU{aQtwXu zn-lt(KCDx;RnoU?xrfdpQ5=*U)R)M3;)Jsem0O-_G@_J?)gc? zm!Bzguf&VcPhJ$-xF`|2BexT9)OHGv*JcY74$_{3pB$Cofb7LM1Y3)P zu67(`U5TFvU5f*#?KoKaM;sQt2ZulZDjveI%_ng5@}D>=<-sw?H-#4m6p!Oj;n#Rv zqR~WX25I8{KlaWCDypjg*Joh(qoR&Vii$WWD*2}-MMa}LDk>`Gq@<{nqoQJ>PKt&~ zH5DZp)nufYl%r9il1)mAMmicL73E~4RFqSpVp2|qNk(;_ug>(QFa3VL_paYs_pWt) z*XMm6cHh}&pS|}vGYZUT+G{#%x@mf8`f8#zw`)dd;x!XAQ#2;cT+KqwQcZ^DQO%Q@ zHJTSRn>1TBZ)o;v4r<=le4_bW^Nr?==Dg;TkFQUVPq0rXpYA?4`$YQ;^%?7v;B&Xn zT%Y@VR`_K4JnfU~v)QN6XSdHmpAUUL^Kto{^||1y_%`!x>l^Od$2Zz{xbJx1slIc3 z7yCZwyV`fH?`Gc}zWaO+`+n-{@~!p#RqL;9qt$EsXm8cVY46g`(%z?iP@AoNL0h2R ztu57ls{Kam*8btw+%MFxm)~H&(SC`3bN%l3d)#lW->ZIa`n~J-ncqpjU;O?3+xqwL zALt+FKgIuE|CRpP{xAB!?(gvb#Q!`03;aZ4`+!~nw*`z3m=TZ`@OZ%bfWm+S0Y?K) z2Gj>O3+x=&H*jR&)WC&-j|8p_+!nY$@Mz%az~7qansjS2xXJh?$xW6wd8SEzlYLE& zG&$Mix2CO{_Go%r(}bq?Hhs8hZqpr2-)&me^k;tXu}iZ-&F*YAubHJ;ZZmtcvS!~j z`!%R_P|u*@K}kVNgPsoB666T_Jm}}<&6{^`t~Q_AJgxcD&9^pxr}@{->sz#GaZ`)Y zEs|S2)M7)6-7Su`__1Y^mfczoZFx`26)o4c+|}|(%UWF%U3cAZ-3;A>x(&L$y5qVF zt=hD@rPcUW3tDBj+Scm*RyC~yT6b?fqIGiX%+@crey8)W<_+tF=P+dk8FXWL_K zFSYB`ZfLvN?Vf12qutSV7ehLRs3EgMR)@SE@>$5G_WJfC+RtzQZ2LX!zv`gv(6hsx z9q#Y&Qipdt)OHN+cx%U59aneU+3|}|pU|G66GB&pz8qQ}dZANTr%|2Eoz{1Hr&DcM z+b}ik-msjo{b6T1x9NO)=XsrTIv?o#z5WLMP<^UCS6`}khlhs8g)a@?6#jAeZ(VwH zxvR?~U0(0lLbhngl>$)B8cCmYp?vuN( z>b|FYO^=WsqkF9AvAsuCWb?=&k@rPzj;y%R|HkMW=im6^jn1AvJ^S}e>A9ijkzPK% z2K1WO>&0H5^!DpLsQ1F&n|mMc6VzvDpJjcv_xa|gwl~G!^vF$nZu;@&h?}S0{Orw# zZ~nv3-;ip^H+*?Z>s!X$^2jawZaLq#XWv94?)Otvuc+BkFGW@M zZ_|HV|5g1D_5XdqzyXT~>=^ugZu^!8}!hil0m-?9xym< z@Xo>KZtZjH{9Cu(S`*VFW_C<|%y+kSxoyU6n{IR6uD{)MyX|(@knkZhhHM`4t=d&h zR$o<554~~dy+aF!{y5AqZ1J!+hg}>#c=!Xu9kIT#qheRbemtV}h`UC-FyiZxT}I9w zxqYO2RR2*cM>*pB;>N~38+Uwkr_r-UZyoI(GjPlUV@k&cjh#65g|Xkp_l#d0zi*t+ zxH03N8&`Elw>uWzv1hz8e)Ra~#(!n(VZ6`y)}8)$8t>e2=cx&|OvspUcw(E0(n)>S0 z3)4nSdw$x>E+9NE`MVA=@lbays}ce za@NX28Q~cZXMFR(&<8d>sCjVagNGjK`cURWCoQ8a1rIlUc>cp5Jz{ud%_DV>PJVP> zW~WR`=68>cdTjgSEgoO|`0-V@uG*NT&6=0>@e}=?SpUQytCLrM@TB3%wNEx=&&vMb zsau}Ped^Dr=RE!KGXtJ^$?9)iXg&7qkY@|lv|6)b&3Dg@dv14*K4*1K-Sg9*e}8S% z+Kua)u3Ng!l^dU1ygp*Rb$!DNDKC7sVc3S(^Fs5U$h-Jr@{31bQeWD!vD3yUH(s*M zvsG>ywQ2X}?wi-X?EmtzmrvzS%0K){^ebCm?eOZ8ul~Me;TBiHgo4to1GjG77P{@3 z?LOO=Za-6)ROoyy_O;?2eRgbmz3uBy+7Q8vE&c6{9OY{Opd;J~?*RvC88~ z$6b}ll{KHIe*V)J%f9&SOG}ks)#|TwU##!nfANTy-g4gUyFVEj6c3vcCwN}RANkkoF-4>K=*nnr3U3npbneeXq89!B+~TZD zL!#3A?QNIsqh3f``*CcKrt0nSWyd~V(ONxx#&Et{cn9^{TW>nBcX=1});)vk-_iF} zqhIQ|pkR4lwNLmnm2c|@t5d#clDK90Fm?U3oMmt8$EeSKv}fd&!+%v zg_}ogSw2%;P}G{Q?^X9qeU`5ms|g`Ne7!<_bdZ&=A5q&(m#C znl|FWy+00F{QRUNL%Y`vSrIaBK*uw`4LPX2dDN;+zG}tU)tk5MZKj^TDd@dU>)WWC z_P)9MwcQ=n4-9cF*X4Fqn}4m|@Zj!VYTzwzzx;G=KXqkg_l_%f->QzCsqOT1?r`y@y1RbiYm55a$y0p2N*(y1 zd~N+7=ls_OI@SbV9CF%Jej3qKil271T&Gi={Wj=2=d@E-g(ojM z_=aBnsmu2ZG%xi~m)7>V@c1r+`p8F9=52myp!&kW`xn2wOI7C`d?TaqrBUje-=`0L zWtUNnS#q}1YcEY!d*1x(q*r#C)YfhO?D5)5bJZXE9!z{?mswTE+9F?j>3((n&ae2| zqMkQyRp~X+dfY}v`c-xr@h$QF8_+dAD!oG!&{zu+ND0y z(;nw(KY00H{TaWf-SM|}sZaE@S9;pRJ>L*Ad)nRmukI*#WW#0CP%jJr9-d%rd zmwNBK*L&MTuhhFe{itty&X;zn5A@U}dfVH2>OJdZ{#*Oyde6LPdD}hv)9C5vuX*sy ze~PF5uX*##zon6O=|A_{e(t<79`C$(kE5Yo>b>*gJ&wk+Ufy}}9!J`x-a9Ye<7hnV zrSqJR_c+on^^u--?{U1>mvu^sqlu)EKQ_;C z|Ei~5>We+?f7N^TgVWRQ9hZi7sju<0d&i~eBE9qH+V!Pf>O(y3-f?L>`^VsE_l`^2 zr9RHn?j4sVL^|jD=bm=&xTIa`(>?9pacMmJ&FX3Qj!W95zQEJ&9hW9pdgu3qJ?-9c zNxRfndfL6?(s+L0!+YQHj!W95-h1Eij!V-@dRG*^XPRp%X>dGdfL6? zl6I+2@w9u#bBl=R z@BOyk)9xMDQ0?XJ6`kk4>K&J~OaGCccJH`0dG61ur`f=1^-f?yF)SEo*-f>C0)Tevez2myUQ*ZUOD;`R_ z)E9WZ_KwT5eH4eM-8(L6m- z_1&p~^CdwKMNIjSm)%%8^UyuStg-_>37?$Q6; z-$LH+n)i5DN6$|89=NgN%G)(vQ`YGC{*T^P^ztYhKkp$C*ZG#y|1fXfN6+LuuDmyO zqsO^xI^QZw1;^w{F~0{zXO| zox0%4o7i7on#TG|SmZtW_@!xAAHLD`k>me+<&}wVtp7zz)21()Go6q5nEPMN+ax|m zgb$p!{PynN>237*Ll@7zb{jQXH*-ev^vmy)_g8O?uGb`a+o5YaZ0tPv_>sA3DOV4$ zvAQd7f5qf`*3h#h|8f0?M92t||Mjp*32cDa%l~9|<%4W4?}e*+YwWoH-VdLiAm2>A z{4N7m9)49PjOhCM84|(AEUCOJ@TA$Z`tp9qbeNW+s(+*Rj`P3M-?+c^u)$8 zG_i1YYCLZ?cI5=G{Qvcy-(AD`%pOi@-YmYsJ$uPDU0naVO^))uO%~rKzq*&}nwLBN z8usfbk;cavT2>B|3I*F5~yUsTjT^>=;!Ot}0`ZZlH< z>Flom`s9h`fA_#*+EhJPkK`eamLQ@*by`A2DR8A$?H4=>7xyC(rq7jT-6yE_rW2^&g&)8>yev zSZQN*qZj?_S+TMI9xu<=|0?;X>+#C?u6<$Sx#k+{x3SX3>by5wK6>iEye~~mOCCSb z^Uglr!p8dJJxLe-?UeuNd?t-I&$w^t?0-F7d;63JG}iBxmtRKSa`oC>(0Kg_GjAvR z-_Pr$5epab(MVSpHg^0GOZY^dtIHZ4=Lp`KXz_K`T{}zva2^ZO=UrRb==l21oOkUj zx<>0b-}Kp6@3=`C!NEn3sP)Y~%eU%Nw}<{VMv0`@!YE0bX6%=y@db7Q3le z53td?g^N;WU0v4r_~Zi))42We*{4?@{U63TkB{-V@9N5bIR295*G%|7)bRnd=Bu_& zV|9OhT-(1tO*DF5jl5pyKWI+Wz}bTa{q=;{$Z@VbA5Y{HTBU==o^1c&dF;P>NF&F& z{I}R^UeEN4N{%+%GBf(WAD{dcIQicP_fN-Zr1T%3zm4-&{M$>1#`+x=_1E9${(6bg zSl!fl=IiRun7+90zxQ}~n2oOQgj8N+UwK>nzm9*U`v3p`|1&e7<9&=t#D{vt#`nO! zCly6{#SH`b9$GoBgZ)S;u0kLFLB3ep;Y?D9o8cCckK5sKl83usXTEo5#Z}nk2EGTw zv2@T(GHJKMJtPBnz(0u@SNR^{3KAq^gEd6K-LMnCm|g#YUeUu8QhJ&(!1u&ycfvEI z1b4$2zCRfweZnau3OB)a?fD)Uu7`_BC~kp!NHFe(LHts85UzuF@XZQWi*UsVQ%EIl zhMy8A?t)A8%!xi+vB1DCiekGlT+zWul8YN)F{$6h`NH(>d_Ps{VJ=C;ZP2#|b#y)^oFz$#*qV?p(1-Yb}Xgd2zpx54;Z7;|672~&s`H^VH_a7(yig$1Mzx5F}0 zi#uVp{B-rV|CXz+6&qU<~jCu~YAYrZMbK#w<)H8Mp=Jk~G`~-x|w4 zXKo#^lH}knSWmKXc~kRlB%Sp#z)8f6n_wnM!L6|UIL_q*_909o_TTlFxeL$3h3)U4 zPh1bTl0xe3aJiAk&~AaFCvfh#5gwk5pJtxn8IpjzVUt9Lh3nuZVvv5|mqd@d;N*MQ z8>hHN;Br!nTi|+9joaWkQt&=~!gkY{7hDg`BnuZFAUfOuPZLGzVVa5gETd1jg{0zk zC~xMTgbR<)WZe%l9#~Iuae0&X5MsskFo|T~CYVmra0}c_3Rp)w{EDQ~?t+oYtP|rm z!1E-J8hO+B3B-z<;5w3p+h8fN;7&MY4%fKM34Ak!eHg~N!%9+wyI?&j#Q8WMC4}VT zdKgXea1|z!9NYxcNj7eQxg-;}!D5nuJ76VA!(FhRq~h|%_OpnI{Ug62Jz+k_ki81i zi9z-%%wE9tNxcLj!vZ&LmYh zpN^t@p2q&cU9f?K;PS@$1Bez^VG?QhjX8u4lE&N$JEb$9wCiCEDZ*9w;tFbT8!REI zxC3rl#XN^{F0h>B<4#yh@^Ci{%3_{z9qdiAaRZDenYa6mg@q&)x5ILh zj5}d1Ny6PQ=n1YVTnBrTc-$Z)vA7YYk{H|!4`eHft2t{5D`>l;L_9 zO&quiSCSan+c1|z;WlV{o_Psk&j?8o?toRK5O=`_l8?)W3xtq7To0p34z5BovEafN zb6IP-zrkWs--ABkx%KQf=HCtX@)p~1vKDZ39_P<^j4+2p(=MF-5_<_3+KCPq4%$fn zxG;BptWF z^~8+Z;IQqCpMH$+RZ{aFiGr)}6H>rDyI?)B;qoC2AtV>q!{H>IH8MdL z$)QhSpEuaMaxC}^N#UBZ!q-S9?GC8h$-bpsxQ(RIZok~d-o{P4c-Kyngj?YWVx-;$ zexvX*I*Fy-1Pe*L^aFKo(k?Zy4~fB5_za1r-U^SADB4}Z-Rv2ufp?P#+zfvt z6{V~@4BW%DhwES@Da8%&K2n0);5pKp{@t*7G4oHm9_A1|?KXIx6iPpP+1n&o+F{py zTo<&fQ1=$+C^ayW#N!4SM`Cd!>{`N{oT47CCDF9o-~nR5ov`oQ?2Vn&z(nG}O)#Al z;}-Y|iKN~EBlfcea08r9f^jo^j}%hxgg=uY+I0u$lPI_eK1%o{0>uVvh>dzTY~xTA zH|++PPIBb9a4V^y-2np+a*x1ua1g1$Rru^7_78i^1~V~ubbDa9=?`mmyy*taT7l;9@#I4PmV3U`wt+yPIKLfj48ma#6l9*!h=xDhTR zIk*MBO0sb~JVr8c7xaIh`#G+I14tUK!c`;%x5C}Tggap92kakQ52Hzh)WbO>6gR`Q zBpA2B_ec=#guf6iE+0D3oirR`&fz3dhnwIlB<>XBgcpd4%ZHZSPlA~f3(O@!xD6H) zE$)EfA2BBC4bVhtrBC=Qsm5*4Nvd$6deP<;K!s0cS4Jk`KMl(OR{krEGC(_170KofvT838KecFsy?6 zsno#jL@PD$1gSen4GjB?dB%m?NhK~kLCT~Ch8V zomiv>o*-tafnk-*i`2mFBtdH62@)$cFzj>grBVa8lSrw7CrF6Yz_2eAu48K8cGBRW z2A&|bxC@4T$)3T5+lfi^T1pMvPV%J&o*+3=1H-;z-K7R@C+SiHPmmO; zfni^>U!?|aC-G7PPmma?fng_j4v-qSo#>?oo*+7@fnhH0Q&I!Blez=cz!Rhzcfqi4 z*#Ee2J1LVIc!Cs54GjBM;pc0qf!j%*)W8!YTWVm~ckE%Qf!m2$YTyZCk{TFRE%#q) z;C2!#HSh$9k{TFxlKY9&!0jYNYTyYHC^azbl){q+HE=tr-A@fXL8@>U3_H!Wj0?9D zht$9m#4a^3tcL4ZYT$N~BQ@{@$&?zXKf@Z~!o?(OC)W`?Mlx_0Z1+9aAg+gtNhCNk2IF=;gMwmhpa5H>{#N$@@I*G;Y@FG8_z-T5#q+J za3`t39q?P?!rid-&s_Vs9u6Z;+z98BGTaQU#DNQ6C&joO*4J^|AjWfn*Yl*l8U4Ty zNy16}WmaG0-i`}@B9XX!FjM@mv|r-B2UCavH^U>O;aBd%u!hv(Zg})}<{Wpys6V*p z;woHCqHrrbKxmv$ zx@eS+Bm~#PUx^~^T{TK5sc%9%j3I7Zg*FnqpXW!YL~txz2X7^*JGpk?lf*!~6>jUH zQG(e6cK9Vprp5*P-^k->SK)C|S4}&7q$hKTTj9xG8l?hv!-sn_57G|bB5}9_*51r< zzoQ0rFldw{Tn}$2@wf`Nks#a--zQq!3D1y*Z+R@7(qE$_)H8m#n#AH(xPwIDcGzJ6 zYeu~u4kAUk3a5}l+yt`+X%y!-JQnUF4%`91CU)Ef#|&nV;YK)*q~K=w1To=O7ZYO+%Pl z+zxLU$~;`81}2d@+yplb(d~}u7@!s4p-rHqT(j_C@JCGg{w&cF5G`7=Yk6j6EsQ&E*wBo zaTVH0Z~77XPvKl};YUQlg5n!pgfDEAE2zBobHdVIGJc*TZP}8du@0IgF=0^9-LPWw;e? zCl1^W$IN3L*?UHKA5mB@;f)J8-Y@#g?2VV<1~`k9;KJAL<2u2GMI-}vz#oYj7k;vg zwZMfH#DKe?TinGp3m-`5I>Ch&qQ!;9B!YV3#~GaS1^zt{9w#-p3pRa#xy6O6A7&i5 z@F|jyTj9$j7Z;X1%K75L10)4^K<6s<5-zMJCiZ~viEPG+3$uwHx57zJGtbnUUR$!mY0}Cfp7`*~PizF8F0J_eOEpav$@|IqKk3CG?3~VFAg)?XZ$q za2M3QP5)94qlp<;g(L+RF4@oA(!T}1e1Ln3i~)Y)U=PS~VZTG{NnC|TND_~A!bR_} zf21F{mc(-H+n}|S^`>1|KpM`|CoCg%xD%dumwOHK?}Bk<+~05`Od&?x46{ibZiRIp zutovlykKJwk{VnOV~7h^;e~S6n{g^1alTI0OLL{g5MU^*$qEijjq;5JxHif{*P{VjWvInl$(Bpx@xmEUn(*1`hU zlYHC;m1^#X)CiM^QPu*clQ>xmSWfck-wDr?2 zmE_=Nm@UDru#jZpc6fwj;7)jsq~UHDc%H}OI=Jd*)(f}7-6T}@9&A^~v6vG*j3zm_ z3Kw7Cyt=ZEFqb%S8!RSexC2%Z2kwFmq!?Fzp>JZx^)QAM;3`ZcHrxb@Nv5m?oLSG~ zf8rcr7OBInuz=L!c34iTaVI=Os&F@K|0`o-{)Gcb2`(H*3UMP`PV#Z#T4Ke8uMrDw zhbKuO?uP9yv48e4C-5#}!A)=#G2>Qvk+}D=zQ1wbATHbhANhl|Xveh-r#JUeqM36O z+(w+#2nV+CQA%)OjLt_X#8sHu+J}GtW*%U1TOY-WJK($Re3VG~5zYzmQFOR)P)8sB zVS>lPHKguK?!E9hsl{EeZ>SIdhC~g#pH$)&c$Ab&4Q$uRM=8bia4{*C8n}npaR+Q2 z=A-0G4V+B!a1-1~tWpCnk}O>5?4wK|8Bzn+ku=-}&yy6X(K8RkgsX53NstBjhp16Sc1QY1C-I4Q(kuy1$PRBGV;Bp0{9qa<5u zV7neZN+zy{i%GiFz&*r_J7DWbA0=68;AE17o8V4jlp1)E#No<~jGx3v4O~Z}a2q^N zBBiD$<0pDtg=PEPb|0!*N`-+fyYTI z?t*;{>@lf<_mf220*{h-se$co@lj%NJzPwpr3UUH2HXK#_hpYs4V+9uaTDB0bW#H^ zl0aPP$NJyN^&|?J7n(^C zE_9LtTxht9W8p%p1Q)tVCN4BiVvpfMJ4wZby2;EHE;N$_T<9cmxX_TuzQTo8V!(xN z5`hbiQ@93kp`8TbLfusM5-v28y5sZ@oumdA8m4jG;X*5M;zBnm#f8Qs?oqhVP6~0M z?ryF%TxcdaxX?+maG~KI?n}7PO3b*>O_Fh;aXR}17urcYF4UQP6crcRNZlLkH+Y!T z;!ZenCVd^_u`rd?;AZ#)ap6|@u{iZ^I4+rSNIgs?SyB(5AQq{I=Vmi*+zo@~aQ)&s zcq7r{26$;M zFpp$OJKRewxC5RgQH;$E+q{S~=XyAnRB|mC;rAp`)&-8($nz0yga=<{PoCqv;3ZOo zEBQW37|F-=a5br@;Cg{uNjYwZ?~_v82^+R>J$}k#3w)HGq!Ks4J4hLBg!@Pq?tq^Y z3+{ryl62g)>LdP-B5F5%*E{3`{BHT;zIzPm&#l8YPQcw)tka52fkEpRij;C6VFq~k8A?Bbfjg`G(X zu7|VsaxCV@46}(1x59JcTsv+UxR2xFI@s}T*7`@iqK9e3id*0ZBm;NC+WpM4JQj95 zz}(_`XdwBlk#Ib*;=*@HIxhTFi-TvKhBH~1kbz@4y$*l;%ttYRK;9qdi4xBG#ba1fL|%H<&kAN@5vqPu%Q(d-b37HIWPDDDLuk`!ZV}36xZiSaf zI<8z|FA+1Yhl5B8uELF^au4$W_meW*0ly-N)Cj|WV{UQbZ6rz_3pbNWE!Pe#Cr;c6 z+cz)}sb}6C@HC0U-Ef@NSIPdK zW5HCCiJReJ`5Je^ABYJTKH#&OG9Jm_%Zv z20BP2E`09>Uw+9W{4&4A-9F3%ytA#ZQiTg&3G-F#xbOh6;SRW}V)a1`f-3)hhlsfRxjEiUYPo3B!-3BSy-co}YlM@W#28P*Uj z?uP2^?4{kT1?-{n-*5xGG?a1R$}rBCIB_RjKipR-#cj|W>#JBfM`4x(w?Y>wrbgI# z1na>V^e~AO<0iO~*l`=IA_ce$_8-Z$fD4}?>9}y|7}f$8en}E=Vc}T0mbh=g&*Ith zxUlt|zKRal!`n$AYbR_sf&Ot}XOe~M;VP2Jn60pYWZ-tFOyoRqVIPu$3vDC;7k))z zaTlzzsX}^dK%}|it86{AQiX`-o1>qrcV=GNeXZa z9JYeJqGNx;6jF(s;Z9PDJ7BYwTz9w*7C(sp$+^QyQi!`?mxowW+yGaSEZhQ{SvZzF z7H%LJwA`FpdQ=#T*j{BkhGH-pB z{&As+l;OhJYghx^3@7CHDi-=N!Ju{YNxKdXC)v30y%qj2Rai^0?37!g0ic8=;SlxuYK)Jhh2+Vcy(u&&%|AntcM>=Ch}9J$!sc7t|(?acm^C&M;)%h8-8Ox@esK8ckT&z2{b5Lr34o~>!(%9 z@Emx`U(0>Bvr+?31!$EzaX7V!R%yVK;IgJ#C1gZrB^~x@rd6VF19XviT=-3pR!PRI z;n3ztOl~TBQINe$h^=*zqbjt`pLH zxEr?ard1qxFdRV2@Mt)NIPoO-0I9?+@MYq{^P!W};KByt#)Un)Yn6ID5>6z_sLo0P zEFgh+#*JF#9TJR}!W(*O`MD?7x|dejN+R(>_#uhG>)@!~S|t{bgBM9WUJp0*(JF~} zK5TjudjszcpCYMvEeyF?tEAz=)g%Kigv|}?89Wj$BiVR5+(&Zo(p$7j*S=aM508N3 z`e~JX+z8(wC3q3sGEl3O;sr1$TB}sy!h3FIFX1M*js(WBrf?ex#tWfxJ9`Ni&LX|> zWVnGu<9TogiN)>k7ZQ)x!wy3@cRUo<5fd)_VL1B=cf)?M>?J%3E+iJ*44)m(Ucz(W zhs1`L!>>sp?tDFl8<>B*03LjiG2o?e)k|EPcorPAk@?5d;fFTn zd@Sn@t2QwnT=?B)<`b`mQ(k5~coN*5&-H~D!>}!!Kdy($R<0LZcxfBgIo<#l7BV*6 z44>P4!=-oU9kB6s70$u~l-)0?g zVe)?F2NyPwd|YTgzImle$1~{E4;}{RzLIUvu_`o5? ziCf@Z?=Vh05%ww7DhAvD=Moi9flc4lDsgxaj3SA+37#S*yatYZkG00*;Jz~MMR*Ae z`GEPvg^v>lUIlxWb1rxSy!RvaJ#K|1qzV__`Z4E>$G{R&EA8;sBV3Qt4&NdT(he^= zxgPJ}{`m=GAtAW%C!)ve;Krkz18#$}KII(oWN0BqT)2TG;(72FV#4cTY6a_s3mb?T z7v6Y`q@2tz`e>=`iyP?oqg~hLqsKLsiTPUJ5sU&06C& zIM>Bm<0OJRV;Bj`893FuIy`AJ2UWW)U4ObdnJKGEXu;cnFOCp1mve za1M#WQ{d+$2CsrG&T_ruI=Jme?kRX7w4Z0*a0jfr$h_e~T|M(A^>8dPc{(m_$f(vB^=$>Pf5e$;jQiclq@_3MtAa4 z@^Imso&6L$UJP66{ghH%2dhXqE^Hd^r&QoUa4@ODW8e}}jifwBn zf~Ue$k$#F9uYvP>aqiL%kN5UdY=8%caUV<4!j9i$X@OWRcD4Hu5R zoBe>t!|!MKDGhind}+3yqEDbtxNk1|055^(QduwD4ZAMzQ?l_0m_c%IVL8deh1Nxk z2N(7+`zeLE8NN@7qy~<^&rd1AjWB32kH>|XOZ=1?T%YErEF#KXJQhAf0&y$cPjt8g z{z^jd2H1Zo`xB3bVfV8?#bIo^pAv@)PmmtaE`5ya4o`=Xk8_TbxL3fMRh$bh zT=oRx$J62KBpSEFLnIb2g-utpm+&BXH%Y`za2qkluHJlqB4<9Ak$=na&TB5^q;Smyo zJK?t^0qkXH$0lldEphX*?P_k4}xJN71zT#Bn?l28;J$C z!JQ-6_wS%VZ%mXfbm4x60@K+LnH^65LId?nCT<{KKDaQ414sqZqaO1nIDQ<)P-(&oE zG|VCmxbXMG+&`08(=zrCiNKw3@%!vCJPjTv@pvWd`61)Sd&5~Daed**a0AJ~^WY9* z!|m{)BYsK&Zh?jm};2+sSIy@aR2@fGYP+z9XgjJ<@X!-h)E1s9J0ocn{+Ks(99g}=zx zcs;z~3-%8l0{f9tJPMvA<#;XBRP%g_Yhim5bPv}coJNB2B={T&#dF}RBoZ%xCx`)e z!Sf^ruY=Z;>?K@yj2Q7s*yR-Gf=9qDBpGjj2Trrca0d+ip0&q?cM%(&2nU~Kjqn(_ zgxK*k_&6!Sv)}0W9z*_DRcr<*FRO1$Smbmd+@pIe<#9%;=*uJjz_?0qykTZ3yBLi!`Da+Zil~+I=mjHH23G1^%(Y7LFm= zcszWaN0@sl&JQu!4a`7_w3$fw#aN2GD zN&%h(pCd(h4t$k3@B$ceyT4L~3-2KnxCtI3F1!+U9Kssmp>P{<j=Ld z%C&^M;J{&AQ+PBSG2CB?$K&8bBw6a=8e+zCU_>nY3Ky;-Id~QvJCbw3rltRb1#dpZ$pk!a=D#7vM3l+XB`! zg>l0Di`ZMZ0|uDclXxKPdmrb5N5N%F7(bp452Z0qycE87KhJA;8T^G5;`Ok5I`fZ5 z!s(<0H^IfE3{Qh^6DRI~hUM%}T=*)f#tUG#6$j)5ECA)?}?aOMO4yx%2z1{PYl?r>q`!>kJ~97im;5q5Z#`Nc!w zM3Rpuz&XT@r@(t3<9>*zLftCP3l}aSZafW+%3>|>ICy{r-OD_{izFDYhfSa0T<{<` zfkfg7Fk!X75+x3wBr2W_Un23i4W1$icnyrnX63r4* zt|TFN27H=C;8u8y^u{aUqP1LicmX`Rj&sNP;b`R?NyLNK)4qXq#4BK*JkAR@z_^W^ z3odMJW9_6K)^296;=-;kGiE#j-cBlU6@Eutcr|RD&pP74a2Tn_W8n^>r1CrtPm>_L z23{f|cmqs*g*C;6J4tW62x?#DyznIWBvJ8f*meu!#zWwEl7t)KOp<~p!v%$`BW{MH zU*mej43a^I?-{5?4Gu%kD3)o{Y zponwC17ZI+*%Np))b3`_;lg$$-1G4eIG&{7MmUqC;mPpcw^<`R6}~_+@jQ5%WaBk( z*M80gFM`Gc>^EGviWK8n@C8ze=fNNc>xc^{kV-rOzDTNZ8$3qbcqI%w$n}WpVFC$U zNIiUv=6t}_Xqs#*?Ttw`+89qWv@JzUql;TD3Pg0I6pZY5!NfjOki%AVGbdfr| z0euR{aonf!yLGXD}fY-uF-!pzZ2`(k2cm{l)l;FA0=Pa)e&FnoGPJ;1BIFtn8 zv2X`T#f#uaBpI)OKaeEc4X4)9CvJkvNfB;=>q#M=2fO^h{NNGr6{5S3`wzV7NA>`2 zfJG!47q&P@|9CL$OXBe;_%x}-bKp)=CH1hon{h5?{BSMFz;j{1Ppms02$zvOJRKe( zC3q$Lg%slTaQu0WwS?C#a2big)8Ut-2CsqIpILi65RM?_cpQ9!SkpKztRNYV;^ zOa6)jx4{pH9e2VXi4Awdx<5G9{p|BU{gs}i5I4X(NFHv40sJp7o%4mWND-b4_mfiG z0WWF-_?Zjl5Z>z>pw!~2@UwsbC2BeQ8P<{Bcmo_67@&mU@vwksaiK0KfY&BmztBJu z@o2c3#NxTIo)~c9x)#*0q#fExA};)?CH>-xEYcNLTofvRtSS@wS+-yFTx;Xg|R}2?;wOQ2rXd{E`_l|Rv27lg~7G58wRm= zZR{Y#hCv7y-{<@O@p`;oj?c_|&gXp2xwqfR1fAz|QP4%c1ygjD2OVafI?X3zx-M|b zAFa7g@dYUBI`4M4drIec-y_{`(R?d4lyHHPW%cIY)e%iEl^MzX;1KI^%90WU?T4llzr9bPiYd~}^J{XgrVOI$_j zQtQt@6}-RW;SEpq%+zVV5Mzu9UwE3iMe{8frpx>?Cg>VBo$g-H;Rn#E!>?ev4!@2` zy3YHb;ojFde)`NclDe+)I%lns48Lq>pZC&(ba>M#_NOr!-sf!3F2{3xJ|^pM{T$CF z9bSo?PR?B;`7@^J5;sp>BbljFJPotsI4{RM-Qe7L&P9jc!~$LC;pe+Ir~CTO`ytWc zE-W`DoLp$#b$B=y=?s5(k^9K_2H$nDJ)kSR&ouXo&hd~-or_NM37D=6oW9I@#&OQ0 zuEUw>p4B?sj=4I^U09*3od2_H^JnAvAxzNW{+GMHI(!=1b@(z2)8W||q|3b773QWh zyz>ler?Xr|S%-JO(r4=&54*~`#dy9Rt(On&^Zr+xo8#fru~di8!XjPdk1^c%1`oK# z^FfDqy4L)4_!x}U;o~t<7x-oL=J|A$m*X!{4SYb?&({r7>F_r9nP)V2V_}TvRquE1R}Jm+VS2C*Uy5d3;ukPUhxdNKe02CMbm$@<`=HO& z1%CP=*H~Bi!G~QJ9X_SY9CY|>EYwBbI(!PMI=tFa*IkEq!%Q9iBc|y*zlgf7aRY;|v*w)t!2Pbn zuV95S4K9CZ&o~~w^JDYY6@CJpIy~ePbJO9Q|L3~Lc>Za*_nhf_4F12*e4gXs&%dzN zI(+$;)>(&7{>ta+0^f~sUK>9CYwM%K7bC5!y!SW0x6~y*`&-XjUF7>#dLLckU;JSI z=ydNI$suUyGPnL{ov-)(Eq9?^ho8a_9qvK14*w4;y|(m==48++&B-{&)4cAg&B-(! zZlIvUn=~~id7a@M(R#zsKEJwJvwu5hsNas!ocwY1=48GO-+{8O^MPyl4CC|sf0(4h zn>9BlIUT+YqjdO9jL>yHqF-||Rp)s~|K_Bmi@ae=b29iw->>pf=yg1NaBFk2Oow~X zqr;oDH78XaJ{T39=Zgk3C+*&+#Lu9p!)pyRPaWO`<8_|j!+agyF6A6`cnnH9^~>hu zeAIN2KR~{7XrE`T*_?Ff@F&RV@cwH#Hys{}Rvo?@E4@$nK`hhZSJ0zt{K7ic$M_o0 zTE97&qs#oC4Vsfhy3VU_=p1hv+UFN_N{6=^+MKK~Cc}qdhU4M&(#?La+|WLsugB;j z--V?*e8NW7Q-@#199`pYFzV)^eSZAcu8|IJxUuVDOq%z^Ovl6hhS_5}d2n`jWPUplyvxg6m^4} zcCa^eivNJcI>+~7iLUS`7<7yM$tRC=?HmtZiRHS)U!Y`6_})?0N{9c0u{u0r=jLR% z&hUPir*k|Py*j)A%XD}J7VB^?x^;M+U97*(@I^@7Y7YE&q;-v#Vz_Q_`>xhh=lFh% z)D`|kwmHe_6mO0Wo#Bhnsly8~O^1K6TXQm5hntbt;ol&qGkiLh=J#*o|Wf!}+cV3^MG@fe{CJOk~z#9bJrtNbZO>jt;(Y5(XH?~JiJ z%i{xG=RNmwjdYH`-rM==WFLDBUAn@#F|J39;rFmmH~6r9n-hOgWS`gF&-HRVd@p+9 zwcND7^)x2DTh6u6;lE+9uJZT;%t7aQqXXR|I>X1ITZg~GJRNR6$g@I+*T*!S<|nXN z*Ll6M&i{7LE8h5E_mtz|QCOz4y#689)|fPJgMtoUh;cf61v+$zuN~)J)@8mMO@Hwm z=9jR{@o?*5=Agrup<9Pvz&sru{737o(|kEbdY^E=!(DeBz7nmv%H5c*!=uJqD;-{d zju^vV9pS#y$&t-T%TcbKPVrt?pi8{k(cUYL^I|O3b>8Do-uI57ecnqC*WoRWalh;E z5oprk3$em$3lppZa*kK{BaGA8W1S!Jy3E@j=eZQe`4|**fj2web6My3GECQvQ{0RH z=UjA+k1Keu7|*Yxtn0iS={bHahIc&G_a%;p-$qS`Q>S?b>hMU+)mh#Pv%NNaBPQwa zCZ{_W9X=RY9ljjHb%|Gr5Fx0hIFo#p(Wty_%Y z>{ZtDPRIGal54Fi{3eF!I&W}|dsc_R8N=@y+UI+3 zb{%#26C^sEo@E`451)h{9li(MI{YjuI=mER9d4fO8KhI3#(3`;z5*ROJP#vvg}=o_ zo!sKth=MNN>U)6Oe3lOX1{EEie7oze!%t$m4u6U%y1{GQ?dzsa@omW6?R!PO8>4i1 zzk95M4&RBvI{Y@8b)84uZ$3K96Va{1i!oP+-$SQv@Q??r;aoo!;J;z1B);0E* zr6(he3I7X2bogDg>ISDD^c>USlOJ-oPCIA{}mj*jnlE?kMXVpN~QJ z_&GzK+U5E>9=-xgbcw5&Y)rWAQF}~>2V=Ajw_}*jasl&n_z#b{H+A>`6m_0wV5tso zKHpq*xE=F#mQP0Zug;OTeZu+c@F=8ocpMt>p4^G4j)ym2V2|nW?=VJZ`D857;Uk~+ ztkdC%n5VhNzcL1*|lEYjhvo--dE-VL*LjxR-a-q1e(^(EIphZmq(hkH;rzRu~FosZ+;-CuF< z>+m7S={#SFh7RBLnmOoj1q*a|5ju5^`@QbG?(=nr??kH(ccIsqDu0dHj)zbBm-E%( zDVU(cGcih+`9;)qpL^^9UFXj+S2uXjzn#zhevOa2km&H^Xc$xF?=aT!@Re`5r*wEW zhU+qagBd!!_&?^R!{1_pPL}u@gt`tN_O^A<;nOis7o(SZK1cH(R>Xbo8u8EHx-WFH zvN`!nudf%n!XN+SoOOfOPx>Vbw7=m#nfZ%;{tSw_@yu2FCCj6EomKn!w@Cf`KcBN& zzobcrufvKM!>_O2&!6YkYxGMtZt0hF=nOBz1l{0zTfbzI4$m6UFDb@&-ezFGr1?SL z!*FSk&viWfB$nuKH>x_^gNhFKVx~^k?w9;(oqowM?~~?TFj{B%JQQ?z0mkd_8yKVO zJYd~^{>~-i`EHbSg|{Bu&)>`BT)25yKfgD}bDQ76LS5%wH!&xj<>@2LUzd2@E&C;d z9x@+pM?r@V-O3zv_;PgU@a-6>!wWE6SNT)S*WvBA?w8Eb;TlRh{BFj2>IPrCO}}K3 zF7q>}#~A(s%XG4>YltBa`~H?!#DDAXuea-$G>pmcBn)%Bz+1KV^PKh^;1WjZ@Ix4( ztGp6S@9acIX z{x6p4I-l`7=VDBeciYi?bdKj>l+NtbFZphi{h*Vb&1)Cyugkp4uCA{R-;QZI{5MS2 zRlY83zr}doe>ZEZb37f(bcx?UL)Urt-OV$`@J;Xsnv*ggHrgK2dA=WGb@*Fkb@-j% zyY4!?+Mez^o#sO^LFc&#lXRUo{)6{?)Q=H(EEp*K1$GMKqIL-$g zWDYvdFJZo}@szRdab4sUSg4bOjYUmY_-)j6o%cAz+#d7&Kaar>9iEA19li-GjSoMA zWxB>cVvI4#q3%oCXh@cSs~aOybMONV#FBpu!b<6}JEg{3<`DoGqF&Iw>#H!RpF@%Sb@&;SboiEwoSQE5@Qdv+o#B6@^@*Wc&_R)-#)`V z9>@8?E6qn&c_n7+gou6r+V73ltuXpe3aOMX0whr%v3STz%A< zKJBykACLJuu4`PM@3lI-%j4c(XZa)ysk)bW^}l%*>G1VfW=#0AXY4l}UhP?*tHb@# zrNhH8M~831R9)eb-JW~iGs}mfsKYCf*Wultx5spj-}}4s)D1rU1#7B{yy`;FO`YP8 z{$Xx0hFAR4`95Qv7kR$DY#zGC2fgAsuk-vqCh7*~YMyC2d@C9{ycA1xc;u`0fDT`S zE*<_7WgTAaHS^RdJ`TOQz}I5bvwmL_|MGR$U57V8N@uteIbG(zp`^nvFLuA^@Q^pm zS?748H{D-4!~aC;xuJb7_qZ;Ohu>b}IiSP4zinT|YxxI^H9ma9JLaat4`YO`@*61W z@CE;M?R5BZOwuL3qi){1!jGe&!!7TcgAR|w0v)~pb9Iql!Af1@)!*}6=(fjrHiqbM z1+6+f2lXdhi}G2o%yt1GW0WZ)5Xu-?+tU+RUY=G zXGo0UIbXRCb)A2|!W!xvcO&z6KgQufUwdxo@B>J6jko#M+;o<2Lr#~uWu^N_r}$h< z)J5L-J8PmdJPXAb&oh(${;a&_X{r86MVI*BNWI|e9dEc+|D@OP@F*cdAQ@jbL=7R7y4DW`94j+J;4(A5sO#`8TlP;D=`z2#RsUqT@ipFc>;B1To#j8FsKYf(h%x-jjL*_(-V9wjd@{;9 z`~XV2!tZUdLP;var(nhO}L!&|kRzYb@%@1LwNK70_C>O8mX z;944!;zN|74u5@XmXfzs~Zj=+t#Sd366|t}bx0w>_i7`=LkY_*T?)ndc$* zPhVU4Lk!p9AII2ZIy`J&@1?`jvCL~ryy|}D<9LQIz$9Jd+b~&|`Md-ACsX4%uYX|w zWR6brFr*iGPd*&Ij)%8C$ouQ?q3F@!YtgM!WBVtE9BiImTjUE5>7V3v_{Kk4H(lmE zjxq zpPX>A`RVj2{rx^w>!wTmDN-+cAKs{7|LO1ujMw3P(4oVJVx$hAhT*!%|2x&3b>%eY zJK3JsX}$y%UE;UV(Bak2=$|an;oY!ESNH{te8tas@}XzC_Bwnf2I(5_ImKG(9A9|0 zdFm1$c8-0e$MdhxvzPQR-usgN$xJN>pWO81%${}0VIzrMqZ zud-G;e8bhwU3c=-lINHXZ+eZj(xZ9ZYwdYGm@mL2UF0`0S@-Z(*I756;e#<-j|*dt z4zE7b8tP`AiTOHQ!9pEg<9d5fr+6DI(d~Q=mg=ee43_I|9&odBjPYDR)2n{&l;20I zUdkh8*`K<^FU@uj>e{XL;O(AAdiY=LJ&e@T=Gb?4I(HpD>TY{oFT1CI^5{I*NVnhb z8Qx_->mI(N+uG?leCI;vrss0wAKq7oKY!WzM)Ni`*G9MVD*tl7>m_{q`>xSz-iM!E z=33}(UjB*s==B%gkj~J~Je>=tk z)d6Q}iUBj-n1f9sjMX{0(O65x;CnE#+{J&u>NkK2?V}4`)ahxB)>=@6<@Rnq*4sX7hIqMO;)mG-L zGdwtBZaVzYHZ4ihoAwOF^D|Ye^>SPCk7{ z^U;&Jdz9DcaQiOipu=12+LA2PBlyUyeWLUH&2H99ujJ?Va1Q@+kMP((v?N1xC$GdP z9p1LXHPG!m5o2}uLyXtU`1XBTl1X|FpE{-`nWiW6gneCW9bS7sYo!PCcl%o(-OF#} z%uV-j`+@eY4sSiyzU{GwJm+BRqr*QO+LDaay}ZLX_m>{YdyY3B-N6qW(UMHn6>d4Q z#lMH>_fhaPly&%fRCF&-$y-MqUi(kxqX+XU$5;#9#J3;Y;@^2R2mZ%#E&g3dUtjo# zTss|J?Huc)n|aK+_PGvU zcV3G>w`!inmgK7otyPS_s3kcTy}H2vLepE`^Wql&o}Bw#hd;((-QebFElF0VxE+&p z_-KsN;fv6r%e=`Y=C5=72uixjFI?8*-!}FB-0#oktW&%l=IbmUhKkPfE$G#C-uw#J z_dni~XJLpg^M5f&*Lj~A&OgTRIT)=={4yr%8V|VAb0&`SKQS|o^B0(*Q&%~TF5Td5 zuQpGe<&KhjMCbStG%xXEQhpOF91kzSQeEfcu4(b_aam{nFNWwk?{%&HpmTiDb@r+* z@&7PdH+Y|!?wuIJH(-)3b2ldI8h?Va4)1ck`RnjOn5y$U6U%kDfgT+mafA2L;bT$J z1^yDfI=QhWSr7TQ{az;C0UbJgGum}{4u(IkH&Oe;0Mw4j^AI(r_45g$HP;xScfk~x32Q*7;k*|LyXqpb#C!>N{2T?T8BH3 z(m8(gR?jJ2<%4f?&+9zjjG4O3e@FJee(xe5RCc~P{Qd2&yD{OP(5=I3{>9fO9Ug+& zI?a2b=C$ETbDX;l7XlrghH*N46*_c@A3%?;@Jnc_`+Z;ht2+tXHaWClbE|{;wM_{fFpMa7s z@Dpf$*E;i;Na@U9-KQ9=i@f%|u8~f2>pa&^r+9aiba)!3>hQ~$sKZ}ktPVHd=X&V^ z-*~@u))oEN0lI)L~&hg*S^j`d4nwI2KG#n4_S+SlmhHu4SW6JzG(mM5^&%|(D z#Gi*jhqf&i>z+&QwyxEuJR(x)#2|jQ-?=9 z=^E$^pN}qGbF4wIoNqZryaAf3ev9)G2=c4WIF$_2dC>x|enM5VY&?#YpQCU;i)j&}BZo z$J**5XZ~%TI=m3Gb&cEJa$R-!BrMk9(@@pnD^SrT?zhDG>lCm5wtM^|*O6DCT_^9f zBsXESF7vGaT8C&}RQGjF*LdxBJxg_X9x6KgDrW0Cuk)Vy=rkXOB|6WyqIH>H3*axX z((!Qr_w6^G;%za+m@FTUVYuo#pc}O^07aL5DxVc%53&lAMc~y2yKc?OCI9{3^P1oe%lOKGb@GEH6;rFr9_y*U%x8{zA$Nym6boe1m)8WTa(BapRkMVp$uX{ij z_%kffsUPhH3|{W{gm4*&4)?hOuJd;oualpB zABYJ$%`an;uJKv^CeNZS@;jKR>wNDjt;rl+;RUOMcoWQz@jL-5bb-G|Z;Tn(n#{nQ&;1-GFHZSy9sa*x`dl5p z8WVK*yEUzw4sXA9Ym(LBosrSueUaAT^N`X-z5!KT=EvfAV`!gW)V+>}w^_%z=+t26qr>x2(&4|OsKZ~Q zpu;~Suaj+ClMRu4>DS%(sO`+b@$ds!tSj7wNyda<#8@3(z1`aB@Ol`b!y99W&hUvl zn71zQN@-&7U}TQSfIo2W1epCXIbmHLh~-W zwI;)LmjAoEb<^Q*_OQ2gc;nHowa)MY6m*rl_iIh2>l(kczw4^&{B^E1sm1sMT9f0E z`r7Y-;nfbbmmCidMLovwQAit;=WCJE;cqZXhYvW&JYx*sgpv+7jcrY)>hMJ5b%ozU zS?3NmXUx@A{^KF;2VFSS`k`AV;}|{BhqWeaA@z;tJfDeP$HP;xTo<{5$;N~iBd^0B z{LyvS;ZHDJhgV>*_Lm7K52L8F<6U=@bcwsrsjK`smgw-gNBKM*z8vKk&yOJW?a)4d zp?e(p&!I?K;YXib*Jc+MT`Yt~BFh|k7wUF12)=n9|sf7VkM_*pE| z;l-%w@E4e`!;1^nCysOGbZe`_Ph+YMzlOZ7^9hrE?sx7Z{_;%gsl(r4r7_7_t;sPM z>3D%(KvviIh$*c}htBg-jMEL?<81e@4xfW5I(!`_>M}1xS=V^mbL>x@=T|U4j`P3K z{Jr0Mz#n0Sep3qso1B-QqM_g?G|KR7xxED=2Jb#++ zAB+hPxx}8=Y0hJeG2y>sqz=D_;kvyapc=&uQ(cxRMNQVdC;CywOkHlE74c~>V4u6CZy1_TzY;L;D z+s(Egbe7-4ycokf-eTS2IRA`AI=R(*+-5#Hyc|nnJRdv9oPRWb9(1R1I?Wx(>Ky+R z|E-g|+*5PyNnPZp(5b_}yvLg8@a8D!4DW&EI-J~ZFX`}3n5(nA#RKk(7|(B^^(Xfc zpIEWSbb-G`yG|Z#O}4~L9e(5?*GPwpU7kNW{4PfRJhab$e8l_c@WV*y@IH_FT;szB zVVMq}gdQFK4D)rk-(#L5I>nPQ)BA+)#S|SbJmDJYa2aEDcp*mX8b7|k_0m;7`AK^q z#`Aws({*0-ls)NBjD$y5t%(j7(1`Io4Ru}OUp?bqHYPk8^%&33K5NZ&c)#a7^L6-W z%+%omChPD;7^lORqeF-1V5F|_XUO~_-RHf!-D5iZCp7B_8ag}+OLX{dEY#sD=Iih(FS-`1r2G6kozvmV zFiMBZ7@@-tV~`FnLX)oXfB)h7>N;y4d_!*$o;bdPJT!zW>8 zjOQ*Cn$mrK@Za{Q4&VNk>#M_$W4I2#jKMm*-+!#X_sQ{9nCp1>P0ZBcb(gpgb($Z= zd|lRsO}>+mRy(cyjLzjcl$zHd+J0#Csb9sU3dbogh?)yY!de`0Bj=j9)`-nzk~ zKeUFcyYKiX4AbHDKXNWQ&BuIVAL;^+`PAp>9RDB6y2`^p^I1C2S75#_@$0DS2Jia0 zwbEHW6tx)7lhG67d9{Z7agB7J3%cQWcpB<4hM&L?W2(Fu!*rcj{lYry6#p6Ry2N*5 zw65@*7!%|9>@V#lUF1$o(q&%fD|6CWz8+KKICo*1uJT`3*u%QQt9|X+s8c))T{^?- zf8$!{G(V3ey2d+xYyEYW&qA**@_T4%wpP5!O3zQ7;o~t#7x-7-Id`4r!_clvyvO&> zQRn#AKX`WO3?G9jF^1E<_POq}F6kn-{pdRC6i+}!7x>pdwI)@a;aTX>WnTMddq}7G zXe9lN=a12>8@zea=HDdoTK)@0=n9|ki?(D`jOUdYr;}CMlBZW~OA5NmlU8f<#}bU; zwtj7X%f7ksURbDed_iklQqx5qn`%o|=seHDkpAY$_pjBK4AT|Pt=*P%==i#Kaars@(uj}fiuK9AhIEg7W42ct>n`Fa#| zxZf7mP=|km4xQoSF<*x#jA%<{>+nUGp-cP%lD2f8r)}Bh@sv)&f5Bp1;SINP9gRuz z)flEreAd=&NxLrc37NKJj4p6_JD;b+gW8>!PV-Dmi{pIv_U53geAy2EpDyvQf7h1G z(;0phOLX`TyR{|VIy@C!y2$sVeL%X;U+>CLRi~Qas z*IhSw?}GbL=lHGD?J-^FZ!u3NliQN@(WAqAqFaX#Mwia>bS%>)-tvsLq@j!aIGWe= zSv=`X=b{UI-hyRHAIy@b7 zbod6$)ZuxUrYrn5n%7GAdFQju-|_HX=+Qa;8wMLw<)tX;@T=#Trw(5<%{+DZZ^-KK z5{%Gwu3Xoabn5WmF=SA>&vRzldya=+zS;9mhgY5DIjqB*psF*x_8jZxwP_xWE*);a z(>d$#Qx$tmhkG$Whkx~udFnKeM912mlROITI=uK{&jcO*o#hw*=2_vjHQwO~ds}DuN6gU40?$1x(c#{wTrVANe%dwCDLxksUE~w0K5rf4 z`R3=1)8V(U(wI7rc)^-Gp5c)TeZAA+J8Slz4!?u(y3Qm2WpC&#@73e8bol8d=BC3x zVUbSWZcF}G_gr4re&$u*^{mt32BySuZeH&Bpu@jKPKTF#YJGK`kNnJ>b@+-e?J*tx zD;DZ-H|FXZpR~fgGuXYuvoKPZx&F2D)#25@v9EM^D@@bj<51AycQIb4zV#f#9G$H6 z|1nR8KgI&x;5WXr_jH}t{ob5)nlFLBbS^1z7Y6AX@BD*nq_cclujh|0^0PnMS22d~ z|H*#U6+Yl+&rhA_UX0a#u_qaZi8{jvqYz{G5=_x09{h^|Nl~YHH+1Rn43u@BbxD`` zF)Y+o9l&ZkJRr&F zBEN>LPW2m*Y>J%D@L8Cy!=Iv{!z(Z@#`hnP?1=?BJOguexQtF6{wHd>#sgahB)vL( zG?wY`8R*ePp4B?QpBeI6UZ-t9GE%2`7^dp*O~~tT1syuP2-9?pPa0rOy1?rWG$)-;6=8WTQji0i1sN28#_=Wk%$b@+L7=9yh2(gTvEI(!fo>+l)q*5NBr(IvhGGrdo^ zxRL9t!;c}atNaq0(&;|Gp&O2eciq@}>hO)I=pfMj$W=M&Mc!*vYP;nY_6wa?VP@jQ0!=-Ohg2;Wa>VhabXRUFE~~v6nXSKKvTS>u`FE&(h&%F^xPNQZxqCY|H& z(4)i4_p^6(c*8@TuMXdc**g3bX6Py(HqJi&jpqqBFh+-0Vx$gVb+|pL!>5cl2OYj0 zO*;HER>XVqS13CkzWoTF6=V2EOx4Me1Cq5cV$*b=f29ZM@WWVXO!#Zmb-3dw&la8I zU*@fk@o7F0b9H#!pFC%Dc)~IEk`AAMf(~DVybfQA4qf8UkQ(mCCcMW4_nPD3r;alR z9exfq9bST}uJbm>+rwTPJ_gftc&iiL3p)H;jML#$(4oVpVYn{xlhK={`@CEa(&0@{ zwkF1eM_`!_{~kR$ycg!{9AAkEUK>8@6nj{Qry#3~{A0nj(8;Ox9lCV*Ih1wybCh(0 zcRJ1bY@Y7(NxIkZ@TAkNj}A{pO^0tmRhRi~jP=^^pC+4w4xfvRF7mQ7yq9k9mS=h& z9XYn1CrA*SBJmHOda0%LVH7JcxUwJ z@X6@b;TO=QYy8kfzRv0@FTB_uh~vD?G}mH;&*d2yrc2z3f(|E_n7G`9JeCqY~Of*++aPR0UH{ax5)+s&- zGj;eT6m@ve&DKPx`3#hGk>9{vUFQd8d0$=Oy=J?QVhk_CLY=+E9!Aqv_Ap<1t7oO- z;f1K{8sB}JYhg@-8$Xpo$s{gb@*^J>pbsrm$lMa-tunyQ)hU?x%QY2ABnOK&qYaB_||*OLx0$+?3 zI{YG*=gVg0BlY!{=d~F7tQD>+C|uQPSZ-FM7u6@V1z! zOZ+WX=jrn!td9;q{hDWv4u6KR zF@_&~-CF4?4_NHkrNbLwi4M;|w+>%}E*)-v!~W4(9`vTq{jKxjyD(UX-~E?oj&ATS zJ=RBOdGmiecb(y*QPc%)dCU9j@YervZaTa#rt2Jkhnh~73`j1-@Ey{9zW8m|ONYzo zH6}aIgHUY-m`Aab-0Q-I{Y8Z)OG#|gGc)LdCt9W z4IK}kzSJJmMScWhjR~LofqkjNS7W#?@ptId;axtmPjvVMOw`nH9bG#7()XSTI{YDuy20E1;2P`ju|JxR4qt*r zy2P&_H_Elj>NM|#**g4ZOxNMRV2ZBrQY_cu z3!4Y}^YQL4E}=_@KgIB!{oE!$)Zgdoa1Fi2gm-Egm`rj!yiM!CWULM!iqSgHAEHZ# zJKD@mhY!V!7{ik>Xcz0xm!a43@Ioxr;n%QOhnJvR*LnQ`-q&lx=Od%TwKd+o1#f{&eTUi#PIFyBWhXe{CIfc#Dm#gATup!Mes94jY)v)!}Uek@>N?MtBW+BHH{ZcCL1*~Dk>;#xylf|P&<);umx0Mto#VD$ zoudvPlJy+W;fpaxm-uxI`+d64@9yUOb@&7H8Wa8j%XPB5=Ky9H6JCKSI{eaJ=B>jG zbm(w;Z}ZgQ2QXJxc-?*M+dci9KW~l|j)$K{j}Cu=g*trOn1RWBUFPR7(QCtZ?r+cN z@SvQ}(`i2I0PCQOeD{IwDP7_52YJ5eJU@iYA3RrilX2#w!#iT7G2vZM(^9!|fQW!>=Q&>%7Y!ox9HR=cwog?|istREHlw@`*?IT*t$gW0?+Lg>GHq zqQhsRqQmE)Qy01apS*T2-?#I|$GOLK z_;57q@C-DJFYy5<_*}>HyyuDbgU;~`bm{PllfAzVuX&37uhTpMD|Gl_)OGj;)O3w6 z`ahq$x6k4-rt0v+$m?*X;JK&61!Q!1I)>;H&%^>9-tAOh2X**hOxJlHcbavM=KIjl z;k8cp+|c1ou}J6m2u$3^dh#q}b@(d`*Wt@2TSFc0M3b)ah%=mz4!4}?n(FZ8DCi8| zg!#J6|HM)q&Yop`boek-b)NT};-1wxo{3RooG(9kw);YdUqMRO`1Espt`4s=)w$^K z?=VhhxdV%IxQ;Fz-t9c|*ExOxP5b&e0si!S^K?AC`USo|=@gH_Xk)?)F+ztwz!2Tw zO^U9m&Ts`2b$E*lJxg`?!i!ul9ljn@beSK;QXPJ7n)T7)Pf*njUWp<5`FV9NU1H8U z`~@1uq%ZX>#5l*pt6%2XrNe7sm`?Mt$m;@MjH(XbH{H7H@X$ZIjxnBhz~KG;Iwrq! zxzBPue9aZsR+o9-8TO1ZIsObIb^c24e~q=%C4L%nba;*He2vrLG$!ltCo}DH-QX9m zwQPkl^*|pH&=G*N>o#M|>isQWgU))zZ z!#AQ^hxfk2=j!l9DC;t>H^+JDa2?BZc#}IlCv}Doyvz3r2O7^O-|cIL4tLCT@9A&> z8694VIXe95J@% S&w=@idJRaf{e^y=___nLf}Dx1!WyR>wbGfhi753F7wJjhksYG_jGsAMEGBIRBXI zqQj5Px8Gt6zliBEhD!^K)8V(CcD;0+KYY!-sT+LIo6b|``S>2|uPeOKJH8(244?a+ zXT>4*DqsAOXO}KpwXM z9ljRRb(vTD+1lw6pOmDMPF>*5R!Jpub*3qm%p8zP=ErzGZD1;C9hdI&lX`{Y;g3+) z4W6-PDj8x-iT`iyl)nqjdvasFRMMft^EXK)<8_t4#~dC0YSUCw(&6KVr;@3F|XkQc15d;VrgIB};X9do0%BgD_v`dCIn_B;&P3UVFP# zGFqqkmF-i>I9=oZze^>Pbc#2{RGr}`c1k7Fbd|3el}fsF__k~+DeLgPyE_+M;YFiU zNsq4aw`l0(_o?I_tc)?d^`6e$G){F|e!i4IRgw+>&0E*-98jt;NH4DB}#CrgiZt#yNE z{K?$Ln-7;yP9^O+{4U1p2Jdx>_t)W*3igr?moYoW^WB)P!}BpkhnHf4ZgBId=Apw| zW2p}BgvC0`-Do<(wdVIuOC^JJgU>uY*1~5^P9-;;VPEO+vMJV0hlibQALKT9*FV0J&L--k6^m4@(#DS?{t=rMW-(C%_!?KKaaV( z#?80ddpgD2V}6Y1>rsvI{5%%wI&Xbj%HIiX-S{~)<$Zt4AEV)TI8%=M#27vpgN@1a zZAj}fKab(M!E4{{H9ErwW27$d^~lC}ehnSE&a3^!8tN3Eh;cEVyOGy5UgZw!7GwA* zOo=i4G^Xn+e}Dx#yyqNiqQkGFQ`dReT<5Mc+=vB z$~@r|5WRUikh~7aiB(IGtP=pt3<6DU7b~tWmqwr%mry7 zXo0h~K$(a~S0x)vqoQVldipAkPr``XLbeZ5-!@P6m?``hP*Wtv+m_KnlobWi;c|8t$hy#zoyZgBw+<*^1LEDPM@f(;=WPiAW ztidyI-jmFAxCUo#WFE$SaK$F8+KuaQ;%3I@T;2;4q!bsP_!RR7?u2iX>39x~{Tb)N zt?((bM)rp%CODVu5AP*W+<+UOVeXZE;E`LnU)%~kTd6l(Xn&R(tL7Tu)z2~C;y&0& z8gSu*#D@#}i3d->qURY$xE&fKfD7$O+F!QAvKOcg+zl6Oqy2FWCdrfThHVz}^I#K3*<$bHls#}WSapd$YK3}45=cZma6 z2N$Vx$#z`0uCPdL#)Vr*8!lWirbq>FVT+|mHR8f3@#De-@#4ZohZdisapArRMJkR953&`hFfN=(Lbx4%hj`Wo6VAF1(u9apAjX6)6iYJabx+%JW{~`6P=AXOT4S zgAaxS}97}w-&_O)76D}ZS3;7vKc+!>hBQCs> z>}4Ne$iumC;j1&bcU<@uN#a6v7444;&lTr5!j7sU*3taD0sJ{xgA3=);@WWGhHJPs zT=)#};KJ958_&Qmd+|m5ULjobZSDaV_7E#B^j=H*u)pxJ*<3R&+)UEA@KutOW5Pw( zaW3Ag!A+zVPr!qHMXCX}!kMHA_rZ6_8ayYQQ>518L0Eb{{f;}~D`YdCfw#@2K4l;H z_v#|G6IV6dJFzb2-l1h4?aX%JDzaPlhp!MX`v|M%bMLtDUgE?JSm0-D;=(Mc#f92} zA~hQq?!1Ay5%=84JVVmBa39%<3%^`jr226?ypH5>AAFePaRW|YNFQq)2ks?faeWbE zd2x}l;TerK`99Z%2Vr>~!L^1WwRB05s+9fVvCC+4+zxMQEK=379WGqX+#>tK%YVQ% z0~cr(ukT-Zvs;xX8>lCk=I9KLl6*N^Am$+s4% zNw^apdmDX;+u@gg$hZ)PM>TPsxNtR@jte8C9FM`BxAW}5GcbAw!G-fz6{)qja2097 zgRnHfwc$>y#fp}sq z_jMohSOfP3FC+>#c|YyIJ_;T~w&TKMNdgyMPGYzRp7sE3#e0QG;=+Z0A`UzU-wkoy zxO$ixCIQ(FzxxRD9&W%R4B8#H!kbA7*Wq50#Z@!+LHGkWDhNwk=u=#4r4NY{S8dc6 zsl;_Sxt%t_op2{vfxA0+-jNWVg5FN<1&={*n0vr|(AGse;5vMQOj*kJ{osUd`Un@E zP81%5&k`RlJR?H=?Hco%8JgRr!RcEFvmp5$<0RWE&k3meE* zT!&-($THdut|a4d9X>-UapAHU$Hj#YkV&`!UmyWoSpF!-#f2558u!4v$!1*m;A31r zF5F0hcmn20VI$x7hChFt{$jiEwYGO-NIT6Pr);vWiG;Fa5stL!fT#m?!|@6$y!{8Pm&a#fFF=FuAZl+NCr?$R_OM;3-ljcq zp`D~~;l;$p{vOywDsbV7-PAcQG>9D+en6^l^-htRO4j1Saew8Sap3@|#f6r?agVqa zUO;x@!e5glE}WU84sqd9(uV8M{x0(ZE?i7Xb=Ds6cYkL*;KC2cUiMM%@!TY}Y!}Yn z!*$}qTS)~Tg!BHva}L+w7P1`|PR!G`xUiJ0$DMFFvE0hffWgQIv?tqzFOdN}1($zF z9kP!O)1(#`_U`2wfeW`17oLL0{EK_Ug(rMWKjFelh>m+;Gs)w^hg7lJjtd_rTk!-u zqM%q++{VvG!BY<|R+Dhy1!OGlhQ}8ct18?MZzdsJST?3uHQ>U_Nj2_)50dS;@Iy}O;YKomC*U#TxMudT z!;6U%_rP_;hYPjw#i|k)wh|W}gNIHiRx5BToJqFh!uQE$TsZz{+6NcjL)PNLC~3lD z@bAQZJAW1%uCsAHxbR70!-cM6iurR_{2V5nNpif`2X7;@*&c*%kTtmQx{0(AF1(vm z;|BbIgmE>A`z7iQyUCv(TdcBd7j7VFT)6AFV*cGBeohz`9AB(rxE0PMwY*n&xSd+U zg(s5=T=+aG#f5($lkgn;&E#Sgz%$T&VzEl$!VO=cU2x$q$$C5mr+&3qSynNhz=uwv zAK5P4KnCyxoa`u89`+Gl^)>nt7cL@pJa!6gSX!+7xEnSR4G+TCNh6+{TC9G1TCvjc z42(MIFWLVL#?hIy8}5XA$!6R)vsnFLa?t?dy z0bFR1ZMg7d62~*}BeGkzFDO=jyMcDbbFieAI{XpW27j`Maf%0FbsaT^3*RI@T=)U; z;HsW;k-{HyO!&hFu9fY=6P8e8xbUkai3=|zaa{P(QfdGf+Llo>xE-F?NZoKuVLvIw zg})?|@Dx1t2i!aEgwNhY-QvOtD>x=DJdLcug}t}XhPd!W;>T0)_FHKaT=+amho>eZhO}FiLi_UDy!h z9&uswJ+v<_>>_Qr@ENie7yjaY<^f!|i+J%IZ2l>={S)2`zw;pTFfObod)Y^a_phVY z*>1qC>lu@{@W_W~D_mGbe7NuhG96FBuROvS#Dzy0v=1(vMnbq7ZX$(u^8E*RbTjwM zcHwy>Bm2P87W#{Qoba+Xt_K%xBvbGNY;I@%$766s2iJ)UwJ>#p3x7nsco1IR#hi%? zmy#$he4MPu6Y%~Ba{?}08)au_N&(eN1j^5cx{Am7h~7ZVRIysw}0;=%`s9T#pO zR$RF332KSs2-lJ{Zoo7ChkIuq;kXUdEiOERG~#Z!ixl3?vj+CWIS$)}7d^>waN!js zi3_Vq9M|AqNR0Oii#Bp@T)3L(xX`nSd&Gs`dzvwb3zw1UxX>WwxG+v8;R*Qk&$u33 zcuIon!G$wO68FH@i2EMCu7L$V=e@YlM#kcHc=Z=PS z*fz!;E}TLx`NE_`M-m^ak~e3%^FJxD)=AOvep4Z6|$!3tuJyT=?hTGH>F-9I3{IhyIQ} z#jVg!Ecf$sNAQ&2a~*6K-ayj0FhG*HFiPTh4Ei$kFYgt;NpxIT_6Pa{7ryZ(a}+N8 z6LI0f!~e{>0=L2h3E@KPF8W2b!zPl#g^!XrE<89(+sZz01u1`kc@LJnMNQzs6UkUy zIGyZee-B(kwz6IL7>VJ+!~ViNfD2C~YvjGqOB!%t9r5A9b;N@k@QAk=vmw5&fph=L z{o}$MvExGL-?%qicsa@PUg2Vr#x?jyl4Kv@{qJ#)xNs8*;liu_$vDM@3yB}s;I#Lt z>7P;q@F|jGyYMxV!87oidFqyZJTOhB;2H4`sBLlKhg?7IggZz%ZvUuQt=!8zkL&Qd zf6)%O58nSV*Ml2ynJQ6hWgmEAL5b?eo$wyA8CM6FsE^53@xl@{pJZ?izC?0(3SMlQ^<*b5JZvKEj|qs?jz&5fS7w#Z&TzJKaCCb1(u!>k8Voe2C zkv(h|enfWSYD$SJB-7bPIE^@QVT9Q580;qjT)2zW;=*gc!Z~mstR`D=;R7U$3lIM) z{fk@SIHDeAJpeBy18f&=BHQo;+)B#XNBBOmd7xNywLyvBt$kxlk;ci$#CK>#U1YAb=i^fd8 zjc3^>2lo&!+l42cN_*nMbz}-|z&5fL7w#a9xbTXpCH%}C^#iL&3Ky;-{kZTWvL08b zQ9opCGxY&1l&sY;==by1{dCVMu|$|8f+l07U~Cfl1aF5;+fnJZiiDy11`LVcyZy2 z#Eqxmb`rydW6EexTzC^%f$Ok|?8b!~ND3F8au#D2cfvB_Xr+GOd@>dnzCre|PX-Q< zYPJiHKAZN$g{z4Z55l#?z=h9~CR}*lG{!9Mh81KdE?hAD62pZN62fD! zpIF+dAGnL`X1nm(^XLQI2dha1`v@N(4qSM6Ib#;L!f`~$g_n|QT)2r$#}jZX*^CR{ zCk8IO@qETCuE7S9$Az6_CoY_L0q@7{a0)5wpnl*r#D)uBBnnT#?L@WyB!~-7aWiIdCoCgbTsWU>!-a2hcoxAZfx4_$*nAr{F)xdfDfS67?0*hC5*wiQ+N% zD%p%@;33ngRon{CBHM5`tRdTR4Q?Sj@f7?A*^R3!IX>BgJK@!2FYbf4lfrJU84i%K zcn)4XgZ9Baa0w~Jb-0_9$v$xWH)#{x4lg0oWq-JYRLTCZoy^8#u%E2Qg*FfKEiU{P zsm6VK2vE%$ScnZ#HpzUxUe2fg>LjMx(3m4u>qIeJHtF0R9ir@HniB(ayN=2GWGr!ZD9>U-%UG@DrQ^H{e${ zGA?ij{BSd40dIJU^}y3yKW>9xN-!313;YAwgAc%x=cuJeSv$e`Nvt0{P!@hw{QGq#27ejhO>Ehqc;y8J z>L8gk4>CWPJU;cB*<|SRLw9Pv9@;p#iO+xL>)~?^f4+cY%g2b<=7#;RKkIm(KHsNa z@4w!`H-GB&&?yH$$FKFbD#ib1p5g3?qm;@>#t>d-_*{mEoNSZNNp2m=nU5$X^Q@h? zNr4)i%*}@yujNDL;Qb%^;1f;P6-M|ENhA_bw(K+iXA%}8G9N>Jar1iQN1{5!^qdN;egi_pL)$XM}A%|VZ>{BIpVeXa{ueWm!G^Idij5P{pt2HU(?>h z?N`D1rTstWHT`wK=L;*8?0OXMF+aG$13p%)R0oI3Re>z15<|(@R6&N{y~^>n{TYK7 z$gyrA$CELnfGEPg>L_v{VV{G^{`Y3^jOD0(3ENIBs!>Zxty;{= zq(;@LlT;cshm>*ePPQ*qr}5sev(G~Qt6~2JT1pMI;AOPZxwOPZL}Q;iK0AkZ%CY&gdm~!2 zmRI{*Q$8mxSj~R>`(gO_a_tu@{v6rRc{Pr?h(4GBWgq#th@&m%N~FJPInq^}eI93D z#_PJFeeN)?efVCb&+FA(vkf%%9PC9o)?hXK_wJ=seTjP+zK#Q{NX|d9j;dK17nuFh z!1dN~-*SKZ@BBZnT)ws&afc^!K98EfIi_>``R383cQsm5s$;(L(>DC|lK-}ipZ}tr z&!8&gzf*@yYJY|*IpBCz9BGc(x6(Ux>PE9-6#q`wB6DjU$C<7!b`nmp^g>&jpUASOw-QxPi^BPW_yLjQbbLtmP zU3&Ut`@%Vk7R;-uZgi_Sb_vVGBpZGnz;>2>Nv!lh1F-5@!2KU*DjcQ zMa}Z6#W&W-MbDe-y8g`T&zW<2&FQY1Gq0a~LHTK)zM|oqIBod84!n!W+0MyEEPSZfnF5aYwunEfRGnvUk8 zmL7YLtH;yh@6mfgJ+Yo-Po^i|W9@bHx_iC7T5q7&=#BTLdb7Q%&(`PctL*djHTDJj z!hMOpbYHGdE^3@AD`1^q*bSHAG5m(k9mKfujFC62&5mYwv$t7m4m2Ch@#a)>wpq2< zTAVGFExwk@$+cKo?X9j>PpiLGZwF{^x9ifg`N3tW+k?*i}Iy&8* z-cGGE&}nqWJ5!z6P8GI=o#D!`FWeXohQr}RI33P~EnW65SC^;D-=%kjx?)|)u1r_H z%i8Vec6WQbweCQ-(H-wjb!WR(#1?TzDkHu~V}M-q{ABp0#Jv#zLz{?wzPXe^qH zW}^A1wa3xp?(z0$J%JvhC*G6l$@ZvTT}b#o<1-A>+jR(W4$jxKZp7Z z`a0GZr@xbZDf&Fqm!;qHef$Hb^8HG_S!pq>hK=5L7)~m{ZB$YPUc*NvXhtK|5HS9| zrqfhM*2qyIs@c+PZMHSrsT60ki)yKC_E0gtWrkE6PJ5@5y7P4Us64$hNbSWs6I5TOGe`Yd!*(jr9rjRzT3DwFjc|-Q zOocO4qUy3zi_R`L)#&TesK;QJK}9CIQq*LwOLbcr^-k*2)9s@&_3j|G8S73^otf?& z^=XaRsZe*sLyc+?ohmgVG3qoG$xx{(YNb}4%s?Jy1C4qOMhz-95lvCExv1)~GAlT# zTThRV%GG;<)NZUNLG@;Oa@4Q2*G|p3sW%_Brc-AIHI|^hGSrr$uI$v5n|kt5OFDIA zP(umoCo?!_XcOdn+0>4ex^Yr79_mG-R)WlZG3LD#^^v1Ctki{*n($B$8nqCl4r0_m zivG_rlUQ5q%q8v?53`BZqBEZuEwTSnC2+m^1hpB2sKq#Sn5G8v%@*b}M~jOY&D-K< zP7AbznAPGfN#?a|OP-m{*6Ltxt8Dc$yEV23nBT&!ab~!5YnD0A(q?0pbG216&-vRL zndw4pVdlDITbkJ}-)3RHbF{md@x1MR=Da|Ai18n9PcrXi+w;tPwhjk#UuB1v*{`u9 z!2B2Ph%*DGJF?7ymQEY9po&c}zt;M!t8sH~4`=srevK>8xdxRu&ir1E!L`J=ngrLA z;)*g{Q;w@r?H0y~jnQCXJlGf!4#tFwQBldb@G>&|9YLNcMo*a9iBmmE>L*PFWvQV& zRb-)#Y*f-0G=HUz8~h^ni=Mw)EL5?LI(AUWE^4`wYW7mkek!_=nhvxCsff?-hv6RZ z@$AxA9q3&FW{g-@oEam@oRMbM$adLzk~vu`xVtNvH@vJAQr&6hi!3unzT3`h8ksW!%o-u)jW9DuoVg>(?2%^v$TEZEnL{khBDMooGoBIa89f?cRtYh$gqd05 z%q>Y~mo)QBmKi3`9AjaYu`$m$m}y+hHI>XZUgjG=GfpFOPJmhGzg0c*j8mE8%&$QY zd}hU&JCZy{(mYABElx(Qn{n%5O4I|=A0Ne*Mb?I znt5|xw3*LKd0NV|Ql6CZoHWdtN#@72`FvC@9*(cG`U!FtqchBND9)28$+IZU(q*7&mo=%NCp8~8+jqWhds5o^uSatbshgolxtW$kMRky$9;?!H3TFbMpvaw=z znX6?#Yu5l(6{ez+R8y8pvQR}1DyWj`X{2gG%r|l7nQV`R*~P)isFGEYpA}Jn)lit# zPrld2n#aX@$4eaqS=%I8)8u8nWt^b;fra&qgSAX0>lpt5>z9;~Hdijv2bQ6Ai*v-P z#Xn-rB7G4XTCq6HQSFzp%(EfOGr`I;VX(Gj#T#Ia65^?l;Hi)@p9{maChK3R6L~g- znR^}7QJ8t(!TcKTka_(CRZ6|M=^xKf56LVbbAUk)#m#j?ni(L+%3-i~WFC#_Mxmb;^t~L zWA>)3H>5{p#UXtvYmP>9^=hzom6dCXb!%2u9*Gl_hqjXOXERqG(#Nv)@S7{uAY(0T zu0N7yKj#>0(#v+nnygG^T`E1SGuC89D!rRD*CJV2iJJ4c%;N5$Ib7!N5bKcxXO$dl z0@W%NU}K%?V5RChV3yHXBMeuBK~2P1D-72~h807OHN$Xq49`TvHKJ1^DW1-D)(LS| zt5%-Je%7Z6)}=O9q<-dsIOE>RcsCg1Zk`rN#;l7v2s1KmjKvtE&dscyi#nN$V?3>G z^nlLTNwT`L)5aQK7bK)TQzw|;53|#X8f}+gcCyk^KH4Y745WCbd1wQJIW^Z}<1YQw zd7M?ah56IVI%D{1D_Lh7tY~uWHr5Y1tCwtto%OTMjFmEHDSB=6W3&ZETVS*WMq6OC z1x8z7v;{_6V6+8BTVS*WMq6OC1x8z7v;{_6V6+8BTVS*WMq6OC1x8z7v;{_6V6+8B RTVS*WMq6OC1-`Hr_+J4|)${-W diff --git a/3rdparty/SiftGPU/bin/SiftGPU.dll b/3rdparty/SiftGPU/bin/SiftGPU.dll deleted file mode 100644 index 2f2fdf64ca534ace47ab1b713360d30d20b75183..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1807872 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 gbdXy>*^?qNQfHYkDsh6ihhpBQxUOgE}&Z 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�>H5fz1WE&DeV!SPp> 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ℑ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 g&#zD>!-P+fIns6iQPO>-_oYYqLV z-y3De;<&7pjb8Beh%&$mt>p};O?bLX&oytW5{*uX6+IDdj zx!JL^I#0@;DP?oM!p>?`i?XrGwd)@4@5-HYMeM2k(v?VgJH`k0?Ge(N(((!qG|bN{ zCRQ6@ni8~P{4BvkSKSF#VlZ+Cpl!Er-K1`{%?agy9&&%qg@qt`{wenNfuglfdNW!| zU8PEeS$?mfaZFi?pDOpe9Pno5*?CUZQQ4;#Dcj=c*J=N+=LL!~^R)VZVSyGXD$7H4 zwT8zs)a!FL0APuJHopN==7t3|JjxMAB|W9X75p#Ze?dvpXAKM12AhspeP!+s_}T(- z!0^lqa%$NOCXGZkTPLA7Q&9f}BgV{*Shd-A5jH-wZ}wenTyOT3YIRTXF|)5stFzR@ zHO4fvZ;^4S>V(<1*hn|~?lZMnXf^gz^e~uuIF=Q3VVM|8 zeP2~;V(%@%iB%nhDBcKb0(&w|@Muo3>CPH~V6YAoF}YR<`?emH28V5;e&pFI=@k`6 zY^7>R+K)ZM;a>_T^P>qqky=wcC7e}eMOAjx-~$}Zk8pCn7WR@86}fxbLgTGzw;TJX z-EF)&ZMm^$T7~hFsv`Y%A1_J$Gfz~QKIu*#B@!u#}2B5+h_?bDSc&TAuN@&9%P2SnE2Hl z&tPKAlXq)BoXB3T)pemKl>TUS?;|ktv{0*ilQiwo%gE>bwg<1~@9oA!{@$h4k0X^f zr!XURJJ3pqQ`$K}t1H^gOJL(dCP`^9&G=-k{z8tW08~lzJRTqtO3P0U-tmN&v<{Xu z6yYl%o*%wYP@3^>Hx&y`a0>zLN7l2Oct%n5N1kN&~g}<`Qk)?vNIIKQ=dj z^X>fi80qz@`4l6sOt|urhG|$)hw60qK~Z!p$DHCBRQ(Q%JL|ZtSgr2IV7YvhFTO~U z^F;yvqg<#1?oWeykb~qEZ8gS{rh<~GJ*-MWcKP-pY3VP#1>JbPeaU`^mZS&jvkB@a z121WqqI%?O>^I>yE)tdvV$S|d9O5VaQpNn;$+wl z_J5Ud@ZlAww$VQ|9Ci(E&09lP(et#5O`KY)(QDw%0?J7~-_H!VKND9^l|p2pUaE-pXsVEcrq7~ZKu1{i zOSHPbIm7^y&s|O$_DcVB(y&-chx}0P0fQK=kOMZCv^;;CRv+P8GBIjN2U1^Whbk6Q zz5|=nGOOGc-XZz|FIISM^6P5U0KZ$iwyVtD8K{|-_wBH^G`=<1up+MlemGLzg`DlP zpIg93$z9-#Myp=~st5kLpxk|=HPq*eqI_}jJZ*CLPHaMrMS-TwyqaSRjPH;boElx` z2{vty4GA^96l{uyc*oRs-K%sRH*}i=*30H0xmN8CrN;IiE_c5?!#mg0MX{xCQKW^U zM09@H>{Ml%+V>uI^1t>`Y&`V7IoM?Lmuw12EBz&ek(jnXtB19V7ao|pMXNje3(Sch zQ!6h&JUagqnF%K>xe$RD8AQ{5ner(D+C^r(Abfq$O$Y(yd2Nb40ech&Va0Up6N-!J zSd?D{NQg=qGeee;C6FnIp)ePg=e{#aG8(+9Wb zj94>2PL&}C{M{?vfz*S6jmv39#VOR*4Pc*xOPJq&hI%i&7J9?JykJd0StYBUU{H!- z9epXKFOu(6QDEwa@9==NEtDUV8N6J`s9TJhX^H*XzVqKurm9})zEH8cF8TpztBfFj zmxV`~@nI{Vz**X(uaRTMec^mE9?y`)mmPZ2eV~6-ogSeq1

K>?1Fa^$AAg0KQ6PQ>A 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%0Ag55rhhu0&yWR%#|c%4k83;eWlWRrd*Xe8q+m`5 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 diff --git a/3rdparty/SiftGPU/bin/SiftGPU64.dll b/3rdparty/SiftGPU/bin/SiftGPU64.dll deleted file mode 100644 index 06a4cc6086b6136531cf7b58d527ffe67c563003..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 433664 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

hB5CZ>CPhg<*p? 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 z

nYDEL&(*8UTjt?UdvTdtfnc0qKyI#a?v%E@9PFJ^OLPMOG&P*nR{^zQZbO7C2WVTFpq&?9{LcpOvJJi2_l5pf%0R!k6uy%oQZ7z$fj2fOdy ziLIhJxjC_!HB1=cN3*9h4tVG2jYkr%wOr!0mc=Xh9RSq#hJ-ws1$DglHOX=$7ROGTv}EOvJxUwJBB)B&@k>kP(sy5?mot zdnn0pt1BNXdnig}gj7-ciknE4h`&C>Z)fy0E>7 zAxmBIr9e?KcJS=C9#r+)(_RFQ=Xdn^_42v%iGY%RIo}QDJAY7jQUZMrPLuKXPfED+ z3_bQ^@b8O`y}OJ(O~$@`Kg-2$R%hGv!Wes5=CV|IIPB8AyZBs+?It&WZ{b~;Q!mT^ zApaYQ7o+Qcz9g=Gg59}jBCW49QAHCG@XFYwih$WWNy5}X4za3s-<4Ul+!~zerGXY{ z9=-ttkojcmi`|5yF(96wlC9_RbNMqQPu+C8UrczcldLOx>zh5i;VTSH=VH9b&eveU zBFMyVBwork`%{mc3;G-jpUoXMBI$It(74j;*6sX&VsRsU33keI zQ1~A#I<^m#n2)->^+R%kglEp<1tY*OiG=>;-f@t5siI;CshdgJ#M=scG8Ix+4egTN zV0Lku`cBH*qa)3!g?3n*D*QE{9pIxd)z#bmA|FUuYolw)W;2}&mZs%XYE7%E6P-P^Wv#G(!6E>NV57^_?KU@dfEow<>8F5N7ex3WU6G5irta4ZsWOa)e(gbzABcy|l;yRuchDoW~bsfR#A z!P*x4mqS3H`k<7P{|L(@yN|?iI$wR#JMI(8Nv0~N5M9;qiC@*wIwua@W9tP;cY3a9 z_Wkr;g`OCiYENBDbFG$5oAU*{Dgk0MC3JyYI(vsSXHJCuu+-8g@sz^bTiP4jU{&-5 zO(87BV;`Y(FI`#{laRvJ6(g-lyY+GgRW_oEV+c(FCLCy9wDe5sB1i-OZ3Y zS@+Lj@^hCz$X~C%lT{89YNOM^y^OK%S>>aw@nvjQ4{#0d&*7#ap;5J4XN9Nc7v7Ry znNm10P2iNoeQD}+7%nAtBBXOIls8K#d~?pmL*chK{%nMsD%U%Jn*iVxmMiE9y!3tL z>bxo8d5e6Ow^Q2^W)RcfYw)g7Q-(+SAY$`+~vDS7>01MAC%(NAD!J zFXS^-j##_oivR2>d&# zr-THaI#gmESENtv%y074A`x{F!FDAd2qgX16{)$`Warj;lOVeTvW&3}#@LlcQiI=8 z>kZc4MzyGcfO3wtR9CoLs})~Y$~yIb7#mPigUf$Nh`drC0<(J;2&Ef z!6vPlP8|#;g~)F+lAk`YlnqH{Zbg&6_^6rrdm1h`ZH#>K+e#+g#2re4zmy2a_a~GGj!CK2% zebBL5&JvtjmQdNZoeyT|4`_!EpadCw5Kebk;<<{uv%lMQC_I1p4q6Xq6!qYaQvLW- zI*&EgblXZ9r#>^R$JKHL)eYDJ&GhAeQ#emL?wW_(CV>;D=KqWHzu|hYRA1rv-mZC_ z$q9sR_#4NC&yynOgL{Pp`a>6C+dL>Uc+p~N5JLk4&TuO^_KjDm|2LYk9&^#)?=Mza2@vaIXUTuZJ? zul+2!@KMFGPPs0We68bjzrnlwbLxTj@`n7xAxg13u5Pr-id6E8=$G6>UjLGy`$)ypA$T_&tK5uWB3AlBoFe^UmMO9Re_K^*|);mUSVqRr)VqwggPBTHgZEn7p#UHMn=^H> z23UI&|AK1i2s5SIYwp|OOch&=#a6wZrysk;&y!2lEu_#8H_Pzb{@^P1Fwox(`p->= zRp+3!gINmKI?Kkb7tP#vIOEF1ucE?m#>@PjADxqioriD(iJbF|8LPm`gy=oTV~Cj@ z+XFoDaZRVbI7~P7K`6gB{!WvNge#IMvCY1_} z8k$;o6C_FzwqRXaq&;u&qHHKq9nDA?kX_;WOY4+qgo~Q(_b)`@;LyiGYDkj03h#vF zwVG915*`_rcuJua}a*pnGCm{jJeanBg|)*>?;l7l8C|*} zNQ;c6>vEZD`-QK-hbVTW@Fp>0YWP}ExlB$< zMxB4QHlM1Bg`4|OWbI+a-qyw7?24iCk&XcVNJqF%KzBstNlxF9*KsiPV)RSSYQtSucl?p9rPe{HMZ$20}*!$MY z{=p6vc=0pm}=wd1lH?`Ad5lP_{*b;23^BVp<-(KT4m$v%6vT7zPEvEHs= zzmneozme6r>}b7C*4yXf`4Cx+PpMRYD9}H_K9O|2F?p>(|5W>9;zdak<0(owSve?MX!_WbGHP%LWORv5fpl zDhfll{GKvVlyKGUO%PE&PY%2}D`0%HJPSZ7d8{j)l_~=D)nX_$vZ3;yuD-ZHP#3s? zVBaTKHt{z{{q1M3!;A&R0t%CrsuH$=4X(OPRHo-MzP=T%(RK7^wVx#xbCJnBjA0x#r9?{WRa4DQlT>51KQx_0s}#rZ|m}_NY0tpMF|o&h+S~ zKbkXr`f0H_)6h?UHD?C&(^7M0nSOfFoOy$OT4v51ub*BwXHL*hHRjCe`e~Ipvsynj zm^0_-r*-Dcx%z2?IdhSIddHmkNBy+boVi#(eQ3`7tA5&H&aBZ-pPMsR=_lKq*`S|x znKRevr`_hv9s219bLQvzsl}XW>!*X}%w0UWX8n!7{-n(c!g7Zj5N^dk{QuzopM%?& z@wISGen`r!Q($rZNe7XGLt+|Tv%Z0?m1{y^DOohKpJjIwHT?AXF=kU)}F; ztA^%Q+hbL8V8J7->Rk3Od-623XGM>?%c#@t{IM`6!M8LcaORP@RYl_ASG~!t_|;E4 z$v%g+@pZ+q`U>CD+uamkF)m8)0+!2~t@!m&&o16&!ac#bMR?C(O^$(@poY{o1>vmX z3x|skkfT)DD49K=Nyt9w(};HEJCQSNYz~I&u0l2>%dWSv2y(Ul4ScI5MQ5l zZK^f66>|y-z?>Faz7x~E*6vYfS(%oL5!ceC!E-tpL&nEjl(bJY$H6BSIdcnlTv4!Kxllaa8mW*`z3vucWmD84=hd)V;2uX**5^i&Zq}DZA z#km8tpmb$aPHN4V54IpVfgf8^OL(=U4cxe)_wsvU zSOwosRPy`ULy5INCx)B;mva0F*nOOBw=tSIX%NkQe9g^|@F0%zp?JtdyQx zvC^@2n@ZL!r+|^-&|GG?N@}gIab0f6=)^_E0!ifURmtv#shv$Njn}DK(r*23fxy}d zKFW_5kZ=6SyRpS>30$X~>VCKCWS_gPs&`n4<-Jzz#-Eb(BZY!esOkfR`Up>X6b%Dc znca|0a1>ZsIG_l>t`A(C!jxFEXJ>WnQpCbi_sEa~CFvfOl3;fc*{nE}`K;@+IidO7 z*XK;TAdvg*xcO{bKC8}pS8Zy;IPmz25atGo7znBA49ksx;Z-7J@VU!!rZ#%RbsK-) zc*y70$HTcdv+mU`W5Oql7xjl6d2_sDlwd0geK_seAcJHqMTV6(34qE(ze(v+^f)Ar&{-_+5Ekpg?n4i!aOw#IpgMg-S5am zyv;IS5$YnGZMwHctoK z);uR8)YIi%Mpcl~#(>-ztY@oLwq`5Hj!ahTuQfB#D6EEJMcS)^1BkSMiC}cTFHN=X zmg#oio#VQ1k>vN)SN8^TzYpXd2;?@8oA0-hPxOb*hS+1o8*PiK zG=)Qgb6wmz&xmL=B=k2bkoI_-5F{)TfsoXlRQu6!_{`rJ#i7b%KWdvm;Z2#9r;9Mf zDn*@hzjPuXVyrsyXw4g-={4+{ucZd$d8#>gB!vi+>`EVHuI`?}x`*5Vrk;Non2)Mmx}PRk)2 zjMeRe&B+O&z)^{kyZKW;cVF!LG}6h-bSHDL&oAZEk?xOWNg?2Zjrp?1E`@!;@?tk( zXi|2)4nK1V3D_I#R^}LugtZYP1H6*&Ix!qB9Gvc|-cMJ!IV4#0`X+GFReh3TWbfN8 zBSP`pyCXw@Uz`E7#@>~_EJsT2`%-l55?9&ul1v^@V4}+*Opjcv|68B-mQ8!w$4EB zf?V!oW`_-6b+J|FwcbNk$j#qU0!;sa$Qrw{xe+Ca|M!OL;9zsyccoXorsp77RJT#M z(f-!p^iUc!>z&P?hSw~+a>R%c`@XhT)Y>N{t*G6Ryl}>+Vb(LxEk~QhMzQs?VZq2> z4SqbKP^s{(7TT*E7a04Qf`@XahJ@I44=Wn93Mdj(?i4Z)TyS737>R8mUZ zB$iQ=eeH)LKw+`5CP$R|tPvx%?(zvbFf`QrWux}9q`3VQ{17<#$6}qCJ>9U9dm+F5 zM2VbP@tZV$NQt4Lw9mkgb$HQ%F*3Lkq8ywKkvu`N)EC92G^an20?sE3`6OJ&X~!=n z2povyFjU^#>78rt-DE5Hc%iF>v*kBhpLKF`npXv|^}a4;eCCa%KJ(9u6$tl55Gmfi z8FV&W0|+VCVAq_c(3xBHYASN0`1Ffk#IX6Fy;|ebKmD=AE(57A9(goPLC;{(q+17% zR$N*-Fxp~`F2e*&(K@|2N=Evu+0r$^i_f-7)i;(FyT2@U1-|rIl_Py_WUYbkA?$p< zihuQo!FUQVZXxE()Co`=eIMP!(vg7G^B4ZHZ;N#S|JHbylLjeTC2H32<#{SdD>EH# ztZZyPTcCTChOSw>!SZG@t6V@`vH_O9ZHq|_#nygB<{wyp2t=+IA*V1@<70{z9eGM~ zxZdILRIQCqJtE-5D7Z?@7P(({o3TuY3;$ih?Y(z)GeX_&B*cxq?H`E=*yDTCjoeP$ z>X&G6F)%Xh+D!Y`m&);Rx6l}atAA4s>sT&^@CSDslxf@s)t`@fbif*$8p!>`Uwe?7 zpyj5$cTQk_L&p%CrW#$Go!9A6|=OH<<>eJud@Dm+@qdaRz|Vw5$5pFkgloG9soT%t>n- zZLCCKT}s3*y9M?a$yX}3y85kk{@gW&3^h7Meyhg0?pP*jJ%NVoyroQ3Ae2^USYy)t zx$hgb2NM16ZLT0fA#i)8vC$6Xav}T5iK6RDGLp8E%htLLm1U+xzN@01T~iT!eTd+{3u2*>HNOVFFX8cJCA4G_;CpV z-W10@A-ytztDarKp!75MQ$a2!@=v^hT4}G^qofh(g22fN5Jt*r3DsZjhH>+S|2tOP z9P;ov@CR!o;$HToW*fW|7lH(%ll1;dI>>z{Yh2a8CrucJ$oUFXIGZ()OlIZx&6Ckt zs?UfP=+FIG^|W3MEYy3JRL9L}T+t_>S&1{gkwiEG2RcUXPeyGxG2paYlDk7nJw~Yj z_%mr}2!=SojVhVa*?d2tzs1yc5DvgBNijSFKpR$!ClYlors>e@9~6ijb=5D1 zP_5q8d0pR!8%p{03kcCzJthml+#t|UlhYe{0;e+bHI-z(wpDV>*LKK{Wi5@C`+?aa zT_u>WEz_lHRH>l_x~h$;$m9acQWB1vL{6qCe-`_Y2vJ|~P{de-UC}3hA zsr&6jvld6=WCX)f^H+0s3=IvXp`nIh>W-S0?w6(dW$If;O-m2S+CgVcu9!84Vxd2@ zjDP5ghUS>KMVadOT(9mA5@JrmeEqXP|19Fi913rmk}hC0xyD8tiJIE_)6}Mtrn-mJ zfK$ExT@izUAP~H$iAM+H38lRDyj0rBzLu}#>w#Mk!8X!1RjgH2?95|dm-)=(EDc03 zd%!+WDExPvS1bFO&q^1qlxM$9POuuVluO$jHr4TT^MU45_HA>XzuNp+b5HZxIh6I8 zrhMYHR&Ne(K47JMUx(o@2QN%bB-`CGE!n+wYLBq0WMlXZfy>4N&0DODKC~eu%_Ej5 z=;r#OeH5jb0H5PT(Qg0|)=B9pqj-p4U%{d;{t4sYs#dDu(7gg&ue)Gcs{3&{u}{6n z2+flR_`Vf{#4KdhV}KrT1;hxt#(h965~Ocp!Ohl>;3}c8liw9+;DfU%ve!Oi0OuC* zcpB$AYxkXJ=+0Xix6tgy@Wul&zBlBs>NfvtxFMHsgZ&DcERoJkQc6k0;lRKM{awbQG{w+ScS~gtJtPcS-uxpu z36<9di*|fN?UmO%DHS6Dab)C02oVCIXJqh^L3-W2v(@++;5zpd__$7nD}b+5qm_=e zOgTwaou$x}K8|5Rquuoz#sUrQ97ze6mRs`{Nv)8*BuDH(-^H9`x+Dizm2!sOdCucP zN}}>ld*(qljZmK#{=wMegqrNXcq2qbo0c~(xFa=2b2`NDTcjTF`ww5MRpFqt=$8(o zSN{UPSMU12;`gzu{$2boqKzZr_hXDk;dj;2w)maGr~fbUd&nj2;_WEjN_5-b-oc9jUgU{-sE6aW_p6Y*Lh?$%;J|) zDV~Lh(8OIU&+f}rq!9TAyYyYaZrLJ7&Eu}9iYp|(<=pHBKCo+^kcg8$ME)Emx*Iql zPE0kz4Sra3_?dnF!m4bfDZU?~AH2l}5-d|lfmWVcjbXy@Melv~TVA6MlL|%%o9}hK z>>a60EXKK}ky>JnfX4Qy$j+GqX%~AfkMy<-9UljrOr@xrj+It|tGLC1R5Ky}9#y(j z6nqMFG55%DMW(n%zSr74^4oewZWlh2n81bCN6D`D*d_&wj_#Kh>ROHg<#a%zZK|Rh zt3yTL--c&ril9F$U{U1?F2RG<=I6VT+*;fm;hbykJkAfljG*5WuxjkWAJ|qQXk%7R z93b0ko38#AHT^!YHlBX>$HP0) zwEB(lo0`8%qd=6ud!gH5{GAWvQ~DX*aBJ&VLja?eCJF*H0RihfwJsL}s>1tayRFRd z70!??vofu4`5*ap&AJTX8}g1f5fN>0-RI@a=dN+xM{taU!lI3DBqvl1FDzOwzX7)P ziv&2KZZBaeWbZ&K%sND{XJ>q(9w~4Z;hyX|onyUgA7txC#;)(t+&)9|YIikpR5#xg z{e$Q(7OCI4*_yZ6B@&8a^BMJK{#`n2^?8!^g9kcIISR$bO$SKX>MeX+_|1xJZ=n)< zPwnZqj`Oo-AE+Uj2YbdEBwMB`RcyT}g^Nv9)X#CqD$kNTtBHxfCKW+q;hUl(tSnQa zje0Jib3`*=;ei0+#AGu4&}|6;$P>DfZ5C+JD>^4cuVvIkK!vF2wX6VX47aoS&Bvv7 zY0n>gGfTL_%2EHar4{IJI%;Wem)0%qy&&3>RPVPsi@MADy>Na46Xg{IsVuP^O;;>0 zs{LpwmaCUOR9`d+cIAoTfuK%2KdNOvt|f0DCWvW4>T@2~CQIqq`OptkwkpVqK z;HOM-!)q%)VTa(x@IgO|K&jnI^FN^4{=ox@RlP!k!tSk8j#09I3|K`i!bzOk!Wc*@Fn5P>dO~%(oZg+PU&8 zCu5&`rztc8MyP*e*s1QMf z6T89X`J@o3^Ly+@%_NB>+dJjA5fk%SNr zQTy#Od+*DvplLs$FpH}vE-cjizIx8^Lfuij5guwIhIWz7P05<+R6 z-QP{Ql^IU8hUK6%CL;Y$QvzDvEk^J4$V-LLl}Q8|IMrv45(6?}*k0#JejiLEWwre` zi+zFDQgAAN(S_>48_t+Y$LyW2I}`63 zwU2o}<{35b$9}-PUyS=E?W=h|N7yz+r|i%8>X&5Mjp8B3MxS6;9FL8Ya%A{gU?Wx? zW%eaJ3QIL!Y#7UhubQOJ>-qpM=U}9Ht70C1aR-DfeL#r2IY(@`iAB>F79Qp@M$c}3 zNzKL0FUeeF1dB$ihcx>b85Xos2)@-vNIu9Zj*RX=(JvKug7Sf6K#fYr}>l)ri}!dLme~;6)kH3Be*uJscCxXxa+k zFMLgpt3i)zt{N8>nH%V)<3IEck1`kHKP1T!i_9fVcNniZH6%Tr7?uAgo0q2OHGBg8 z>O)1aXu7fq^xCy-9Ew<5_b#G@H#90m$n^71D}4D3qaM3Ej2SornfAS>p2(j^zWrmP zylSpOfl2!rV{b>QCb0zm5%_6VBUD~0EZE;W%ib&I4U@Cwt}1LBTp^pr1GzsULiP!9 z`~pP9|J*)Bx(8tun6I}VcsKlC2RttdczY2CzaVaUGLLoLbd~Gs7AE-(!XrEN`_HtbPy3{IEN+dZrTV z$^HUm6U+BR>69=Hc2Axhn?JZqvCx=HEKRCTR8?PO52R9t7bVa6RKq?qK`MWk+9J!; ziZZQ_ujW1%(cd{Sskw2)( zGD>u-d$-W)$fBZ+hOP>-0mhxBJI9)kH5u7usb5~4^WrDs){Hs=0DWUOCLTLnzuuK6@ z#WJOQF>WIZATORdfhegjdBskn;rY1d6q`3@2`{R6n}?Wu_@?!aNr{1i(dcQ6U9HVY z4-21CaFiUwTCgmr_gX(-S~8n|_wnyx)VX(gtq;x7(|B9rEqqLN2iLp{yYTlauJ%eV zs{SNVd8njJ<3ZWC-1uk8Hqn>V;H2IZzOS=z*b)Ar1g^oWNq?wkl3rO#hM=sVhK2%v zJUS4%s6m*%VF#HNj@wx{-7Xi`TFk9+thKsjFHOd$@{V^1dxt=zWVZe1fih<>^(Akq zd&+Kn4vRi+2bo=5`0Gs9ye%oR7N$IGSflU5wx_u8ww|te$CF5Z8AB(zytK2cdN;TR zU*@V7H@#vmhpC3MqqBq4!wo+7ZdVAExxA*ytL+Na@G9Q0Z!3F*k@bigif=>Re4F83 z=?aR@QNQeV&7RInAavtxf!r^ZdeptW@@7`2k=rQBJ_Ec~|DlgPU1?ce;^T_&3@y)h!YWQP3vmZ^HT|3FXyB)an}*I9fgtaOm+06i}Ew(>&2QVxwvQ zZKJ_9jbeXsn*_HBl{{hJ^*$XEjg~q3K7m->{}^JUn2G-iVs$(IbBO%_U-4fxI;>5j z@enKC7K0dgRr$ZbtJ43r!;7~6=TIW><$njI+%}EIL+K=JI3jq3>Bj$N`3?MUdwloD z|2ddGV3Yl?kSV!Mqw!#B*%AX2CNIy4&!c(cb8zEtDZ>=B+d?_rkj1&XenO}et}7<$ zm1}MjF9EA^Nx=HeuZw4W?y7zOI)(+s%YJKCi=uDmLaB>02btMRF?O*=+yyDOk6OiH zz0-|Z*3>lY6C|n>b~K5Yuq)zp06z|~Oq;k@hk_~m?CH+|i|r@8N_L@e@m&~M z5fpxj>_=_`r_ad*{0gsRNAtSXZZ@TyEn{afsceLU zWw5;b#d*0fZY$+J)|WXqw94GR^^ zREUw4eFLs@uq0km9r(26utBKI_IJ~M@rl3e4`?oY?X;+8YUgac))$knFRFsoj;!6*qPJFw1Zfg z!H8g@v>nIjRO=zdDl94OZH(O`Gh04u)6~-pE2QuySeuT?jhrW~P8dHOWW&6S>&|Pj zkXlo@i;x|)I}rIdc;5gtY`JeA9`dakS(f5is5==BxR;(8$oDg=BwtSc3r&E6BlW@9s z`3_#eM>aU(fuvFTQB~S!PRTMtp`|p8ZD2ZnFIGr+F>k2&X9Dze+q{DV%B>rUUDs}E z?zL|luLK~I&5#3NMiHkc9>#32LW}rj*6OLpE~Dv$_)=GIkfJHwDe5aMdJm4IB8Av2 z&B@Gl3o${(fl4H#CEO=3*V~C4tHknXAk}g%>S#Ro_oh|JYSV z=-lZtUz~}ZHJ*kOk^T98(M3Hbv*!<`eQ&=Qdlw!p4}@tI{$#Kf=#xGeD{m(N%A;!% znMhDZT>1-(J^{yFvu4v%nh^^}jCrJ)DY{yff-tC535fqy-5yoerXLjNRYEcb6~R9@*Q&!yEHsu%z7DkC>>44-C zf^?2;@DoVMHCGtAOGBfMN%n{Q1VY`3VN`ACqWc1&WXXY-oGw4{H4kiwRxHT_)1}3p zn8Nd%sT%C9(z7Hz+iNA619MbzKb4#($>&l$*&JA)(l27O^I#ez?%tITl@O!tEc_L` zxiv{Gv39W}nt9MlOE-r)gW{uff4GL?RolekbOmd#ICFse+@-F$->}GciUS&ly|Y^o zRjSpSIY2N%d!9tVNVHGR+}$c3A~MnI;jma_$oW>mGo64~z|+gPl3lNKY92byS9o&8 zxYntH>SD98hbXjr2*L|n1K7O`&H+0gU7uKy*Xk>8Vn?6h5->1LhUOLdq0kc$+1;Bl z-6SN+`lDJ0h^mwreZ4*L4cW^S0uQ!s=#w_sn$QOc?2kz1e{k=cnqk%DGS@YJ#OjPHSSuHJ&xI z(w_b;Yly{i*e2&gphuXG8{)UQOP-c(?l<)i{E>VY8Ib5O*3bhHrFpGY3-{gK+iHlB ziQNS7!GP@QJyvm6Qgqu@1j(LpzQAR4mKTq8|5&0%%@R7rdY^-%)!l#+<-u2+hX%G# z!zkwGYQkfvV@+pLhaQwvLdIN8%%PD@%-*HR%uQFg-@=K(7zbj1?$o^Hm4CQ5n&`b=OV zMcs}>Sngr6S3RjWYuRmqTKs0sjy-gl#Qa^{YTMQ_1NQ3?(ePuT3E6S?%cjbPt>b0u zOCymQC^ZYs{|Tna$Vb#rjDN#-uzNd}Wzq6NEWuBF5`QaY=PkRY)$M5B(JYPfS~Uyz z?I&%x-i_?{au(+p42kVP7KUF`yKT|(hpR1qj zFVs)Ym+GhASNs^E5iI2<#`zlsd}n_*R0fU%n^Qw;yCjzKd*m>_9-^${yY9l= z5+MWp3^$x@RvFD64t@g3jv_l+9&>EDj)mzB)0G_afa{j zis8G8MI=5BSiZj_o_>|ftnE@kwzFS4-^;g1Wv6}z4l8Y^*S)>!Ge|c&G24C`X{Y1i z{vvZKqR~+@himQrU&rL53w2h~=sA*eEIF)Zplpra;15-G-EFuJRGbiznoe-)X|lg2 z8kZ_)bt15%#4g9g794>f`?9lydgBsdq^X$XSnCf@ZL$0&5ksx)J=-+K*NK5S(n`D)8f2{NuC$d~^=A65=q6aq4viZp_ z*=YCso<>6FK5lT=EL@h@GW!Bnm^$kLUwI)U#WfH4BCI)v?VlvF>bHjG$SMzKEUro9 z{Ie4yJb1x{jB3V#1dpsnVW^hLBlHPFBdC+L)hwQF?UO_3JTcMs;W)XEh*;lxt%E*m z2XNfsC2q^O`R0_xPy_xIPscdaRYH8px7mC-T%s&*%SigHqpd9xrR~sLi3v&Hdy@_o zC+%`$U89<xgjeCO%np1r2Umki~j?zY~RFkG`bqh@e zCa$?6Klpgu8L@Je8l#WeMNX~&@ zxQz)$5WLO_%Vo?gI7p#7eF;^f!c-mKuAB{4z8vp-G``sVcOjQkj@kJxk^zp$7jqo4 zpPa^_man3eA8~;#<|6@UL~v?W!qkC&>&NJNA5>uV>{9zFDp%sB-K#d1P}+P(C zF?n<7KEXBn$44M6^YtpI7udzT&o9Bk_b8EAhQ&(a6oR1q6Tca79quRabF)w2J4Y@5hS;2vq|F@jTA%oXM`d3|anSk>!g(Unvde zJLhUl{Kf0VdQz`_#V#6xeRzM1uZ-pO7`fi){@TZ|?q9}?_;cUU`zv(cUf53%4a{}o zdMWoTgvqcTnZkN(TOjfShMho&9qlD<)pI)Sst%ehu4}nGCH`!Rl>aFhS|VMvSk@w1 z<7C=x^H(r`_1Y7!Mb4z%?x9SuXke|Hjbh?nH7CQw)Uq#|bF<~T5Wf{JR9^G0c?3pK zy1b13CLuKVy3f)kQ6G4)wMTliFf3n(5lA+f@iLlsu?rj=(`5!6!bQ4d>^__(1y zEOySEIj@5dFe=u-Okd$e%cfTP34)&nFFWW5;bpNPzq+1>k#ZCv$(4So;(Uo%esz<_VGa+qD>*7vycAi^`n=1QaPbC*>C>7gvp<#JqD zP!h_n7Li*-lg2<*7?cyB4IH!tN{F!Z13kFE)n6N0t1=+fziny8Jx`8y1c|{ z61N;fpT{Uv;I+~^38_UGqTMyb|2g@7!bl~XzWX`ydQ$o2_~1$7H%&Yd@o>R8Cf(eiP!NkUN? zG9D7@?S1ECc?8#`z4EP6@##qoh!9PevGe6^lFqiKn=3a+)F z`p?wrZ}pihQT~l!k@{4*I5T2Id%AU+EK0&#o;*%j=z zb0S{0OS>Y}U>K(s-m7iZ`!D6*Miud>3hRGDBb&vG#1~bsy{B-jq&?BFVeoAGs8jhZ ztLU&q>kHn^E{tUqRbE}sWH)?Y>>AU^pR4O6oQC<5P_XF#$;u{fq&h;!+0*@hw4DolRK@l96E+J05^qpeqoT$d+h|ath)s;qEZM-WY$QI= zAPCU_Mg@(qK~TWNO(g4O6<<}WR;t$0YSq?9L8~Ub5`97nCOU$yv+#XV(WP- zO>+7eLz1q?{TuCDMjkIFeCsxb?3~`)V+>h!y`R79XJQ>acahv+7My!uQXrrF9dKKz z0sub2-a(Po1#Ejpsi5R%HGI?l4f}1($Qtx#S6fqG0XC%yM06r3 z^*ojG{~xEBKf7y>q15;vaQY;g@}Gy(-2EHwj?+v;TQQs>VqYXP4u=GLwHOU~a8YWp zsEPakVa{$s$N%%X>EY(|{{g>Kes;ram+jYZcl<7SDFMHF&ca1N0(!vYZ@aC2>?8lH z)&EFT5C1=}{=F^xbx2l!yK+kASlM_nv*Cc$NA4tU|Cmi?N{Mle|EV;a<5?Nyc-C3} z>L25H!vEK_Pxi;3H;;a3?y>j(iS~)G<>xgV+`r-O^QcCSaxvN`n^(`XXhx~MpRj*i zU$EcaVKfd_+~5KBkGoOxw=cyc_1{zb8=}?#|MNt<<5e5gYKKu9dG{XATawUYkEwV9 z7-X9};G9_JLyk>v7t1n1XrbI~#g54vzDK`XX3W=q6fdiE?jG4eedd@9@yNc|o7<+{ z<%MbF-`Tk68}px#8O~0IPq%G8bMjByKjkp^B0H2o;18!P@rBDWX)D!wUQXJ<(o|f& zUS0&O?RW4e3dJ}DaF39j{Bt156G(sntV-V%AcMOB(m~jTX>~SgO;rxzgZeFm(OU2% z*3cpbm<6+g*kDYPCv@6EW!$74ONkd}&fi1rgu@}!#42Ps@WLA`&tsfOW~7-nW)O1X z=~Z%i5e2@m7X?SlxUaST`li<4ueDmmKTvo(Ih5_E-QeJwgF-Pugzi zS8Q}+BX;PMF!7(0+A}GPu;PxFkQsb?jaKeANa8a)alItovJ+QHVx671R1(d0qFfTs z+KCcLJZ>jWm&E;c;si-7uoHtMag&`eBr)Ai^peD6JMrC%Br5ELC5cgzur9^hB3N`{ zh&~2RMyv6c*-EQ%=u>Dl+>dFk#>H9I%qP_&1wMa)Z+}IOl4l^@c+D!?sx1T-TBAc+ z=^!U9Ik+WQ2*|-rD;?zEb{~JLn)vgC$R+rFwp#zbBJdGxo2^BEj*jZTW@4p!p2~Poxh%z8O3sMJ zk|RX2aw*whyO1ou1ji8rTZUx&#c*Eu#BP6swPh@Mr~M?SlC=0&uk%%~=up)kyh=U< zkycr_iz)HEC+NVR6vx`eWE?7B{}xS`bv)&y<6XT7=qXTI;t-CdTu+m>fX^-_cMS42*8hY7j zzxE%!)vD*c$S*fo_foNsq6%A9h!hlGNaI>-4haG%Z?|T!5~OU$Mn-34RF9@Tq^bwa znJ@DPPq-%O4$ep-msf`KYbj2Ax0?m)=R{m*{!!^zRIur{_?db&gP*AvT;jR&sCr1VD%f+h=T0BlGcL@|I)KZHh`D?8D#=k^ zCFIIfWU9rdugb7zZ!AM*ugb6|Z$d`5$vchV)}AAW;CLvmDWzr=H|wk@EvzEwr_2^L zgTGQU*u(JIde!mz!*E)zwe7X|{4KR#`*XKiExgzBcMI-z@%g)&RBZnKgM{hN-EKWQ zM$g||JZtQDv7CMAhG>_F8+!{AIMJs_O@r>X}Wi)KE?H2mNejrO(< zg?46`o|Lm`QcucjZO zb_>l>528W6Sp(Nyt5(S)kFiSDp*IpslFgO_r8H0EmyvHhr!sIA(u2x28ynOLc!HP@ zQ9Q%XMMY#NhmFLy)#c*+a3O&Y4&YhTL9Etb(QPbRaVuKO7NB<4@Ze*#cBFt$qr=+x z2R$FwS`G+9lqy}T--bu!Sf8)qwe3k%5q-VYdV|-&qFYrPnM;>9$XGh_RlbVaYCT5* z^MdW>s64Cl0?A!xUB*b`bNINxg{#rBM;Nn{fDU34^*Wt^QIi$Id`x5RkZZu)4fiwX zD(;Q$=*U&W+Xn0mndV z5Lui!yUw&7#)3X-roAy2V2y?wnI#d|0GbIFy&|syQ>_^Z?d|-!)EK{pMlaG+;a-f4 ztYxUtq)`~@)zopIZ#skR>RlSvYaKV#T;905?Tpu)>2I*HjO3J>SUyo~xEQ6E~7 zx!A4XYOEcUu0ckd;GYC%Z||!K4oXoDpN;KReC5w?xZAU>t^~* zH_91z-NV{RNzTp5h8&&V(9wmfS@#|kPn^qP+dN7s@*KiUIYw7%rHCQ+m45SUbL2pE z{D#&Hyp?0{V$sYIdumXLxi#M0eM)Ro_>NM1^z8TaBb6hy z@e%eQ?)P-|PZQi|$wcHyt@+jnRG3AI^Ge6{zJJ^B9Y4F_TY733ghaI-Gyed2h)Vus z#B852jltCaD-6B?>H2vXeEEt034>Su?1nu(HJpIK(~w8SFc>Vd7SUoLz0??T&`hML z1LoFr^k%L9-%Laz6#Psy&iwQLgvK*}cEdkn;TI<%_6kTq;}?%4pwSnemxfWC5xifP zY2j{T{z7^9DQgw7_A^m*K~InpD>k}hN`SOI2t5CO?H>2D0a>$nf0QASh{EyG?-MZQ zF{f!`sA<(0#w`*vu~`yXS>9qqu?hnU=<HWIYq(8m_qluHmwU!UERTiNh5JVU=>L;2s&Euy|b&CTT=yFm6{X z%`w<)y=L8co48#~&F;zXO17VyQw6B?*4}2##;>-tlmKgs_!!vJg^7;V!7<*?n1qd&%JhN zqv!NV7@m{$2%Eu-Boi4r@pSrVt)iB*y~!A>ld#2`EIq$CVG@vtO%*@-(P@m+;T zRr`Pf5%mvUE(7t}3>k<#n6=Sx5dg8|J_h3>F}anG!1Y{J@T9u2 z)eeAlXfyy;x~Jezk1<`0#yOA`)A>ufDG{*XigX05$?{Ne()#IG%#Y_`wLDw^8uBk! zX!z^Bpy3@vRrH)lR|o$lii5$6!NGDn<4vm*yPB9u{W~}rh#VsT`z(^FML^&QJ)u=N z`Vv_&FIvp&!tW#!R_KZQcn&?Q;$Ris4-CPGj)Qd>CLrdXf|rCS1&;IRD`e*pDEkl^ z&|$EorAzLML_(^NsUozZ1IP<0vneqoGmEo`njy{k3!ad1shvuc?Qlw+ziN9l+WrB; zlOKzph1drX%C$onipVqMBJo{x4&tPNHf#*5g}65~Jgl47V$ z2aKM1mz+ZdYWw>7J#sC}5}-+nWkp5E67P>Z#-@k79V(CM+C!eM6LQ!?-gcH~9!%bC z185U6BKQbjShp9Wi#l-Z3^5aGAus(kBOkUa3N_#bREG!-u}s zwv}IQUxvRlwDM;o?uOX!PSP~(kAKXu=Y#(`82ouA_3 zA!8#Y37;iDjyn*vV4_+kx8BV%dHz>egOA*?Ypva-zlpv>H%f)#F;$N?Cs8V!Si%CEv0qTgBL z%U!Lkh+d>e^6zYkQb=gS!WA-b|vA zA+i9<(!wpo6Nq#WF?q!u^izI(qb7FSGrJJukBiM`U2~ z^#^$%PWZ_kgS%D_8m#lB1wz`w@k1cQ)@b2VjB&D#!&u26#Ne9PS95! zLi^qIRg6kjqPD93^BoF@&f(YFaz;JHFxADM>PpN}hdnke1zFUrL1w*>0m4vva{r_V zV%1(>)mC+npe$R?)i_>6JdW=@?8lSj+UN49+ez5}0LRWjZQM%%a_hX%G$D&|mS8X} zK|{8H;;ULG4hHNBis*#>A%Y~I)`t9M)?K}{yiX*AYptD2qKts-haPiWnt5)TJ6sZR ztS)Q|^!5~t8(2Hgydc9oHzPb;zwB3VL1w@SMeDF7U1Ynd>h$4)sP)$G2$Utir!S+z z+^@XBFLT6uH}qU~9gw+mS4G+yo9B7Vt+DgAh^*FIx7goJkl*mRIo23pvx@Cntrl5B zU*bfvww8|R%8V19%id0ipaMiyP21w;BLerkD1tzE&GcmLNa~xO3fA# zAlpvnS#nyBxPH7{Y&0{T%a(0Oga^+&C@(6WQ(voxMJk6kYEvDuZ>EC~D2fl`#Qh!? zj&5^HXiyVjmW3NjX~a1#nYm&N*=Nju8*=U=MxUN4>#do8*Qj4-Eg-5{uxQ%VT*u-y z*UihcuI9zkq+$W;K7fk2Zs2c+(<^{_|LwAV@cjMc}ZPSt! zMuwewsS>di-T8osG(ffffK@Ndobdwod~7F`B5_HdP8q#_h`-{GQtxUULhvdRcxEN& zSG9g6=bPci;M1Owm@9vzaPh8{$>Qv^zQe&~$8muJ7TW{#coK(uPGW&&LnCth)3G+? zX@MRlv8S!jpUKD)YjLCxmu?~PRLdPNnicWe(c!!JJFMC;fFIufxOL#OGEk)sKbk)w%|jH2*Tq5ykjsZ{k~wwIA39F&lP z1g0RmC3zF|UH|5eciXWuA#(c+K+QhI;0gL{dpYvHg{Fs&fGBWUfwz)ixETWFr5>Zrs^?DxKEM7CI;)r#Bb4Nc< zq59N#uvzXsd62FJi*}$gmf)GEOw*bPXpQ;1MbzNr!tgt)-3dF za>m$fJ%xUfC-RfVasr-6hBVjTw$6`iAlz)kTlLjYZh-kB(1(R^e@jO{tv48CJlG;~ zOv2U!#1;iauIa3rl7j2xykiSSWX&5SB?%xMfG1fs-ZKa0R$M&gENXeIqtEh>J*;K6iVr{&4 zF>UyCjxtUtdQ=FBAne&Y;qy)=OCgN0^hMUUpUaX*`39M4FX1s*^zkHV7-NF$kSIG| zS4lA}x)A8;)5{^6wrkhBHt91_CsFf!bbQep}UEUmtV6_1-IHH%MS}8 zOoiOEyx#ir)0&#Bx0+uNyQ2wFHVSOB;EREv2!O2jQ0%@%x|nrFFO1{L*6-7JjJ?-m)v3By>-`d6EaXpg6xI z-1#ioLxpjIWdieXP1fr-CbCS2F;Np)rjnve%t)d5mSs=Y_r3O$M%rYb1YR znKF2v$TDRpG_SKVrK;7In{350^@cn9k1SJp7nW&VV;7d`m)~_^nQEBPAZ1wO0?jf_ zWbN22Q!~gAmPy~J66Ko4E3WBGiV45u;B%W_n&gGkuj8Aj{_20=mlpB)0r@30N{U}n z!<4`;$;kG^FUjH*eo3m0@&bwclGJD&zf%aF%`Z)2tjG7vFKJ2w%QVSoJeE&jnaaH| zB0`g+T$4_*jA5(ZRg4qUS-2)yr;2N8=E3Hg?3tCoGTkuSW|>xk)&!R6V7ByN(Ki!g zEYnURI@;6d=ms@`Ht+^J)!_db%k7ylexh#QGLN&c~tALpKaOTNuL zeRY~pqjlCF<&)KxDj(&ZG~XK5RlEnj?J9(%UQBt&W~#N%u3!o;G`saACq%_=3E2{M zYrFc59<4M7E$r4%=Bcn-6^uIUmTWRTuv=a2i~cje)fu-uz4OvIzx7@{@N0hS0UjY0 z_?;37D_Au87R`2fLN{fJzWB8U#dGyDpB9!YvaZA7Hox*$eZl3QF^-E{{=^x%53wlW zvHBLOqBe_lAN?TANf@KRzzA!7mCa(AyUm9eQK92uSvih(+0kG0s~x=qqb2LmSu#?> zVfi)lK>qJW!%O^ly)X62u)J-vTHIAx5oNVRhygHD z1(+PuxraE6uv$l>%$|3+RA{qW>%0!)9*oSCMcHwV+(U%T(*|qjZcNh)ik3C-^=Ybo z;;QW1t<_eZuFA;z3}N zTE|#_xx5R5_4v68CtI*9vU*X2VzAz{ejTfOE(I)Wrpi$a);sq{8Lac+n5=bV2|oHo z*7@M;&WAq8DR5=GmnWKS97nUTS1a*}Cple`!d`s`%=Bb}G#t{^o7io?GEcQcfzzb+ zYsrbTSHB6?AsiLx*6 zxqfxKh_}8))p+p@N~XBMI=3n*DQ10M`llZ1)AyrDBsXqHL>iu7-=>-kE#U`C36K+M zE)5f=3ei!-^~F3fF;{oW5x+tbtwAX%)2}tSf+zVWII*R?2gV2OIWLm=$nAS!=R2y; z^^5pAxKz~>=xt4;R_23py0>ngtvW^Art$b=Bh8%He(mMmZu=8^$z*S&GEY=V>K~WP zU~|XiM0rD32lwR#pFa8a$UY6THO9VBbF|{2-6_B{uM%B3Crb`AVLk<#k{fM z^q5E>b{Y-;0(80Z?L&TLO2OiAhEL*3p)5p~%p3v$R+omyW_gKGH;WuY>|1;$HyEuB z3`XCo(#8wGzYHUWrr{AT0ovg+^W|QVq0L`E<=y?d+}QM2^ewhm?PimZpq1BkRV%4n@p_w6@sONn|?G3_589?#U!guWv;2|!Ig_Lb8Hav zZ4mQ#l_WBcGzIEh)uftlcEP;Fg4p*`01=Z|d#fqOc(c}=&sW+*MV8%o?Le93=z#=e zmw+5x;YDO0g#rdETeOcks<*U~CK5vW1jcsV2m{OfNz?NuTFZvTb}g~#qR)5gx}{j3 zzoy#({GFo>N&Usr3n5PV%#XZ&v{c+zwX}ei^niQKeRP>&H{WFB{b4uM+Fp29NFkj_ z7$KJDxhg~5(4-d$mpH{?lP3{Ya>pZT@P}zQ>|;n5P(RXXAqX;^aFfvsPFHNf8X+1U#g(n))4s|dYdn6 zzF4TREc`BglKx~!f9MoGR_Z2A8}n{+r_Z$1jlY3FSMO)qeM3{g>_aQ|;}Z)iVlIdvB>s>1j{xDJ5_HUePvOKe&Ex3coXss^6Q$ z@AT~Yy~+H}%&On(koy~eR@`X zXK#LI9#P-fhu^^2^_`;02%KEs*;jsh4Q=L*@a6TLuhmid>-x?O{LcJWedl(50|V+i zKj63aEfo=73Mz%OJj6yuKNKrn7gVJhsxlDtz5F3&;u> zNb`A2^3z1$mM>wE&J>Ar#MP%uai!7uXT-I0W~`E-Z+pTeDVk6cX#;IR#Pv6_xfZ>) ze*wvWQ%4?I-hLK)_|WF|5&Q^71qrEL*K3|zu7TbvsO%I}qIBe@zPF8r=Xi)=Q1l{Q z7e$u66J%k(N*~Wx;N6~p1XVz&0`Za#kO>`(xH99JEuXT83E%-oxH;mA)Jl(4mGK^X z$)ywCh^v|GXpg&tqV6(EW1?&rn!;+i{dcRSQb_L|Dio92|1_zLxE_pW zdcq@#p@+VQYNG8$T;nJpNbVCvG?G+{{9E=0xO$K(5O-g%ayN2TaCSCLZVOa-O<$(n znJ9;mKR|(goLw&`FD^Z%SwW)5Y^G(!gG5{(%+M_#Ov`ymf&Pk=36TR$VY8A6ZN0*s zJ5hc{1Cl^rfk6{<_J9}^v7P5&1;sUHd8H>v-=g}oR}^df=Zb#S%&}s?9||hYsqxoNxiX4 zSIsvgu1C~cn;&a@Skba#qH}j0(FAxtxaeoW!wzG^lM{odPjtryo`VzN`3@}#L){ZR zf*#1R^ZYnC#Tq?(WG(f9*e#+dP;?(Zh(WcfLL678so92ZmC)||1x4UZn9aOHHHi5U zSL1cDc|m5Xe327zT^oO^&@9VSP|as7##6|J70!ZJ`-RLYPdH_z!-dnGrZ~rHS6}`684G1(<`atJfb`NDoSphF8(_UZ4W&Pv4_;EM9 zh)S&!ua$FL*m+5@oc$!yf%E;d<*`w+LoYY;*W21Cbets?X>DA=ll((|uuHya+~qY& z*ApP2OJpGEDkdON<6$*J7 zuMZT^Y(=lTp5<8^Z<(D-+u&2Icc<|Mv%FkvsBj>44045_z-lMD60(9XWv+%@ z5C4i4Aq7P2nPn|P#ZLpAYBVj9zi3%ryzFR#0Eqre`cKP8NIbM&DjeEy9Sv=GstSfS zJnfr=PgNyKcGA87?5SQkAe6%d`9!E9yNK405_BVwsZ61^h`v6|2;#e$f$4fi$4549 zL7-Fs5ibYYyaegVYp)%sJ?7RN;ewc1+hT}22E@%2Bd-@=;H|??7PDCs5|o>a;@%}8)WLonba*o&`1LgH&6p0C?HopS&HtTTEwvaH$ezPjopZfu3W)5wBHIN1D4X5n$ z61x1HaLT9r!nzE#H+RK~2~1tO@RQ&V?u{t0Be3e$TFkD& zgL_GayfE7$t}04_3sm*?VSl*55|^@Jdv+>#dIw(#@RW|>l$C#(%oRm=PLjv4>r=J4 zw^>J%wDDwbw1Z?$Ln1gbD0<&rKBI#JbY--ooRi{<#o+>c;_@ZBi<&9{8HZE`VG zkq=RA;Z~#JNi{UEYH}@xxzU@;-?hY6unOpM^j_+`=)KhHw8{vs4Hm(LuD z$Z5mJKijO3 zFpd+2^-7MSQ5R+S9fVDmSmwa7ERKkc{=!W$L~x5Yqyg*Mjn>IZfFp?DWI;Tl-a?oC zxy^z8L{N5slP_e=f~k0}H+L(9Y?~Om_JkotUlqN|>q4(81qIdJ_6ONm6`}nN%)Zt6 zs{}&ZvL{9MhbfBKUV*`jUx&r4@E9+!*?O9U#tBsf!OWgf>}kzj^8#S%Z&?&yC~@;m z?wrrvsVVSh2+q97fP9W0yrCP}@IKw)bA0O!&A?vwxEN)ulwA*1#odt>dVpNPo!C39_SA7VfLI)nYdNKc>;r|jh(h2Ak;*6ynJ%g@ZtnVD&?cAXY4d*`0tv$7`52%{xMl zak*QR{u7fB&l#Ce_*A)d<11-S?xyjE1{1kt6;*XY540!x)l|;vgRa${X1)2UBp6KB z5(+@8c1~uMFBmVYyv;nsS~f{Nk)MApqcekyU?fs(+yeu*-nwrgEHqDkHdqM%^ zt|$Ey%#7krDz-#HfE=C_e=DNU48`Q?bAGZQLiX}NmXa^ky((Bp3e8`^-@t&zx96n` zHa3e^vj1m7yMzH|`Y8I;MTUrqNlw1)ciY>lqR-M)p5Gjx7zUeOb(ZBW^Rdc=UR~;K z`G|$_mLgf&+-Z%saFfxngZ43oigh99ii&Me zRIEvn^U<`q*&iOA39VwM_vZG2n5D392*D`sLNBbxi8|&tC#L$Kd(m=RH4@(L27LnU zmq%~c_}SsbO+rHJKQER*kFBMdn&?$zMWbjlHCK{mZ*bXa; z3}IF;faOCv~go;Bz+=~&UQ!4RKa&|g-v$iw8MFSMo{^APxDz(XcxG*- zBqD9WJuYMZC&=x+_n8akFWk;GVU7)v4^-v1XH)z=r3Mub#QcPcA*fV*5EVC~?xuG} z!*uFv|0gwtYMXRPb4XGKfBi zDsXLAU;mM>+iyt7>`NxAOJ&|FnU_ZEdF^ySW)q=GH&sG)^N?>$R>Am)X+4*v=@Dwh zr9ORpRVv7CrFHLmu|g%XHC~zv#h+e2KbX&Dyoajs{wKylR%DExjqt73&S%M-#I@i{xFrumhAgO&K0RA0yoj3fDNc)vgGfKZSqLjw8Sas(CN(c8O*B+bWxdQz&WLGv4&O9!L zgkMLIFbfi~KN9X-*8>vL`J7(M z!=(%lYn)&ju%d#Sn@>@=dG=Cp^PW0|x2fJAtT`7-lvBc5PL}G9+k9JgH@h#N!yE43 zm)`URHHtkQY1q2(UEPO@#6Fyj%+%bfX0A;REI4 zk(BC79(|Dl37yo-`YXQ!sqm1$0W6J?8NxU8f14x=5P5W(Oy1+ zz`;r;3-G+M#J%mAKd{esS*N#hA&+h+iJzsGQqgu;m-Q#Fosak-# zS5ShfmoHS7YwhV8Wb8=#*nZZ5zC9`W?I8PG_06xPy?*ir^2s=e3M;y5y{cC(zH7H2 zLtRl&oY?>2y8o9X^ndt&(|^~FlhphhdkN#xP;QTfraHCJq^pl`15-%)O$2VW&!AY6 zyLJ5Lm;5x_;K}~$t$SGrWRlTP%a8Gblq;z>=$Q15*p<|X7zwCbCYX`XEN|$T^vD+XzmobG z`$D1pucUs)Iv$}0{{PJc2%sxmZWB)VRzx=8vNr34uR#UJtD(Ev8-Yq7x%z;RY~(E`2#4g2!txMzH>sl@mz#mohUUPLp!J|L`vwrOtHXu2 z<}iwQIdcN>|9sQ)Y6}T&ddt=)zaq-@M?_L7zUSQ)sWF~S&uYv4AqDh*emop_& zScwoE+%;WS?U1TZwyTCwwl0rVzLm}2Iz%e(D?6}aZ)s(JzWS>%sulR}=aGgR4Zlad ztAljYlNZUPxsDvwle@>z+^eG?Q{4|3f1{iq*>O0%hB8XXdmZ0B%6Hane9(RZwUXP5 zgNQZRB}dY5`gbtiRs{*zRoXgN+A_=PA}$Y43sr}2E*H7Ktg=}JYL!Lg@}>>eH8;t7 zg!Fwy!cZxxjuA9?gfw^*4XR_$4;M!3IU`n2UYB})*`=OLNr|N|QwSX)DX$rp^QFif zw$zI>=D}<^lp>I+h{s+_^m)Q4Wy->q_mh*pdM`OC^PkB{7xI5C|EIo@oYZSea?<CT$+t=>s zBHurdy{T3lg`EKzJ}genkn%&s^fC==H+5Kxlyg5NDD>^v7b)J=nk*OothEfRbG;(I z6iy)+v*U=iFPluN615;^`;5x9&Los<^`e@XW^wggsili%QP+UvB!`g6&x@`dU=E4_ zvv8p->{jQ!kz$zZQ0qs^^OaK-s?}PDbb42vEYWq=dSqjgUZv6^0ZG=r^KVE7hJC-rt)Y`l z^||#bPk!@g560{21;H-zDX`Tpu-5vWs%L%2;StxP=gBfTGFo=6l$EB&sU}Bf#G1N_ z7uFd5P}6OEZ*{Ws*b*wtvPDc)O#ibcD&3{v$8RF7e;uq&6cv4NX|v!jU@+$L#9pcN zvl?ez$`8@yWfRcl}m$J>OhX4f7Mp-0U2} zZeq=lqen~U=CfqI#n->-{OSTqbP5IVRz|;i zn|B=jNQ$&skSueRuWRDttLca7d|JN8wg6E|U?7#$NMi}#WbtlRwBnl&P;pjlEaZWn zaQiS*ps9|zEcHdY6j1$TXq-bSWt_mqFW@&dQJ6T%(Y6ki%$)Tk>56!`X3>j0!9j7C z*1F;2{qv*vXj0TnQ;+mEUiXBOMFL{C`eNg8m(<00G@;Fteh3{(pC>O9ha}aWuan7O zwzbMLvv)T(k!oS?5tA1~%>n3}c?eo)&P6Wi0b#zUXbkrf=m` ze1ufIQz1uHob!zvSrpTGZzLo559NfIM6imBcXeb0i;BewAkfFSc-V|zu+f_7@9;v| zKnXI;hU_|i&=e@UCw zTTPH>Xu>wlrQ`KYdTVf zzHNT*4HZqGx{lM9=1T=nsS38nDuBSWS%1+LoSY>U9ErseRUDPWS02-O!nw@5u0HOEU;=^Ol% z`h^*k zj=_}`Lz}x9EJf+rw#H32X;+gxv1;|zcraYcvfa7`s!W&<_zh`)oG(%p^#Fn%u#wos zIq0Y!p#5BtuN#WeBPfxtT6V;vtj=4dL!+v#MK$_CeUoARUXl>|E*6bLn_Jxy8->eQ z2XT~73b}KsH8UaU1$- ze!WZhQ@+@A4^r0*1otEvo|fQ^IZ2_CLA6e9>?z^cnNgC#9pwBDJC)OladNA8g}Z)( z!0Cox)gDhLO9rtCskk%~+Mv73tZP z3w6NvgxA3jJHjff=^+(8XFd4>h8hxXfgF`#y|-KqM*8Wf?b$wcopPV*j0|fTd4M-= zpafrcdCsAS19$0BdD9*h!U?ZYLvkq1=n)yq@A&1GizX!)P^(!oSUQ?2?dGlo_BKY_ znnK;wW?TXp9Sar*HXE0;eI2ikx6S;gx$!%s2dgl7Q`LE^HYnYO)R`8#G1I&IgT!cn zP>3UMfNtNIEkP|&`|mEiG3TbD1brQxS*F?uZa0q!w+RgbMv_RC-U&35Hqa5H2P2xJ9L6;0kG(c}fVF&!xg z5PaIK_@S?vUvHMP($=9MFiqLV@KhdlqgM>Rb`dY;Yhy4Tk|UC#%#|`A_I>g@xz_X$ zx7n<26$bmCHdE7%4%h5Mc5mzUdcqD<1#C21@88|&G50oZn(gp7I`KW`?ivY~Y}Yw< zClVgl?>(|`bKd0tdj7A9xaRZ5ZB85PE}WKU+&&)&r?4N}{)J^V9%7_ML$A3d+*;&3 z2=@+iR+?Fn!M)M)HJ7#Xf1j@Rnzi(U>-|1u>VDdcodPAqDC5*yxY#YmP?Q{7Q!18UVvcAu94+F#Q1y05&Q{xf#viKO=rJ$L z@rSD0N^?K(hh{Z)^b7sTgdGg2OIcua8`Q&QyLNAjXT+vJ#P#j)?t}FfZ?FwuE~==QZawslHH>@V z>qht(H7RBE>KI2NmF}(W=LtQg_^gh8=ExkpPlcIg;zjPZqZdpZY>vz;2@g6tcNLa! zw+jOl)kkt?E#to+dy&+ojv++UiIl_h?(mp3X#|Q3Oo6v#Bb(?k+s)B~9Xjq7Ka2UK za9eF}x8n^ENRg z5$`8y$U5#TIn|E@SePtxVH5Anx&{0tvT!)97eIIlJBZL80RO9@9IAn=sf1KC1dzUcmTSU zyYcnCcCXywl#KA8^2WDkJA%s{!SAuPNE0=T>8_DzjEnfzAJXAi{AP89gh!Hj;x}tD zy=HOc&|UIoqbfC((-sSOD+%IMVYDiQObl-xRTS;X>}FN$AK7*MzL9F|HUbnN?i-I0*TS=iJp@zXyc(IBY{vMzks%RbYS>7JeGIP=J<`9&49$pB zs;!)BIu4)BefR1e$3soo^j^G`MQ-H4TH+Pnai(44&@bps8{qk?zAt!2){Jr6B8Zl_ zwaeLDxYbIUEjq#BkO)h-nW*SKmjeY!6tSho#haq^iK{aT=459Zw<#@lc+gW4lSQE? z_d9>?lL~D?^i9euu&gmsTf&)JxWJs&h-)G+F_Wt4|LE}eVeEkek6d~uxQ}GG!=qD5 z!y_|b;a?Qz(}?Q~3VTe)(A6DREGf9{qkMDy?k}-pwZ}M|vWfUqxozPTAI6Qu^;z&F zeTlfzbQ4aRAQbjdNp|(oYTaQU&A8b`-!7Euk5q<`Bf}{VlscM+Zi=}6$!p~K)Dmfp ztRe!M1o3wlN|zVNIY4L^+oGNyjNK;wujYSC#I>4_7yuHA%Z#|HsS8prxL0FK%yFG6 zY*NFpfCyL;90nBg7rYC%1Ub~>5^( zo>Y6ZyYK;7OtqTb5W=1p7Y}owjiv#YBW8@9btrMpU1##w@GlEFhwu;wU*xly zEKWDKmSl`gHBNR8DdTwMsyzg@`;t`oBlnpcL8X~o^PX|iV)!6_eNujaal&cYM$UIe z&L>b)SN#AvbPZ-F8~*`mk(zK6Y9_gn`O6#0;kdDqA0Eek!+&0nxA^S{?r|FP7YVk) zPOp=L@_pP1SvTqX|I|y+XrvxhV@<_a-O3$N%s6p9aLo@&63| z%amb4RC`QsnkUpx9$~HAq0|KYo6a`}rr>>LIydp_4keC_{rwJaRXN6f&QmgE!}j#s zk?^@KUmxH#xsTS1znFgj(xBA=tq0mqx^Bz!M7~_i06Kq%TAttZO|UPrr{sCV_Y0kS zHJd+V4Ew-P2Wk9Oz<@tHXQB1$yJctGy5yL~j`Q5RJ6iTS%{49U$2eYBj%QbIn&inv zV7ytF!sTl8<;03pW0rLX+6Aaxnm4kcc8SbbMM^~e9T%7B_0PN6HM07RhN0{vX8Q0$ zFGltyT3UMpAb@Mp;cI z^2W7iVT*+Ts{o5;QCTyJ=Gv%9zjfC)0v*Die`~Du&bZ`NQYIdk3@ffppPv`b}&ZMrV{U2Yn$=U&;7d z&H(5=uVGch^q#L*%6Rmk_Y_#wq}Ym-`450nN(&DFZY>k*cjoV z(${!lV6rE7O>G}Ick5EpK(V4)-z(Ayw0heI_@L!(&G`A8}?WX6j{Ks%5e2F_BtD_jgEPG0WSlgB^Le zn4%(>pFA%!Lc1m8;9An+4W)|n1hpZsr8`IKQc3e%Qfd^XFaaMc4Ly*kh+3Oj)2 zTCCDj`>SnGB)t?27I0l<^lOwqe2`?;-HOoUU%J6~+sU8Zaj-O6yn~oSlJa zK2KBHf7b--zbv#=FtAb6-#z3%U=a;NO_WM(EC4c~w?ZfmG(Z^}9p#+s~=t1a0u~;C;nF#JKbdPagO!$b*kFPG2!0yH4V<@* zSL4-35A6mSpe*Z|gX{si{!SU7J&HG%aKfWyfV4ow8i}D8mw$8{BVVW#`#oPlZK}`k zd>1Qx4uxfe8K&O$Fs)a^q#HIp1v#(KrJERVuQ7TD+j+qO$1S6S@sn}GSuwJnTIY?x zkGM81xHj2FMaMAXg*85mFVq<2G;9iFyPF1P`ywm+p~uwOjM-w(j{>iC85XbR#*V*r za5GUfTkG>ojam`Z<-1pWMc6|BeLY!9nY!oLTLuIUf!ZZyo1 zu?)7PTGz5U5+B5Q<51-?;tv&F#}oU)o2jxdq+dG({ZqeRik3!fohx~3eSds3nJmfA zW64MQkbIY<>CB~;f}%h4Bbed&EKSS_hXu%G5rmypJdnPSpMs*^(wtGscIe7g@6)GB zllR({nr~VQV)YNtM=~*&WI@q5srmS&vdLI|E|)pYEEOVPc*7Sk>TOnSsjwe=I*wbK zBZWS^tfxX5#C{a#*U*hM!J_l2GbvCJEc*SaJj@#%EP7Bqj6kzDnkPW4A@DC)DL4fX zzr_v(;#tEKgIA)=wUjYklldDdpJlq9>?@z7r%TzK;ngcFjc zmUHYt(u2M>FIe~LDcX&_i+N=c*P3!33}gxfxm zUqS@GE6s`k<)5nkl5)B;R%86Cyp*I>!m~Q71=u6$7<8+e&Q*if9G)xv<~lidy-7OD z+WWAaGL42?sj0ODsXh#->t&=aboXfLvI^XwCY*k-JckW=cD<=F7h!N=c1vc_*!PnC z)QM3nmQVS89GkCuiEp zRXRDuPX1jdH^Rh8;l(<6ot?a2Cwtk+TXphL*f1%4qfVY?C#UM>`g2IXB@L*#yuOO z$yS|wM&=KNm+0hqcJfa;*~?D;Rwo~I>cXZ@dhFyJo%}IH=T6hfRd#ZMPF`UrN9$y& zoh;VL&QnDS*k+xglfOmmD9K;wf;FUUc#Q(_rwC3Z0grLfKj1!^B!_5xo zK|IJ8qK_ATM2rID*e3i(8T&5>CEi!au>~*j;h7S{klze`l2#!nC8hwJlTRXpV&8uh zJ79;@hYbPpF5-!@#*kZP97HQabv#k^#ti}8R+pbt+glj0z)``X1t~(s2P(?wvfk%F z8!Vb7?`qTVO(s-rl66FXHFA&2#D$B*Bdb|O7^Oz*X!g3Hk%Ak&M&m1h2AR5$705VW zs!FGymCJhUR9n&ov^}a?E;-2-ljbbz&fjagxS9{4i$WGm*B=~;F7}r1jy+QvPTzYX z&$ay&5U(T)5b2-sDoPhWlA^y+O~~$&Ko>pP*;sRn$q%&AuPgqGRMu9gD*nAZ529j4 z6+7|^iXNq|+M^1J9+qF1VyFn1vpSXHz;0z-#Ex;JHyMdx=*C$c7yu}!Wm_v)`WW!5 z;7r36s&WM6YAD#CpK3zECjAr$1x3C}?yOKyq@O&^2?Y_1 z@T5Kr1w~j#LcR?JMTAHF6~*~yh!Lhfs7PBC?xwXX%ahHwSLdNCUtf>IaSg=zsieA1v_r6m0g(b786Jk-b-U$siQ?{tvC_0VuxyF4dk z^sdD|$bSM`2OMMdQYH-M^HS^`Y>EuePAR7@0MQl0q8=)|+ne5k+1?oAU)fh`*3D=n zimZs($E|XrC(x|I!MyEk}4CWw7Y z&aM5dspYiWT4Jxl61@tUi0UXAU+U^f_z}(*af|4!Dtb{T4n2XJgwf0!`h~af{lI13 z;EkCXQh$FdEwTR4)fvT2x!&MSnHe(%`puDmAyEW4G+P_#zZ^`7m_US&A!svcse}R} z(PfupdRs?o5JT5e#Sq+VauAQ@cnkN{hJ|~|lYPF}wl$A>-p9_TauNcWUC*f10`aU; z2}No7?F%t?Ni@eAPl3K&DUbz^x?A=O6=u2d6cN(YKf^de&t(2EQS|{g>}>vCGE)xs z6SP8RZPhI1$oFz`nn_@8Z+HgXxt=f)*O&h-OWqdWx-Zf+5`OO{PKyVd7jtx!1EXXe z&;9ZXRUOPT;!h|5QVtg*f)lwJ6N%0D&}Dz<=vB8Y1cszlw}j-!8y-O$BY^o_Pyk_5@{N^vb_r~^ne-Db>G&w<_KCRL*>z-ZJtIY*K3-T$27%eu^Zk0= z_m9NQ{wMd6yF{_hQ}K0@jMn@DicUczs&jNQl%DI-y+;ZsNoFtDtx<&k@)9F z)Ko|;c5$anv}iHsl9MFKS$%^(Nr1_T?Z5g|2`m%kk4vRmv4h)S$^DlxA>&lP%wl>d zaw!&l?C?P^2;&eSDi(`g2N4p}9u@7FnAfv2r!YrV)QdkzhzGS8->dyx#9BMR@f0~WYlCS=U_=8avP6)S}tsZm>qcutC^1& zlfMd{_0~$xKnuMR@-kyO)E?1>bqhfP5T!>4FRSD4^1f0C@5pndX&Z##ikgH`l&)*% z53gAu;w%~;u7#K8b*VBrex;~hR3Gal)D0BPx?93d2!(<=b=cIYztu}%5UGIh7|=M0 zhb9D0tT{E}izL;RvhxD`u zNv09hajJ1U9F=MPy&Yh2!eCici<^BlQl9S7dx+LAN9q37i-_8{|Z8P&EkEB=G^eFCNxUkL#bYELZl* z$Xs&Y;Ja}8gTl{;4q7U*VylFpk4g|>i!mUl@Z+V5?mv2>(EVe?(0F1_Xd>i&BBXs{ zanxpc9^4AslJ%(65gJjhQ}^gp1qXX*d%66yj_}Y{QfNZ)(ky9oz3kFTa2jIo5R=r3 zj1E{YyjDjr{U$K`m;*{u5kF2x^-ode-rTlykyPd!B&~ZmdrOT%BibxnW9^`h5U41F zW5*}WQ(?(`OfXanwW#-Lex@?zdN`x^I{ z)oTJnBs1$e<}^BIV8$1AtzWj>+=G}rl8}?jW*<7CGMR$b*=3H6Dlk^L3XCOZo7_$6 zL`03m3xHTiO?)YISk=;1gW^R=NJr5%FdT&YvO!AFPi84uyI^oHkJ4m8MW!Qbak!ufcWwAt*rKN+N=I>N}Hk)LzR}SBsE9o zfW1w0`?OMhD&I-CuU4xVHJ>-w?Gxx}%-`>iy_odSRPiUonhbi8;J>G~NRB1)S|siY z!bo|s*SxV(sBnhtW0wk<<@ZmeGBTsett3UtM^(h3Z2zJx!7E0`Baey2E7c&#u~fF8 z#cF(=OCo?J=SsGiGW`9j_F~c`B5+@&z3ct@Ng~U& z3UMM_Xv;oEaFrgx9sa_R@e$l$Z4;b?(_WwoJ$e#y>EcxDx*f;`kMCCaNmY1srfi-J zEyZqCTGf#ZG}~vfy?y3bBcT@J!e02UvEVT9RSFSfEM%#%gE~hF{%s5fS5(GD(wBB6 z;9lZ0*?I|YcyYcE&HB$5aSj)pNs@u6fd15E!3zx8XAeZ~*}=Z+0qOFqa6O2OGX|*YJ$q?w83-@)@OX_+E+b9LOMyhW^xO z$6swNt=1zqt#Vaqd34;uFH5}%V`sNhR&MP*qU#8jmdgNMtOl?Q_Oa#qQFtAfW6|wn z#QyW}9f2^Vr{Vh>3i^)hZo~J5Qqjxsl|u3uz1PsXdxL89=2mA+=QIU~=5WDD0&3p; zgi>e?Z#I+bIEr*I`P-_)x6xt;xk2kz9F%UtvBCTE4tkJFWHtYe49~jb4nEL?b&FgVE2wG z#-6cH6V^zrC_M3I+35|3WnjFyYi)W?(1j+>Ns;B`e)HmZ$~9DvkA8(42ixQ zuHOYRR&fT8zWf3g=~A``uQ!Y)0wTcX6hnaXqaTFX_M{bSB&C6*g^BqueG$ud9`g%% z7IjA14(m5>iE=WmW-=>gbBQS4Q4}%v@c}Q!4&RI6y?w=aY}LeBZxtkX*Ro!LA`cPW z{NeP!vUFcuBs~ zqmIno?>o`Oj9Jbe{3#FfN z@qssV>3okVUI{~YdCYwt^Hb*WZ5z_4BkUTy{l^Hl{K~vD^VnWud|Z^8iS^Dg4K#uk zY1Kc^F;g}(F^q;fKB>z;$!NTu2Y=X`>?Y8m(QqXxH>&kB-E$5)^OBqTd5l+@-NELp zjuiD|yx>kI&c_6@)5uOWUfC0D&B8n6l|6jr?nPcfJa1!gnabNJd5-w|wqT2T-`4U$ zD#232t{LzD7~$rQ^^)e=TJ_*|w4G~?-<(?CSyemfGUN7tFn5dw0+J`ycTT#J=i3j5 zkQoia0M>V2ohNUerIYrhww;$74M@t8f}PbygC7grVCOWW;W!EhJ7*Y;?0VP<%@y_F z%>H%&Hl$4160wq7{Z`FYn?yFSJ$J)=R8c%BPqmpBy$vp%wqeGV=x#c>Nsa4W}d(^ub zlXz~tC=Td4UWbM~AayR|vDG;+TFJ92xee<*)x=}!`zh0^K*^^HC9m@rL{633Z&%OL zuBFApK-?^8Wd~>t4O>A4?N{)zjhXR0zDQ0|&zQM~5EO!$@L4g;NT5j(7K!4K8EIoD zC%88`ik_@sK}r-ok9|Q`cK20tx3UBB5l-(MfE@R3C8PzLu3M2ZS&gw|;lD`EB-!4= zSFO&?F1yEnBU#^Bd(Ac6rCi@R?bMp2z>)QxQ!c;u6r*tnL!6SgQ&@$j%BH(?8yo%M%+ff3~r?KSc?IgFvl3_`N-{L8m1O(lJlY+6K)pTTbBDgPc7gh41j!^uwta5A z;B=MqLx+u{Dw6e`Gp@Yin!rgwIel_X;L!Tc>d8}UjK-BfYn}fwVLgk~N8dz0IyqWh zAU#-=4J35)2vuZK6w3Z2L&H|`MSBvD^_`PvT^YFP{D7NIUp9Su;6#2WO`Z`rM2+2y z+VP=b6;gO}AL;C0qAjgZ$u)=AsCZr_zmFy#C&`MMe0i4@CF3lT}F6C@1&%2!h=5I49C9a5HjBI4DZC5 zieu|WzTBdqXf53UFiERQUybs1U3l`cSFIClI9^q7Jixg};Mk(!c-1;f!?9m&fH9}O z{Y={AJC2une}T+9nsCA3qB&t$a2oooJ(Du%WCB`KBn0*G-I{D%bO!4AW|jiD#+=lH zmHUuY#x0+z^Pfy=Jo`K}c_ZdY)tUBPemt03F1;GgtPAP97= zU@}RMF{IhJWt?QVjRB?k0!i6^6}`Pncg3_T%I#W_#8p$DpDCXoM2byAY0Lx<7iH*= z&$U1Pk`)8zmyJ1Ra8F-j{(Gd|p|WiGb`#I?t!z4?cH!QtKRDtEw!E%2pT}qJQLTyo{Ic(YgiqrWo@_lfagV z|LZuIwilCxG*z9#nn{CvVp+pqAzreIj#5PKY35#i=ap*LS*V_;PJ`5qVs%TY#vfQ~ zY!+D@GgW9LfuY!jA>W2!D`lh)!9Dwo1#3x!oK743mnEeTLO!25?N5LFwFdto`_m$e z2V=o)>f`*+^t7rY#bTs4));2Jpko@ZY{Gdtk3goJucN4b{2+~;-`UllrOV_1JckH1 z9Gqb}M`lOV2A3M_Bh2t`Bb?VswVk$!=eDKi9V5?Zdl^V$GtDT8QTFKGD$ zM5-kd8<-j2>ZXsgn_jL;uw=5-l6g*l;Pl%KJhop2r`i>KNQ6|G@i%v^fExg$Mdw7j zf-2I9n0+E$xBsa9IewbyxgW z!FIX_i2R|WE(183gY6`x3I0PUqWX299Bj9hMO$j2ze-5~Gvxd$ZSa&Gaz4S6)bglZ zOSFp}{dE^VI!$*mXn#v|+#5sA0={y;3XDk|(`aemPAR$OmgpP3@R>6mtNL&TEBj0&HRC{* zoIB-=#ktdqd(;eZw7+q?@&cD5=tTPvN&vEj(|^Uyo&2x5Y{#}0Xb$Ddyy3*>1(rg&yP!r?YI$gR+N8neU>P) zI;|%O3>i~oJ$ec?AtQ@RC~f(>c}E?{HJwvFl*v&47zw##q0<<>F{HE7N0DVN^O$RJ zO5GFoq(*QhC+_gPBh1?uQQUaC&0{>>>Ls{ncjqYM%N^wW7bm@i~(( zM`zLkU%E}H99Pyn8qTszggSKt9084W5$)`}~q z7|JR@$=FT?=6KS>;D?4d>bn0QZEpe}Rgo?JC+ReVkk~;9Mn#DdM>L{w;*!`nrlEtk zrK4dGhf!3VD1(CwLbu?^66{3NxwbN+&bW>^&f+pN>U(b#a7&1g1P}ta1Q;~nR&C1| zP(xVz_dRuQcbdibd+-1G|M+P7-m1E_oO9~bsZ-~iij2?maI}K)NnbJHZAUMr_0_)k zFG}pTPy$yhscoRS`Ymsqb8oVBS7j$n(+mX)Rqf9)J+y!&-Lm>Y2P8AK6Y6Klf|L`$ zN>8NJVZBx7P*A8tPTQ=oDYpOI-P|c+Zkp#(`_aduBs!|koOrcioST;x%w58I1qliL zj6dYkC+Hi+DP_#%!EEkDftv5$en3u!B>J3uFZO)SDs)_y+B@$s{kEvqt)fy~%9qKZ z9?T34@gSy&82pH;#+n#M&zJGp`EnqLg+hEvwXRRwPu0?S)F>SY0k)odj$zj6TQsZ8 zP3C4!?t*2^#vliBG;R6YB9GP;HSr}EtML3(k&p}h8Np(udo|vOwv$At6S8w(M)Ke{ zzXg35B~ah#a#!iUll0%oI(>?MzDfU`rvKim(>V`-9q3ktS4p^y<#MDbjX$EDIHhyE zJfM3*Y2YJ<=+&}FRAWZmVfVbMehd9Hs$7*~APS?=l{Qf+uU8e7=|lonh{KlH)fAc= zf$=0!ZqE^xIidt`Equ3@4^mZEhvSFNBYuFT0f#~*i3^QS$kIAWKSH0k4ViCQ0gYJt zyn{bGM4xvCeEX>SychYbPoKB=Y(Z^{5cz+j&x?#62Dv{T+CI#&+{~}u>PQQ9b-9p3o;RvFcZD=H9)z9NfTqJaD$b;`bh|cr5s+!>0S(1)&%0tAX zuqVd|6HrKk3Gq)#$(+S0HII@dLIfh&mI)~it1{CU?zNIIJ`)iaDZ%7}cSrc*Q$|Ru zB~}5S>xw4bg5;$fiCFxHV^cxABJRG}&jeih#8ksGdNDxPPZs~c2tuEqRR~PALx(_P zlALKy5hHU~*?&7E`yW=ezFABChAhgOU3{D& zSU(lkPl@#-ltoD)I(~$>@Yl$Tj2$L48yS_+9CtQ<=?uRi#M&BK%wPWF>8a$CthAB_ z=|0bIp*&jif{87#!pdM8a82>06ntB*GhjT$P>d7Gi1S)BSDIsobr%mkl!RPNJ)9$x zD`@;M7B8jss41P(cd{w&M;4?=cd8FTikOLnEZ~JK;Dwr`J`3nzZzP7Hk!oUSRAi|4 zFrO2A2wzr%$=TJXjzd#fj%b9C4 zO_`n?=5toLvoalN!Wrsy%$2$M+=sv`k~``EULYTR%L=HqgCj{*ZOPY<6|YN^uXlXF z>x;a$UWRB-?1l~otCosw5o|)shsRpHf}<`G6#p%@C`R})BIk{jU2$Lxm^y)4U>+I# zgch)_yS$zBX97*ppP|y9%j_08_aBvP{4D9qNd0;x7$WqY>AZqUg_Rlt5^aidxcs>W z?wgc9ho2!tcBuS0@oH^%SRGpWHwJ?nBA37_tQ}l+YXy@W-OMSCOZW?0=Q|@m zOOH7@!Z1E!{Spz;U-Q-I@S88lEWijgaLj0T-3u}^Ty^vLJ34H=nVM%y|7LjIP2#as z%eSs86;}=k37K2D;=^oV0y!MZn|twp?oj{umB>R6O18hI#fN{5AND#r{I`F|1T-I% zNkp)Ti1$o{*PV|eoxUcMY4;wp%~ZrY;&g&~aAnq2sC7f)aRN#F9T|JY6{i2=X_rSP ze>}}2G46=BGJXU<>J8im%=;EoivV_pTf?a~nTIEh`)YY{TBlqb8{M|64Z7u~}@_?hvD&guc!Eov;AvaJ~FH=_=wRy>2C7lE9 zxt?v4a1!eu=UWpv@D{JxNk+|A>^Z7xqUHlH2z)ua5|z{n9y##NA_7yMpBQjn^L2qc z5tusJO&$|>w6i5SsKNY@fXQq2;?sMOb@{W(`W?Yj%?DnV+AuhAwEJr?meHlSATC4A zhlj+fDv1eI{wG~%e#I4$oWHZ$(fIpvo+_3ex;%>0%y4FQ@;&Swgmm9zgqCOd9GkrE zI{p3Jb&!Jx5|8MQAc2*RCZVMF2aEOq)K{9Vmz&;C3PAlQ1nm*j$E!8wn=$`4?-v*f z(nlHQ{T9+6eg9CTOSln1y4+Z=kuH}AC9wWfFIc}tuCTGr>&@TW_?CTx>*aSrG@WXd zPJw9MLJFeKqA5XiY7@Hl1g0;cdbZ$R_g^)xpH)NYv;;}2mPj3XGJ<%Ss$$q(%WrGl z%Q7+fPvoCTMB=h8hU~+?ZS6S4<^Vq8(~2z;9Du8I!2Gm=(jETrG*pT*`6t?w{{trf zVk*^>|8GZ|{Gt;(eDXh~`=;kVo@5e;t=j9lvdd-x9`j#E@_-ko$_W2pQPz9Nu=x*s zs?SMJE_A>$%@0ERGF=b6KvyDT_Oq$XYE~KXCbpdfZmK8Mx_~S(|M=e13(*k#p*hbN zZzER4ayy)!YtrgnhU;uRqrJ!NmvUeKK;{9-?EZnwgOd5(4`kvah{2w0HisRt{?N&G1c#M4ZcH}9+Q#$G#mhZ5lgV~_3V#)NSo~FHp2c5R zSkvy^A7Zfo(Qzb$)eR-)-HPwvuq)jE(6jDI^%r*G_N)_!NIPhbu-V4|Jo9Wy!bDfE z$0B{^X{Q8PPYW%#XE<&(o7|QjF}sK^ZK`7_I}@8Fte5>Y#G(9_Sj3Tt7M9MSIjJqHCT4S;dfjrGpnM`0Sqw~~1#D~tr(tvtJ;vfW!?gjnc2kgzrWO5+gr!?!4Xj0d< z>uyUMYSBrFR`rgNHnj$7gmAaVVRMBJIH+zuC!=%&@;xe>J(<_|&&k193zPGl|8_9?>lVzY>{g+` zlK=5wLc0XN&i5HpDX`ITKGe!{@vz<5aX|L29&=-**;4+fbY=ces<2}y#~S2p7awN&G$7HQClXDa*guwVyo$efKK)-N9`}{4V2vux4Z_&j^={{)honKC3Vw3D^EQtc}cEI(92>j^C1 zFYBR>U4wE(;_g}z6eZ9uDwXp~H<%Aq>*CHIfvk9*?z#Dp42J5D)oFUGY-dceGS=&i zL`R>a-9(SR(K*zc`|~vp27N}i5VU2#uW!s^%2=YaR_zoLmaR(oB|a+a)GD?U7!9FJ zn4#t)zMC!LEu6q8*qjbFXO(GG3rY_)LO+`QsEyO4{pg$zl6p1lM|WaE>eVq9BraGJ zpgJmwk7G3#6mwdjQOw$#7FsHz1#&(-jF-o(mnc6r@=-dd%UAbd|N3wK@7TZQls@dA z!*c@rPe3^~_K!cl57r?&*~0z-B2_G1Zqdapi4O|Y7n0m!CI2*$T)}=o(@Gf~J}RwL z98N1 zG{Z_;tkbH9!um?OO>zM%=RpW5p2sa~eK~WioPf>|Dy^_TnQLWC*Oeux^Z-#gw6~xd zgWtT+q@v|Mn(VN~0&mt_y^Yr@lhyNmq0AC?+!ke)sL$FHl$o@#eU@-@|5qDZv8U^^ zuQfz>wD9Xi($&_=iiAC#M4D#ib!@Qdw7!;pzfSd8#_qA1`H%h7TfTURUT3-#h}RHc9^#H{&%_miWtWE({)LPE;YwsZFx3 zgd?NmpjNRa^+!hDhn0~(d}~g%HmQ<1IZ1t0kLDtD$+0_SKaWB;|IVvr=Z;EOJ@hGh z1n%|KTX{*CFA3~L^(H{Q5=*EV9+bWOjfu?X8NPZrnR%vUdaX=q`}JqMUSPel`c41* zK-^Nsy){gLqAftR>fy)Orc_%Ux$sH)_{Ja`GDc{QWu{8;4snBfeUZ?8H&=(5++h8B z3GIuHV0h@$gIJOz&2qmRqS0d88{e$$jp@)P#BcKoR1nUKb_h9!@2?j8aSwDd;}knX zv(QNX!vylOzHr(Z!a~LWVTAlC5u}z`bfn|!WKQYF6=^(@=6p=F)+1bJR#syhiKEmm z*PyB?Ojj#pmNqOF?JH3g^mUDX2l`mQ`{dBcjgtpCK6tegd{e%M-$j=`T`6+oXFJJT#?M&w~frZtW0Wqa|^G(v|hpH z)8N&@<~JuFip`QfY)*SzC~kFzQ27zKj6HY=E`^u%v}6b_52WI9ME+56nY%rK%jtx& z5nO8i70LgaZd)k(shy#jSS0_O&54Y1J416a&g7FBg?7dai!5GEX7sl+G&hUnKa$M& z_g7ZqnwdrNYxXDTa*oi*~wwr#Z!IpJYuze|Ry?y*lB>bM1tN@dxkC~lu z>``I%8CqBiX1`Jv%%<=)mi3rzi%K`S7|1Fh2%n^)bLdJ+MnE#08awPFtZ1aw=r5BZqeco#+V>ts0o<;nCGxiCOnzCGY)QO^` zT=@y>Ccpo{y`nYrH>%}7*>@gszQr!H3@IizkB|9nmMtc0yA}CeLf?P-k|LQ~1 zhQLtMQ)rmKd4*nb0>w7mU(=*DfO7$jTy8hiyo>_B7x{yMHFxz-6u4FjpouM!(cOS| z8Rtv3#!4SCu|E*?J8s&taTYmtQ>k2YI7hqVvQGF}%5l#apaWCkgiOGMEYPQ;n}z0{ zz-hUQW#$As;M-Fhge?0qiEG=yrUBE){#tH|jg-QM)N8IpM;Wg+bh_bQ>8jjguA_T% zBt`&!qNW_rnht5-?^w;5$xx5PU|8=z2o(ThYMt@il5 zpYA*Bu0B20XF~}sgRRU<(vR27AJ-(NB&z^NrMMfs4}pa4<{ISsRXm9 ziF5W0bGNHrqA(e_u6oUN=0~P6)b+Bywg9Cqb<7_s`dIv}=N(6wj{=Xjp>+fn?f1BM z%~igdcAvX#9=#(F0)dO?HtgqpZnt|@p(`SG(BWBy<(G_p$H8vlC(?sR{?1Q?7v!FW zDU+JG8F3O_Y@0H%Mvwp)ZFobf+e0$3iMr<_>ct5}y%=Ice^C6V$*31DFFGzOSRG*I zTE~wWhCuowPJ#O3+{uAjiKxT%x%0KrU#t{NdkRcnAxf;`ERTEDd~{VGSuVN>eIZVJ z1#Gj;%{UGoJDd>T%m3;B>Klid=j{bPf7vB7awa-48zz;UA_rWOkgs!sRb-3Nm%^i zUV+7GwRfHL`;f7uZ0H5ozo$`AIkA6d!V>?Sh#Ha1N2M>}@r5#1N{pIL@o44bMap7N zU`E;DYl=|is5HNTL4=IKk)N_#9H^6;yFqjn>FCkMTE&ftHh^Q*Na z^>|9sgO__v=X4DGfZQV%t_DV2MnWRL$Iz}fWn=avIH z?94Q3I#t({dn{$jXO$35Ad>$hYE{jwP(5R4jx$hBAy&urr3LnL^$0V6j#c?4!P~n6NcnduPiLN%=K?yz^KW=&TRG;|&?Qxr#K_5c zAAJfIQ02{1Wl2hv6RKlFNWVbR3sTb!Gr#K%J!p^AJ(+gIHFUMwF2|IS++s=n%W^$5 ziH{w6r5|dI#HfGnypsn#4b>oDHS~Y810z(?aMS+&eHW;pPeGlkK3OGKz3&E zBy&umJ%1g}4Sb??#vN+-70lWT){_u81twrRU*j<{xQ((MRNuTk)2P{?4pea|s8sVa z#??mcjLV0}4`BXd8*Sb8FIma2=hKY2S>+e(qJ&(YsL89k!puLN9LnA&WuM)p0s3os z{=Uf?+T8LBu8=Os47kOrelt=upG=Tq$54HO=q41J`5$Tnl-!FX@26IE#q#_YtGZAv zJ;$1Y+d;##N>l}%r+(>^)IY$g{})#M-*)lcKenXQUuNdtW!3+YOwFEy~f8_2$ zPv}yJbE8vu>Fgb}mq{G1Hjal6!}>yh6)Z)Z}jQr2)zDMU~8 zM_dPa#}Boe9Q)fYF{H!xTI#Ns^jiK`vnlFBStf|b*Sh>7*MqO|=!;yJML@^>RZ@#o zc_rmY=uIa4garHYW-MrQnPQzJ-c0(AZuzW$B%DG*EqTVmR>|?WFPi(8*wv*y;eSdW z)NXvFZDKAvaTMS2(>H?I;78W{}y@Ho5Ma- zqLaKk*%o>e`2*Qv==>a=ZPQC1@+wZZ~ynbk#5~J7PHmSiET9M_A+yxWMyWs9t%XKpM z%k)Ff`<6++kjdw$zx+{Ta^S1IiX;BP2m{qw=;*8x5m6GWg}P8L>h`=U4ig0%o1uBD ze`P2y)JyB-wNq@woaj6<>2}v;4}r@ z=oe50h=?;8m0P@32Y}f9P0Dp0Ilskp!@Vas2}01{)!fbb3qcKR3hlwexnJ;j5SoLK zEkV$bIXo_JqC`a|pJkS?v(SOhHX7x368OwB@Iy}Q)i2+~9o+H7!gwt`cRetG`H*O4 zKbyNFnmP6do3VV(f1sH(nlbgn;sQMV>v8vYJ#Y&nj;^9*^~^^J(#jRPqiQYL*_fF{ zM+9915mRj*^E{a>6yjha>1xhc9 zlIK!;I}@KxlC0IqtSd9s$2!mM(YD!EkM5S>MX;yVJoty}fnN*D*8Alnc*wd0j}R1= zsoF`Y+<`rF>t{9(;rZcD<=NgpXFr7^mkqP51AQCjds3VMQv3k4ISVyx6WxM@>d08> z$>WI&hb$vWwgFl20B463a8-J;ke6jsN{BIihjv2vM8iy^cfvTRA6+8sxSP|fbT#(J zJco&WQQs6e&WK#i#*YnHn|g2*UV4}1(VocD$(4<|tv!4#M%L`U^xeo=>Q699BZQOY zO5)eq8+EIk5xPFfGg6t4vbAoKYNypy8gMPUb<0ujL zdAkFL_IIYvm*6PUYjp#)rF1r>uMy{E6xHq!6jmRzVNY75{RWag%pxrTKj}&%mC}`q z_+I*PbXY{3=cd+wEO<%QkHf{0^)uQLV$2X3kNVyiq^9u&+Kfk<7^v93M)5V2%M|*^ zLpDYQ)a3m6-UprlB%VQ}0x;*=~!+E>fFBg>H7B!x>Y6 z^`mOn?`?i_9qI$wcg5dloBN;1QcqJVfiF!`Y`7`}{+`LiDkXGP*Z`^3*kM%^__;^V z8p?Wt0puYAp5YPGhbA>faAA!_e{<;loz+HYN*le}Te?A_6PQs>-Z}9%ItXW`#6Ohi zHH@ccWO6*;;72dx86%n+oPX9SCw`oM^MRsThP#y37GOwXjmUMG4tP>1XURTt_0hll zDMX((BF+(~4xx1`#;pu+2Hn)R|N6~;`N9+Os)(al6&@$!DwF6~5wrE}9=nl}IN^}d>x z%gw=gY~b@G%7I{gnI0ww8S7P; z=9j>j(rgeD+0*&=@w`TIdnU_(ox|AMlh`1M;oJf87Iq$MCEjBt4z&`UR$_)ubjIJ( z-xXN3q+59ht970Gth_So?R)F(#Vd8*H@pem&Z1|ssXpRgpZ%fX&b=EUEI7z$ZqA#! zyEz^YW_;UXG`HtPAEjCNVte2s%?~8Vm6dsybmmC#c=bs!O`4tY{A^NS zgZJ}JsJK)fh3x>-Wujqz4H~D|yed-F#IpbNI_}w80xr|ky#m3=L~yZ=2pP%<&+Aru zABXQlli^D0sO*(6N+vUz z>Wb(8a-A$2&=+e47l+bCZd1Fs=|GPjW%tquiQOdj2H&T#VbMFG2`WkGvxta@n?YV$+RT40 zp~5s*L1wnAz?)rxE~u7wy79HKFe)W9n*3&t)t=wgGJyZb6LZX;ohOYI@aoHxv;s*h zl(Z3at3uMdBwb0m)vseC=_Ld6hs5K^gaajw-7`~sbmxX zL;QHNdj%5R)GP>sO^=7hz1aY6u+#2s%$}mPf1#rq;*PK2mR6*67-D+k$$UZ7J{-!9Q5DeXBqfO zPNU>QAAKQmX)H}}y|%%(G~NE^bG?k(ySHNIDaP(ip>6#G{rp7D3+EY*Ho-)w>r$}M zFIaB9^w+2vB&c6xga;)i&v#6mKz73*rlZW39zB07`q1-ZDD$I4%7@3ZMgnw5kzcsU z_%4lJCQ5jGH~;qrJ7bSn_PGY*<$t~qXy{LP=#)485x+`ay5&!Au*0I#t2CpP#aFqm z7k-%gC(-SdH#C8(z;R}+7*5nXZ$NtxbgE3@=ce<_|DcGh%F!p;-~&6oc2ywf#5jRi zt)e%G7=nz+^Lnw==jBU2sJOPpbp8puNXxYV*MW7gUJqvl<(`2sRSC2l$(>GtuyY=N zS$R%*PDDH}hgF{uc8pvfMj_n%K9+2?Z2)yg@-Nm!ane>rT4|7fUcG!vg2+9E>c_ZN zv8Mb?4^_)T@Z?)R$||$~@j5o`{C8OSJNJ zfCN+LcZ|sSk*}%LS94vS&p~)X$H!iGP3C+bf3p^h@tMcSkg7$F+4(U1yk<~Xix+#!FmjnKab$JYgh#Gdj=l)9X- zug2fN9L(Wzp(-#T?nuZU2p}sgR~wP95>CaeqZ+RlX{6PTU|mSA^MdbTR(eTFx-AzY zuVzSQ_%Ztznudrc(C{t<}uS zF}qn9&$oa%R0C5rNbNOMjB=4~9f8NO(LFPBzSBK3Yd#=}0g_-Rs$i~@>gOTWAK{_Q z%Mp}_jr_iZRQT-_Y5+D65`xm9VT%ZDu7y%5%FbRyL-X0lrXlu*b16_%%D`!I9B$lfkt{ z&1HOaHf!>&MOmdAsOM&Ji=ir?!(W6$Mmv|c;3Zg@S)M-sbZ%2B_hc?O+3P-YZh_Z* zOXh-OJtj)c7MFK9HzacDmnWPNU+xPv4|Z>yxwNvpATWdi^vG@~v%$yg&x&PxLWP9; zI8FS{4x!ZQ6iQ9mKe8C8J0^i@_hX_Ko^_16QQpi3dBdubC4XF&s*<#@F44Ec?3=ge z=uZ~uPX-v}CkEJ?{}gS&hTtx_u1pKQrA@IOd%tM!&-bVv{9_IG68R2qxuHT#pGuOc zTjGq^y)STrVO~pBTY|gPzS|{FR0-60LyLM{mmSBHvU>X|UyXIA8IMTQ*dEc|*2 z3UJn!XJ3MUOIXnQ#iDF5pDxttZRpk?AA{ax^Y@J` z7B%$}E6ryP@tH1}ltf&vuaIveCr^N>YeQIs^9CO=*ahh_XVL$3$d!E6!XYLJtxgfM zlNpj(D7%3<{_t(JX!I8qIv+kk4SP_Oe-Yiy(lu%tu_N$bmSH5>@b%YT*COkEPT8P< z+#V+@{&&9JoyJK`#&BF;59OWAvpM!br_cO38?{|BMlUa{5qKSq zZ5y_a&KivOM~kP?p=~%>;kRTBE{eexnstINjt|jm86FW{l><3yYgEIr4U?DCH zu28(ePqeO!WOr=QFwv#O7ue0tj$M7>DQRhMh>{{)pbAPv*$^8pg2op|Dh&5!ro7~G2L7n&tIV5$sd;$&!1^!Zq%6{OO1+qKAN7vAgr6rrn)fGbo~`rSH@C3kV?=PMkm>SlOoY_01hric*MWN{pC9E2KsW zN6_k^FHcIUgStjq>Y#C29mLifPEyr@z{UwyYh@?0-GbIiHCn(JzOIY^JJdF~TfC~% z?3}}CXH~~a{B{Pi`4`356M#f3_Ns4S&Z7rP)d8UZDom(-&igeLirPo;42qdiHnYXz zhsN<$)T+1wF}=Ipm->ci5{0!$D`5JA?;`;Ef$Jg?dJpEeM`2wVWum~J>1*#yX|F9FZPi0xrK&in zIt&_{=lviw#%wtp8iQW3p^?Rd1&vcE^uK||Z*c&!b*3hIBnuja0u6RVj*5no3fF4~ zjkqJ0gRT_A<=cOi0LTbBZvmvl;>WN6ae&r5KtrQvbU_6&AwhKN8|@MAn+pX+@+ZCQ zU>S4XxtX7uPOct~2TtbiL3R}qB(O!W2J#Y^i*bIv3iK@qo}tU0q|4^(NMtQSAiAt3 zk|qXU8#qNtm<)Sd|9O^%ddkusipK*xph65w_*)y;#@~$K7A~a07A;G#eUJ3hBtH6> zJnnzICo3)ZmPHmud?1AxmLrf`QC3DYB8yu7P|;8pACncTDflKdt{@$#wZxrVU{}MU4jyVzX{4Gof&Dq1wODLi`rwV6qo(i!Dmn@1@%c#RA6JRz?yW5gv@twHz_28M$D!9IrS(0+X>XgaJkJ@25;<#K5$@%^%5+ z;2^KBSI~3OXJ#%JtfGlY_i>kAU?6`F2Aoy0Cu+@LsBzXNsa=$HZ&9CKSYVGM)JYIr z?IpJ^=Ld0aYBs9xIFpA8S({lB9cE*cW2ei3+T?KKTCJoW}oR=b|>u=uS)5YzKSEYL(zRQ(ok~= z9q7RJQ<}|nEt^^3qV4F%k{O}F`04*irnCib)_lpLIqd7iq-#@=V^l`)(%qhdED?t< zG|P+c&a02lAE~Cn;@CN|#cxHK{9}{^?$)3S`?|WOyIrBK1DprM`a{!&p|u<@jmwiG zG?SAPF(e5Sj4LTHC-fZy3l>{4H#0owAHV_=2PhM zQmF6p(5SAoz%))By5LBm!1c6x5tPRKat@PxGpDP^Lq@45bRa!=th$iSdbkE+czh-7 zy(o5)IMENGNJbz}onU2h&@DEuBQW;b@{!arcstcj5dvOp9gZrm;z(g77Rv z`soPs#o>E2%YKZ;Er=KbW5lVX>g4b_p>-$Sh+2r{!A!P}R|~lFRXrB!3DJCB5p>xS z8zozyV-0r2x%$+Z_!nG(8{__t<@g&cg6YWZ!z3 z$z=2G{?6}in*FxV+>Zu0?qUAtK$zT25$IQG-cl5k8xKT?7X4rsT{g!SoUS>bbzxAdhps3nG>Q*M4czzgUG#gXom~!s+lT6X2uCEqPkfk0#NP|eFG};g zC6kcw2DcZX4T+w}wB&+}=sVUNVSlMFv6;b4nWN8g6X(m8W{r69+XN9+f(QecbLgeT z+hf&aKxR*tiQqeSmJs^_obwimUQMX*L)mNO(vaNJL*`yG>9l5Vnq_MKdC1wa@H;Ig zuwy}$q$%=SSF)k6Pt~(BcD(fI3v~-qOD-_yS})h5Es-(Q-p7gUH6T?-SI9!ZWx$5M z!|wTztF9K%l=jGU&S5&I>q(svye-uA4M!%D63kYEDP$fr97=}p97FJOtXEmL~wE5F}NtQ`g|fVeRM2QlwdtI|LC0>Q{Yr*I0GKUq-YDE?8%d z70E}WkSigv#2Tz|3G=d-!@=rDeRT|@j)ASQv3lLPSQ)eqhzZ;BD|SNy>X|Vc3ZW2zvXCDhy{)m25 z1p0+;EJ%xWtJgS<(96vQjK(5gH*kxgjIm?JrQW%vVPxj!w#u&l(pT9>LsA*>j-~7@ zIAgB2U6I*mp2M?-(&vmy_p>ndW(V-f4Gs21^V1N?sw_nrh(^UdLuMg7v0Zp#Pw))Y znUfsF`co1-agOHtiIW84?8q&ewRA)dvW4>`cw*aKXBxOR1UPeJH7D4DZ(7;Cc$p(Ql4eNZb9IBf6eTy zN>+$!msTSB-2e8vMt@&vuJyayUH5-Q&-|Q_h=R;tG)-M4Q|!#ySsrtxTn#_XC{{)~ z*(lAu{F&l+bQ{HO(P#B%?drg2B~6Jdn{aF$UU;1fP2VC}I31&gv~ zMlYa9=t^PmTZO^52!kJCrRBxG62?sc2<)W}=i5^XwIUv$d~u|Qb;7rWSf&WETq`sy z*a@CSpxbzWqjXugKiW>)zspWi&_hGv;^)f5TZ$@lk?>E5aV?(LXjFuiU!f(MUMX>| zXk_|Wd4=!`L=N;natt6xHYFZx9+($pTEoMUDvcVURdH1zFtqJvj+FUQCTYIf!x^Ey z#Oyi==+put_2z9*a*giAAl*;8lS6l41M6f{J3JIW7&rwiiA9%eiX-{CtC65AC5Yk3 zSsvp!E_c&2NXc(|vXJf@JXw~g-!Ig)AF&EXh7bKZbrwVb^6?=-@|RJUi;6gP{wMJ; z4Q_+CxYqyLcc}7(-U1Y;ANLKTm|-yeqSr7>)Gl;Rvaj~nbj{0@Ek!9oqt>ADN>?vG z2v6#+Z<@2goKUcPxkw|y?BZEj=E|sqhc?}44lY_nEZ%R5r|0!7cLYX(AbqJ|VvE@% zTg)DBR#vU-F~;P@vTMt-gBkG^vRQXKX6}T6oMf(ub^z154RdxXeaC~g9E)B9ih7o( zyXsyhQ}6uym+pxEk(c6o@caL|%KXl8PnNIwpwkojeAqZMv%6~gj!B*#uanm1CPtB= zq7gPyD}%$OeemIEOLU@DccOXbPT2+(LUv<-x%nZZSO4T2W?sXG(7E2dyL)F3Lfw!R z7$}92f6d;2!@LJWUgrhvvg^p~)-7kka9fLaN4yuXc%&X6A|uY#Bw1)hY_|0?hprEY z(A~oRNxJ(r92|tP0r?Z=5PC)6UJiRjI1^RAC2k+84?Bv?q2ofMBefMo$rk!YOIJh){8|nSX!$gbPZ!FP5&Melce+!^G0+fs;fuTFQ(3TR`XlPCyOLLZ{%=+9o!j!<&tzG|#_^ zF=+5T41&dJ=5>kGZn121-Pc5csYKtB(^g!&ngy?yF$}EX%|$%g*!@!}aoJ!-1vnq%6DcJfryIMBSLc#uQL0)PrGplG>ADa&?>) zjNQJTv<&^*0{uG8BVei(nZ;}#zMQzg^{}oqF=kwl7BH*;&&Vqll0+P+V9V%pEmK4E z_YzPUj2(g*u(j>48EMO)OX7jD2E?X#>e~X9N2^SlZxa7DEC~*+N`@ic5V2R}<1Fb> zd_im}cJ|=qTi5s|{m~5lKEuL?mM1_47Yf?80ttOdNtGGZlTmLqwuC2jf{h}N`(N{p zWoCOYrF9$>iSVN(0e}hD?fa)mR>RW(pz^QVnikU%1Ptv7WI5Us zf)e09Ln&j4oSJi9TYDT;CS^yES{Lwtbgte4C!7Gjy5-c?%{`mZ7s`8e@U76`b;a$)?Yr z+&Z@?&-FS-whOnVRi>qF*}kdw$xphbzL1s~{C?}uqN%Q9GmETZS+3WwD#}{8U5ah( z#J&N+`CKq92YrJ$jA4YBb|*kfwftOVeCeqbyIJn+Kru(Hj*d|8 z$eFt$o*kjyL9_cU$>p<_h0R_Ns`W&nJ?xyvHnkX!c}#C}rj{K?sQky0j3&vYGwIeEUC47BrKk|xZLP&?Im zEW2*HgeeG>hJd23ox^lf7 zjXI1lyJlo>J1m26ycFHINU5PcU9;!vdpy|67(@`@P@$dUDc*vGYBL(xoEFeW6gkNnZX0qgTB$`Qnt6ql; z$jKi@G4QoiSZ(y0n*_<$^A$DnY~fKkStue`<2r=rckZ=3tNZ9D4QI*VFPDT@&!)qX{44$p z5IL9TN6iN~G<6N{zefwI_I`q-6ghd;pQOko-|-WZBjzIp5W3_cqB5ifhVkKjJgF0` z#$2))YxhK|GvfKTlb)cAl(C8$IZq1{sk-C}7As(a4LU8{2^>D*q)Wt-e1 zy%-@%d*0a%e8Ut&qUB=ynKM-SdBCZ>e=sBVg(Td7bzeNcK!4w-45UX(stR`M#t%_zPZ$j&YPJqWu|r+ z(!51JQo~crLBcCN1#qJc!3FAcfn|719ww$ubW=0829d1zXGp^KxyE#G znys?Vm3#+>UE6;TTPq)v6~K>+O=*KRk4wJ~`;+Il)8I5?!sWUS<< zJl3E1xZk=KU~k#y4<~$``~1X~C{poGsP}}~nVwKnq3gA5?2RHGo#F^J7mAkI>2)^; z)_aSavDp${-^Yvf`JU~pQXv+t%v`)lxEn?71{NH!1V^YSDpGp^+K%3)c3y9qTm@9w z9a=2oI3Uu|g4lFX-$6~4U7b%D|_I-_*tP{KN2OG{CcL=kDWV$;8n~OUW?IpjqwsdTM#N3V@&=esDadi=%9J=H> zOwH4RBV`piQH|{D`;X|m?kdGC_3<`&iv9-!g=+Nkc1-}(^Z(c0NmLpCyS)=GzqwC7 zb@<-tY4oxvH#^(DUjGIO?KY9mC6I2m)=HUvihQBD3?eud1 zuS&7$M4#SY-39xOtyZoV3hRPna3Gsb&){wDqgo$45vg=ib|)Kh$-Tr{J8LC+RR;_T2R`47Nd1GlT5 zVk-P)Xm3XFYPI(F=*ZhxaI7<9H!`fSK;gPWXaXBRGxuX=X*~b2$Avqt2G~PTVs566 zc>V*ViTf9pYOx{e?RH$7yC9&@UI##GSH5!~IALl))RO6vGLU_)4xynt?bdgv>yix9 zRh~as1|(qa;A3FV5emI6^_#hkPh_NtZneKBzFxR4-N{F*EB6AfQ9jzX67|P$6{I9P z@6MOtc)gO#kxcLcfP9{<4PK)rl9znRq!Pb*@~OI^ErEfF3{QL`Q!i3lekfVIo@euX@Vbyj|psTsaxa5*5HuZ$r8&MoF7ESssoqDZxWwpjE|f7-EiT z$b^kHVY}%x;;Vu?gyA34Fo>8ge0Ul_jIBT^vkJax{p;Jm$To5JCgTMWfANr8)R>2Y zv2H`)hUfd^FSR6oQ2(2c^lZ5TEi_E@m~HGOvi#g5y?cYNbiHr*#(*LiLoeTruj>h1 zh8&&8b2-oF+Rvx*e7u#MUDp#_${jc9Tw2QCgZl3Q{dd3qyHEe!tN-rNe|xPx31aQ0 zrN_`yb^0|jDNMYh!P2Fy_iO&Z>J-U;OMV260>u-I@^lFfh9h}+be7t-l2a&RKCF*% zXpG(Xdfv55w^ikaNt((_lB$aYu=q`ZSMhqHqKS{;NsZ~F8wU@#czN(@y)g;=%-G#5 zU<`h-s1IC(nHn&4;r6&|>cYLfuGy;%GaYLWjK`M;-|;O=fY$}UUig%;@(H0)j=cif$#e6k)8ouzjHq1IZ*m4juAhZyU zU^tqH4&Rz%j_LKgyIuD_&&bU&`{grw2|za21nvpZS5$(Hid% zJ$0D8`GJM=(rRYi))gFKaK{QWmhe2R9+&W>svCr>29CiBN_g6P@%$f=Y>vyctiE+! z>(pMXc*OCc!km!luc-q4-l7qbFv92f$S>|O?`i2Sp5Ofl0-V@GSY}G zr+S5F+=L5DcT|?{s48xKzA)e}-B5n~T^LQw(^aYOq#gZbqz3UEfT)+H{fP$B6ClC0?$C(9cx!S-%aPd2{RCgUchy#xc}ghaTS*) zFb5AkqG9nNtRnOd5Lp}5^>jqbk?f^zDk>DOdq6My2sK;efsl5*KVq%I1q+q$X2YEw zys4@@D|iW8-epY3G!-2s5bsY%w6zmLFet_ZqE+v9{LF0Ar2+yl^Q0=r76LuQPSX)E zTy|8U|GI<<7zXNXPNN4Jco&5ST2jRkx(bnh<34Roo$h)~79-qxWBt`le%MQgR>nw( z2My1H)boERx)vNj(Y5p%ivH=G1Vzt)3MB&jZIEcy5*^oayY_n{G_-_x#0gFmrmdB) zTu;exW!=CF%+)uKxUSu#Ilnq0RbPUM}> zxph3JNIh?5eWTUY;xAMky@2{(J>655Q{<|D6J?F3%wL4oU@?z=&d;hfE2lG)8wgg? za9u5r!ZxPRQ#w3}|10>vq@wZLhU=d9HhyaE^E1_%1$D;#_M;mSp^A!+o`o3TBX>rEqQVr9mc`m;v(R7G8ntL`S| zQWzg1HgBVZj;g(lx77*+y>;!bdJ$aNM=pU2DUp@;*<@`99MNV{%B;FL*I_j3drcrkUJ{UD8uzu8} zz9=EVMaHh^)fAJS>g0@74LnB2l17Qedir`AJ1grx3+C09O$cPwmW`=w{4(h9 zHg3nKsl*dn%b8HH$rCzwd*D|7)&xfL_a2;H4=|&YH!RH`7*g?Yx${}#-F;+B|R9sAq*f$xKZhM>5^bg*=4tW22>RxJ+9_VIm?k5AU zXgFW@NW+6}=^jaU=$87Y-tD^T9JC~3`~NnsJM3ECqBr)qz9Xq`To*Hwt#Pe|KKw!( zchF)eelxed$17d+H}cjustJ1B^gTJAm6?v4kbzYf9AQMWFGA-lOZY96L6fO7`J$ZEW* zs2p(~DY3Wgx-M7Ug@7K_Qm3t2ivEcAP;YT?1l2MsjX!TO2n#yV&Bg}V80(;63q~6B zKRcvoV7%_VV`q}8tl=L?RXefH6I!?w24TbDu4K(ZRc}76p}$y<&0E~9XElQgxDdfn zaT;svfI0#NP}9Q-;w?k<8)OB#)z)`<8!}5>Z;J(>nfuLML@ITt;!ZD&b4EP>val#7 zCN8F{7(1<$!vj54qgSw>^1J~y1^YQ3$G39R#S~$(a4%iN^~gcXm1PEpWmcW za<8*S)M;Y<$n#7_H>}3>2py(DnDKkRQyAxRQ!TX=O%i!}co2#?e_x~ z;~{>klKdf(fI<}X`y&YaDvx??e`i%^!aKSrlA(Wjsg)oDL!hFt1il~ z1|&?y4`#>`wgfSG%i*Gd?BZ@|Czz4Z>3RDOa)hfIDBXz^4ekvyxTuZ;h&Onaw!I=C zPqXUDx0VTLVjxd4OVl?{*g^y>#LB(HRsR`u5{j3(B9lonn!j*F<;%X2qz>Ym@AY)I z#v!tQbTIJo7ArkQnHJf2@iQ&XLz(stM-(y=B!y8?IkEL?vN2JbS+MWQgpr zrqP!_w*h%c)RRerDGA?WJm!kZ;+SCYLSds0{kdMR)oo~=Fd3BY3m2>FrXPT|CgA1o ztl)8>_<*@^;9S>(ACeP_7Y2*TxW`rB$}4t0DA1dG9ii?0U2prH-LAJW_b+X8J-6N! zVpFM6EPTRp=R~XuOcxwgG|YQUJ&Y0o&OAu>BhDhOwxP^wsC<0MrFX&ZWbBag4vl!(gIVAH!PTrX9H~I0ZOS1A2C$ByWM3W-G3` zxunTJ2hgds=)3`cQYLn{iDZ#Q`jJ=>1LDQ5`n@nRYYGMj z0;&Fuo1$Nmluo}bd4z=eAL ziKHh2!rAn}?!YZ{ATBfr5OXc>#A8swY2Z-I8bM2ZC@!NpkK89;0XUeY`TEV+K2h$oXlp3VZ0kOnedeCCl<; zYvj#cnJ}TrYSr(omd$-K`Y%681%PH%k-$$n(qUK3(#grOtHju$RvCMV7&&U`I697@ zA2#-5S6JAQ8f5H&e!D-G>gbJd!Lj&=ff!fa^DNKSYC4Q*Fhe>Ub84zUWLPXmk;g%N zi`XXX^-R-Qtf22%zAXz7;VkBmz-It-n(f(=c56{Nn>oF6i~T}2ZES!LI@8MtBjxQ% z-4xy|W{8%cFY9IED_J)m0Kggt+aM+=3EkCNbphpBdvf|pwgr74K6t#Wf~ULcH?S0V zYUdWEBc`vEXT52yW4z0Fi%te~n&G_2TVld>wUG0;6lSx5d?kjOYe>@t7wUo^#y-+- zLF*09_mX@xl~?*Pv)5{z| zy0p5Hdrez7kZq8%yFeKhI!Ei>nl+r@rB8L8>wE>k&FrV=i72nFexp8vNp7~HQlK~) zYyt5*W}2S`n`k>H#g3J<3f0Bb^oo|tt7DgHjXYYc=zlSXI3y3klW|as?vP(>7%e}n z!|cfA1O|)x?S}bC(PAi6V#7uZY*P35YC6@&T_76u-x$@gIUbMPFa(>XRcKZiouNlZ zH*8`2zqyRj^1iH+(|^@KJsy;?Qd2E8dCOqiXuLpqg^k77f5B5kA(Mpn2US%FI0aC}q0#p8Nrg(!_rL^Y@` z{Q`--<~_swz|XaqhT}c8;~(rCeln7S>tO$t{YM(y^d3iW>!fJVk;RB~MI zSm~%V%&}+^N&;7V&9U5T*>-n-&+g8ZW70?B2zJxlla>stkB#^2R?T~z=JIAW%+ZlH zA!GW6r@W5k6v(E)FFD}Q1u~Ka9Md=SP{5luCbP%c{s^WOfx)%qC7kC(#;1EQl47Ng z|2+j|eSk)n&lMeLIuBESdd10`e-#r@9HJ=XcmYs5&%DW26vAB%bAXPiy4Pdwd}|2# zvRxuP8+vLl0N*SNBXo0|daM)m&j|G>RpJVuYvODXUC*kxT()~6%f{TI3J|eT;?fp( z=$+9s)|1ZhqTMH}w4EK(Gl0C6xJ@X-9c^kGD;Rv#xw3y+?8k;#SJX*AyygQ%9r|~r z=mOA{GhUgv=DL*}b)cYyx9I!og&)iiZnpjDvw0%carw8Gyw0Oor^YgNKN< z(?oRdJK3>xRhrub2(6VRagmVU5*b##v{ja?IO|QN%lJ}&aE5yLqr~KiM1|z%^=jst zFp)4@qF0JM&DnL4`Xyx(XP7r6N~2Xwnqb5_xQ)DMpts>t5i8uAX6L9<`9@}kM1t$L zG|mo945!8D8>4QmWFtZlci!zoviVs?r8sh|W$7a5G0xTcl40e75`gB()GHn2GFrGg zD?p-?(ZBItyC4JEN+wLa)x55HW&e_8H`A=yvb*!d_S$V-was%Hu1x0`C_~*rpG29R zw|k47HKQTR+c3u=wMVO^@+EvK|7v~p{Q`EaUbCv&^M*juctd0XmJSpa&EAG#yF2+! zv_gO+`|IunCvBk3(Dts-SDUCPILzB{Lk3_VaJR!4-a=d$&iGD0C;$#umc+7FUY2p5 zn$BXM*%8}ecU4mLMr#T*AKgtq1n!)w6PC}hoL;_7xL zxIoTuAc$?Kj9gPvS#~_E2v_7yCpHyzWzL{a1RQLRo{zbrkVkP<$(-z!m!%g2ij3Gm zo<_7So`x6sf*6F8aRVr+FOk$^mr%;St(HWVPQ zHuPt#hGUh_-5Uh*dv2P0jSW1|1!-;U8wtEjBcpgVRm5 zT7)4V(jp8m@s%fIwYO|yZ-dL*@G}D0rp%JQn`FxfkXw;*+$Y|OM_CJ52P-`0&oi~h zD~F2KLfFYMZ+x}KRBGcUYbiWat=O2D&F{eHE6P~uWuZqkn_LQ`$BU$dMOBgdy_CLQ zmb>5R(%72@tl<5L<*tL(bOslmxwi&~pvMZ0DjI3jtQL0YXr0=cnO=gJPFiB#d0>l~ zX$uNLy_)(7or*By%#UPcQ$wC7Vts!Wuhv+Wuf|)HX*hc8cg&wnStBw#U0o>UwXv2i z{^eg0wneD$nITIeLY!&n?;$C(e#_kzySM1B1qYZd=2o|IRdvFW^`z8Q)3QuYT}o_uQLj0N3qi62w4LfFiHto(4hE));AZ)?SZhw`YvAu(a$R!4 zq1aIt977hI5?OF=K`|t}Hcy?w$qNfk(Py&Yh^5+=KrFG^q;IwHB6gfFD?7;Z zI#vSNl9760U#CJ3^{p82KEW+6GIM>6Duk5B<`Y7&G%DR($#9wB8!lYQ&+5KUmR7wAg&c?~S*4-CeGy znnS(&=epf1=3P|R9>_^Z*}EH9H|(`FS&KD#vH4?7ElgK0!2yo81g}V4g6-lX2u>pw0`Q1e42v++vCI?Nb93NV{H+Y!#NVkw)HFa$`{-JgaEC)YCV4jo ziumghugugEBY2ypo8y*=ccctbR{7j?UZ{KH{Ohmf&AQ>+ z2s@uAv(-7D!~q=lw>szW1RsSn}3$~Vf5vel$N<8|mLvcFrlB;)UAW06?!9No}HmT4}C2-^i?9qN?&y0h-Z>#3CeSZpPzFB zoh+~JmW3@(EZr!9)5siba#?Q(CY4NTDwMd9Z1NUPqO$II?G%0po4|P|Hp2`hz zW3liQQ)*DLKS%?nHJ)S%XG)3hpVTF!QnllmzAv}w=TKtgmq}xzsj5u6yf7-7H}#fP z-XQ6nTESNFI`x8;RxQPNeZqQ`YSe?)vml2#SRw10yA3Obr2+=&`Bt{~G$94ljZ!#r z&!o34GH^Pjc==`BqEU5;Ek(AB)~U*+TxbE{MsU3nYDne`(>bk$Mtp7XZFs)LWms)7 zb%E7H4)VJws*l(z4&Nsql9Dl6xmsAY`s!o~C*GRxaEvKYY+s1A@W}Wka$Q1CBKHGQC^`y-Erkz)F0A2CYFX(%IoR zn#dNR1$PyvU*>u^q?1fNe)TGc6trj_bS8k(n`#@-I2ifLgx;JgK>b*m8rIuX z>ur+tHpzOMX1!f*z0I)RMp>;$`NNiL~poz1IKWKUl9JgP7UnCY6EqNL^gxE1+3QWO!$yNjAm{X6$t?o zS!qiUm!3y|C8)kgID$!#^LYN(BuE{P$Skxcf{mB0oU5sVv$eE01x|XWMz5O>!p`sM zmqZ;gQY_-nM2}iDl+?kr2K2hk~oQG@;HjE?a`iE>Gjao_E>8%;4R@65ai+&)K8WKJP!TAIa=zKWkr~z1LoA?R6>2??ngt?fsf~MqHFR7u@uIxU4##cY)ir$Q9>r zudS8Ap}9ggJ|;_~r;r^0JU7>gf*?0!+-xKZej>77F8{OiXF&yTw8wZ}tA9S!D<;(; zU^6ZTawI4u#5eQC9{WFBW8c!=rayu;#@EiEP{Uref_{Y)!~2PX-dhr{ruli618=QR z!yPU?*4%4<)pts)=3kiigViEEq&>J;hfCd)a$;?!?w0C6Qw>2vN#mvX^N6?lI#*<{ zy6hW(r%PVq5^N82fttiLXNhWtOJYrlySedCG!%Do=XD7(GqYocj9G7pDc{>cuZ;voeUP zv_$^K@y@5}R?cGlSdIGyQy`SRnj>{KN}Vx8X3d{4%YN@6>;4Z&kVANcMXm(}Y9ZYT zH|$b*Qn29?_r(d|0n33mFU6SK8$2}8$EitrF zMIpK_SB|8HONjJzQ6+1nU1S}I-2cJhB^pM!po*<^wS2O; zE9LY*m6z`FUa+85w#-!V{sKpdV8`FKMsJwx+{(GuYei>;A^W+p5d{xVVC3UQ#xnAj z@=%yRJ|*#ELUv;}pFI>e@_lu$$=j33+bX~I9aBT`r^E0_QyTU67-(v9u1FGJ!OZ965xnJBucg|MO?UB~lC6=F*}5QGD5 zIv;yh&wwN1c@i#ivzm8EI>7~BE#z)ce0yTM;kOG@#o@#y9xjo}>?>XNpmgb#4SB3= zCQ-h@*BX^O?t0lWQ)Q1{z{rqBM}18f=#^a+TU6$Xp53&_;~{22P?fO$8RH$O%4aW^}c?bdKVfLcfvt|T5 z*#)7UpXjqn^M4sTxr}csu0ci9-Z<(U`&Q7N?UL&}tG3}26?g=$Br&7F(R1qj7gH+l z;Q@K6PlU;GglGtJ@XQCK`Z{;CXdm)#N2Aqz6B)MEvSEH9XArSGT;+Jme?h*@-cEPq zLcwT^`v9Dos22zgYn+rNGdUvT6_A>E5w4=H#0Hp2sfoMFlUmEO`GUx$nL3`%l=zUk zO&!0@C^13?gf)hnJ(0Cn>wAJoU5cYs(nn%!e~0 zyr?WYLemcCZ?sBXtgDVJFb$12Pd^AZT**!HmEa)8bRF9m+76vKY4hC&!LwJWqYPxg zlyjM<^GuT;{ihUIA8NR$3^X8THF4I>_1n<4e&w#c7zXa!;oM^HfTHn$3QG$?g&d}C zwF}u7o{loep_dtN56QrB2_A-8m>5^ zNYE6?0QXa;O7AnYK$d;(b$?KnMQEUMglUhLFV)`)_lob;f;{XU60lOcw|h-y`Y|Z3 zFc*|h03~~Vnf~n-E6+h3qV!cJR_^On{ZHw8!zIML!K+zk+kF}_5MNvLnnG03E=56` zRWR)Quw6D`_A*-VG>VVaiGnioN*6|11aP=WwniQB#idT%5g zay~f$?@is-1A?{Gzo3mn4P2WKjT7L9u*yRpj4deHoGm?Wv@3KCba+sfMqE?hTZD?2ArOAzn%3re?i}(i;%K`y8?YU>@aV<0KTrbBIt7Eg7<#BAI%f{P^jq(qMJI68{!V~{>zCy6+XMD&$(g}a?V*T2%}ljiz^*gi<}K=xcFd; z>GREDHKU8|Py@7EHgn`1`j<@8rBz{jsVtyy{Kl))mp@9Uwc4Ms`W?&#)KY=!pX*KZ zbm>X-lx#1K%JyPFrWaUINDnCVasllR~Ov1QyaNSUskE(X_F9l||2p4<}cG+;bV~{s5Rin`fxaX`oozWqFd^^V04Gnt7lrQ+RDyD;*gdEtSUi?Bn>4+`Y!G$KKPv(p54##IsR{8hn z{QuVZrB4i_I`e9`dZrCdHEiE_RrWKT1tnxY( z3R^^#A&V4(U91yOAxpzxgWvs_^-@mEL6_gD|S&rJEfk@7FB z2Lw5(50^k*G>j<^LNOf-I~9&)t@{OKtM_@s7l*38tSFQSl(<6i;2Y|tUb%KrNxXJr z%Z5egtK~Yo7Sr0v6c_*&m+pY#^l?!2YD%fwt&m zdBw09Jh7!>Ylg;ayO@Hz^EgI>m}hCNMB8~1k0(r!?J#$dJ=FD7uur2C>^szd~9sm%xrA+=lij*s?`H&<=~QUR3$yEjDd|< z*f*4WAdS~^Nh!P71#VOSouc~8ibr6#iH|L~NHPBqkb+%$qa#@dw{L7*c$*_ShG|*M z6lG49D%J&aB4-T!Ak*RUY)I6d@0mbMzaddk)sa3yby;cv*_~sbgf;daGVX$!J zS5nGXV-p2(rx8o3-5<%cJX_vK50PO#EBSSP;Nv|rHD+<}4SJIbw=Mi#9-$%VCfvlz|A7K_>V z&1Awfw2}eRnki2^J&_BkB08>?GeYnZr=onI|3=vk?;hQPA}0%q6l@o;xf@`oyoS<; zx)a=%Jce98wMV*koIS6~;M6;$fS*^0YDcrTQtaF=UGZjhJl{C^p6(LI^%-{m6Ibb# zWZnIe&h&_^|G@;a({L#(rZcmejPZh*3H+h|u*^C4rluciOnm#bcr2)l&38K@N2%MS zq&=lF+;Fv9le^;^E1@*cSkIpby^}^)N7AAKN9Ig+t$~Yg?{QNE@{cYVU z4MW$t(%VuFbq#C4aP|U&DD>~&6;bFN(~T(9mEH=_b1WBR0{WWK$e0qvzb7wUdX>O& zze=Ip`)`%gO^5@A79=EeY!hACtZsQyx+KE?=x79_<>DdW7OJ!-TnlI*@u+C}1ibI~ z?Udh|{ITErag;5YlZ0x~mvO|S<%C*M<6Gr~V`=I$EViX_t+I|(=Oxt{m+zpva9VZ2 zE@bPskCID;52wMnCLS{#7_o(XzkPsC9Q1n+MmzqOPJDYG_G-6ex({02RBL6Ca&~^32@TaB2Id z`omEmpDAUbhCO>b?~Wd=R{dG(A0Yc%Le^mCeGbTBZ>KL(tbV88jpAMEy|HNtFn$cN zL-uwe`{f(33MQ4r-o>G(Gd`sZkp}N@P#jyIk6Y~D%gIb24qKR?SO-@x+rb?R%~L&3 z(L$E@;Tvmr9Xl2}KPb%kZo#aN=bqoqH6kk3Z zsP5ENu?-%2ki3u$29m?6_hIeS8*I2Plqem4n{nu!iI0_Y>g@^;`OEM2M2^OR_hxL? z@>Mlsl^7B5Nt$q;Qql$vY0XOwlN(Mb9(0rdwbv@IZW^&{%UU3<<@#)rdmlckPd=&^ zF>8Mm<_s(YNQiU*8pcK zpO|~B3b_<*d3jMdBV);(U$Qod6REus`PyJsS(mT-+FnX;R4p_4?bW-a=l=H?Ep!JP(W(&jTmoc>us>0Bia> zAZXsuaVgqT;?*9I>j5#o2u>9Pi{qs<@?d!PBYhD()G4L3KY}r82};J9ZpT6N-_&jK zQQ)N83|I7WW{bD4u3($TZ+{kG-q7-&CZRZACf4*57*<-%t6?i(wc7p9I3PFhYb0|w z8)~)n_q-7;;hp2_?l`K(dVbAB>-qcKOU^IANxg;0)_yV6unJZYK5;#-9vb)ruR6bS z(L%kA11_uiGCGn*edI2;`5l${eK(W8Df8Qw`Hj#b@GatYiC8gfP-wRvK-r0HDaKo| zZokzm;?h|6B&+!~zC)}#Xf>a#b0%BOBXr`5NF>%>XEndBpRS1<9qYc{YA)7K7_&c4 zv1A1Xs_4@S3_k=1ohPLfbd6)>9fE^d;2}lh@k&r01{qgby)MwhdGL-xw^}J9* zJukUVU7@0{Vf3?atUN_>fb2Dx=v>QcY<3@Pt?!NOF^TVr1!A3?nu z%S;$c0a2@e?^;i!U{?J6RDS&Y>uhH0WB)pU)D#_yaBal z`J~R!pH^xiu+Cavq<^jT!bRPwqxn?)l<2&EwGE_QLHnX^{annhywgp9UKx&CY8|L+ zo$CzR9WCiQ`)dI*p=@Ix?(=&gWiCAcUcW(%dJ>PyJBd@e;lvlghKTmkTUs_OCM+M< zN)jHWqlPWH@6)i|k(gMCHu<7_=ekz0ElRygC4BQ1sR42{P=Te~cJU=FNj-Vi^9NxR!OrdJ(%)YUl?ra>icOGxrL7~8 zuM@!&Nz9F3A?X34J0^BYPA;>G-iI$;kU% z94qNkfA9B=-qWLQ{Vl}$id}sLr-n=xSXAaGP(7`DhShQnL(|{yEcsgQdHrR{oz7?4 zyEZRUA`cb%!!aS@)L?S+BO1h$Qjou4QL?Sm1b?971}d%C1xQi!D1ASVVbI;3y(!=w7gbPwDS0<^7l9a*$eO{b{P*?KajMQ$QyU;`K8nC1=& zC4SN&A0|<|%xDItw;+VcSHHSDvm2sk+S8Yzqy=9=+ngM8G95_ha=P_+1~?skJT=;H z?-QF3pD24~dTTmrXZ&Y;$3V+8w(OlhQaT(tuBT=*+F$Ld2U)G z1V(QG0PCYBQpMldkyH75OQe{;cSIkPB~h4q*xy)nOLQPdqdXU-TEPq@sk`~>N!|&{ z!zG}+u<$HX>TjH%cTRLr4F=FUFIfiwj%D${syc0d;tKto+-be)Klaa z9D0KM&SzW7FUOh1ube>jp#Mg!J+boVI8qN@3u&~MJqF22WSBshds|y&y+) zVnJxD&bv(md?mdKB)^-j<~YAWldPXQB=T`_ImyJi_H9pDEV4SH*6UgV)WT!(R8!Cs z@zaYFBWLlqA~K4hH7vpsaYeEg~f}#22t$<_jXY0FJ^`Sr{8f&=I`t{B%7{Lk{Dn*;LLYa}{h~JL%0zMS@5 z5`I<3fu1y4knnyT$a(l(lm+dSs@H#+t9i>`IVA~7ayzz+TudPjgQ=?xA*b_cY9Z`> z7vCaqoYv${s@&R@jSd~%iJ|t^cyK$>3}6W z_=pP^nuDrmZlBmX!_(LvF-j~3Z;tyBz3rMM5chyNqhAA$C;^WYHLCb`S+9)EYVz*>1*8Mg&G>R$9XT60HA>D9i;I%trNQU<*k zmwsC5OWn9^h7=0Lt<+G7xflka|`|A;_-2N}YwMaX*a#ly()SA2*1R07KEP%%k6*#ztwO@)C z&ypwa)_Fl<{tgJmF5Z`()ip8g7#N7pb&@*MF>u|)v*c-Vpmpw~kXb{4aG7_bmAIE4 z2Cq?TBh+xyCb)>J=x#C1IT?Za65E~vqzDjk_EKZhgN`}YR1o4rPU z++cdUs61Wzkx%PAq9L=J%`%3fE0+r2Xs;zAm5GJ@NnOY}2C=@Y914hq5|mTAq%}x1 z`=@kitNDBEuXL~|-G#?AuyZ8 zFkQmFrPl?#?xua)+d*XM<2J-Pzr^&<3an3;&ZPPfDv`3tYh@OZ36XuC>9ZF&2X>p`mYr3S{jozVhGyP-_HC3-CB(&9`vSAj#*0dG;NSc9XrA@T%v`|3J>dQGV5 zfNWmtz7TnKaT(oCm%eu)E+b)?e_pDP%&oY>H8j<$bo zBW2}arRrF;nlfDX5X4gK4?Mv5C9s1i-5D86y5OA2d*-Pdx_wlEigRxq$GF7>Q_U z;fE z;lxReXCWWlJN7NY*~S*T^P(q*8rGThf@G|y5w+$s!pvBrXg$a+UAkd{-Y6$Sf5*B? zVh73>Y-p!NI+sIm5Q09LAVkNZhSecw2R&TF>PfwonXe;cQ!jwuseVLvB^vGfLWuOx z>^Y7oad{GG4&n8Mv?wU#{2%0&0v<3oM_;ATMhbCXz#+SVBgUH-?T4L?z{getvnBd| zrdxfaT3Q%VaE}zm?WWDp(kHE#Vn@;sItHRA)UZR}LW^GhsZ-x3fyQl2lRB=sw#vunvIAy$|bn#RgPnUj0p;YXf zK8p6IFXJ>vc>_ie6Be8)Gw~K^=^e38kr2Rg!X!e2)h~a+tlLHJn5yLLVy=Ee?J`$K z#2Q5|xb;JPo>d;lVBc)wqgL!>9q&Amy0vuBb1b7x2Su&*qmhuOOl**5#2@;Hh%=yI zq9>^6!|C+MDs>4znY}#u4|*>rJAnz~`qF-%HQ9z)Sxx6b0tW6nL4u8IcP+Iyr|bM} z5sMg+1P}1#@AYjx>}x!nAL+;6yyyUKboOmsmKXUM0sbtcy2VI!H)^TwMmah+D#tuw zt#OMe$7-VBBpM+e;7-8%yBn?Mzml!K*eo^@BIljK%})p449Ej@_^ZEqsPu&M#Ihb* zjlcYH^4+ZFg_J^MH9$MaoWn!9bQ-Uifw~&|?+|M<@(#&>hD;(R=xPKt_ zoxBgzz|UwvqOD^Od^DDS9)DZl=rFge*BfuLh*IkPu^V_L9ji|v1>B%!U9JTM=hPtM zu2@~;NuieGq!Z;KM+w$-RtNn_0;8A_dQ7xSxNwt>bqpk=#`TnJ>GiS865ioHva=>F&>}a zdI)vb)%k(=4M-5Q-=T0X@Np7g%ctWG&+-Buy$NG`4)e%Jzqo&eD%pgis~Z5Byoe|g z8iXdLVLcRkwZCGm-@9(!$;pp6s6agyciu@q2i_`7FX{^R;=QD+*QY31Q?O3VGLA z_Z889`n6KertX||_eH=bKW9E<%cBUC9aIQd4ft)tI@@nO&A4J~^5p|Zty%G5e({cg z+(m?-@^4lgKLb4M{B+v?KEfOEC?^ZA7CifHBRq|P^c|sBMmU%k`eV%z z2p#uK_UIf5lbZcdXC67CB34_T8Okb#vx?!E$EL5AG39gD_1@94o99%p7x|HWJe@hV zJ69w*%^s;B&hNJFfW1EZP0Z#_oGY8VTZV)_Kh%Wz+*;uy)PR24L&frGgODYO;;mPh z3DUf(`SNy=J5aF>eusJ>#0s5|EdlT0#qB9*>-kKUy?tN7AJm z;c{dce?{WV>I{v2EA<;W7pNgQpYqvtT&M@(>eMHLFH(r3WXG{q^Vxu?AEQ;(<>2zq zp_Y-1c^t_JdObTseX1}j zZS(24rl*!1%l;91s=3_982WvXhI^;4Z#GQ^q>5J|Ay5$yAqAv&ABE|Uk-s|6bI1?`#Bd?%au&})@ zSxhYjInN_J_D9WK@chQ}e>vr>o}$Q9wt(xqvPtYwd$4xrIFkve}V zb&}mWjBHLD)Csz+dk65KDH!2BCs2*c2U{Qk>?#|pi=!yXPsDW4beC zi6qB*zIpW2Q|Oix)}T{FgP=T4aw5ft60X9e+)}dli-}JST^=k+6-qR0E^jyzJ3>So zn!8&Z!tmX@lJ7DLM8w{$?t;-XG;LC>Gj&Alr+LZzh z=6lG`e$ssPG;MPTMo*N6;mgW$jdM*z z4Oh2CHIuVZZ3aPSf+v4YUdFO9D_oPhbNmfqJ4C8p1s>mXJ5vX*4UhAQo!#KN>Z_2n zSIL<-UM{o*Lo7K1(^-_o1bj%k^wTr-*)#x&dIdWbD@FJ*Uvqmq>F7Z3{^*FkzWTh#(W(}3 z%|*~X<&(1B)sf?==Nw7PhuJ+Qi@Im>i45xI{p^Uahwq5Emsk=a+~wOe&>oz{iz4Tp zxGr#vTPTDgR>B!FCYDsbsH@~Yk*u3OXVQ+?0Xb>^5jS6DIlpGQl1=8T+~^(g<;8rJ zne1SRp=i#fkH~^G4Z)FS&ZR1S(wyi)$D) zcLT?%nVAlpn*&g1{Ffes^uO2nBf4F&k7wg>&^mRFvPN&UMt>T)kw-3R$ZHY33PQB|J1*PC^`NXc#;c zvmB|@jsTZJeZfbPXQ;y1Z;T!*5KkGs%|vnj*(l@M&H8aA5*CLl=dU$-oi$oC;9_fL z>pdiPl>H&E!1j@WEA(}Ro5#HcUwWL5d~Wx(JZfbrjDSB8%2!7tN?5@$NW57P@Fu%p zGjIWW)rl|q_iYWF*byj%9Vu8r=4zZiUN$ZtkqjD3#2Fq0I*qL=iFFT~OWb%u35zVt z4s^J4d%4efjrd+vjald)9o2J6lVk5!T$QASG8O@l)%-7}Cvk~W{fL;Y!j(5iGZ{@} zfB=hQ6Aqt^LmjJGR7IN72kF1nEF%LszcdcX_OnA76CGaSMGYTE{r0PB2Sfr^@z?_l z1rR~h>jK#B;$c?&+->6#nFx{lJFMIMi3R1mXy-I)IG!4ePWHo8z^s<`M90yzi1ILn zcoBR_eMiU;nU(JGw@Y{cbuk%K@d~r%*OSa?8+;aDFEtfwL)*GT72RBrscCn7bG_@a z+abVnK`X_@vVjVJ3EH7weyvuna~?1`I9MnhD$TT)AmS9zAFz*-zhz?jcK1J|+P;}H zdS{wp)kxL~tQp`7_K%iI5hQj*rfWg_rQ+?3#ot!wZ*|YA9vc~)Ud7e2u>DP6eZiE@ z{ZLaDzSgJ8$MT6)7esguwE`YOvqQI&E{*8QIU!|na6*Q!&YX}ub3!6VznSa3d7PB` ziqD)CtaGz$f57aqMj}U>$LsWDMn}XZyp5)6 z+6ov;^U{*s((KSVKQ9-zHTd={v9F-w2I>Wb6oVs!r4?ei_{M(1miIJLMvY~a zqRYpJnvpJjozca*?~2aHjMu}on<8R;S_D|Q0kFu7#PmE_FHhniFEhjfM``yXYvY?&=SR;u%i*MVY@Cg#q{0=*V=<= zU8sQ=2iv=X61*}UpZS4Y`W~Z>!b*N@I$ouccjpeXweaU-(~w`Szaix<7!`0jtk}b( zL&9=)qsejA7YEbAFvR|O!W8?y{hDgOd2d}?;4Jqt@YF59Ho@AY7vFxg`X6ehfmYps zpy=dvybjpA#A(C@B$pwfIZtZm+Im}{?Uu8uz*E2l^FQ%#J%p;kOM%u~>(h8dPH#XD zc(s<>E{nZ?IBb8Gs?R(>ye;MTqMJ2wJ16D#ts)B$PA7VtvUI~DURUKh$u*@Txf7Wr zH~3hRqAsl7K%IhNHGY1Oa@(ia&3Z@#UaItEAMjG(Zx_w7y@5(M49$O$YztQo@Q+(> zHRD+iYQj+G{?Bznbnvj9|3O$Cw5AVq3h2WGP= zgZ>9ax}`|9zwuCB^o;CF%8ZjTBPerBPZ?8SKSia?xa?=aEa&ZjD2x}KrOg?TYn*5n zQ7dyCYMA~uYp!*HQ2eX8D7NnX50aqa=w}Dh0q1)(s@XspbfeXLE-&d#zSVpm+ko$d z(#-_}<*KpL?Mf9i-cad2P}8u6OU_#RUo}qLB?grac&K?oBk*hsI>sX(5yeFRxHXYt z(Q{kPC24XKIq)$GH>hE+ns;7clMxFdMAdF0RNk)e11ln0eXPI-J zN-|^Ig^ZYw4@(~`*Qr_63*U}h&$u4_^kBO8phA&j8yD6MjNm->#B9xN!MfJKSp(2N z^!_7SD6gXh>zo5q6)YIjl6YEFzxo`<8@!~}$EWRSPL5f|{ zGmkzqU-^VmU@r9V1NQsDihnjPtOwn0V`k;HSI2!qpKq_T5tcB$I{6m{uTfyiihbo; zd^EkAdz2{rt+JLoCssY{5H2}}4IC+1*?8njrp_Gy%cnomZlKHWidO?=7_ zmT`M(X79d*B3_zmWnB}EcER8_XLe2`H*i+9guVS*?l`HbVtZ-`^Rw^G%nUWWteTKT z*uf95M4Rfb_{TOpR$>jh4n_)f8!bc>8#XQk29L~K2t-TMr4RAxV5}Zl1TLq$FLD@o zSKBjpBA0k~w`N53*Y{aJ=?XeOh%cxNC4UD@%gZZeb4H41`KyQh+XY##7vznU$w=U> zr-P1Jv2NBRJcMJG-@7JKG7GfL=`JfTDYgl$%X$nG4$HzkmW7!=tGXmQfY+|%pJ-Or z=HL0v&Q@k^ZZ~UFP7J9jvp6q?X5qu&yiu`(J$*saKC~R`vAl z5g!uV6WfAdDYFk`skQ}o^m?q$e$m!xmE%%jS;Zx}@=+9LhosKuQ-!)#$yopbbgI|) zV7@97Nb7gex^8J$swkT`OY&$}CWUofZ{01145?AsmIOcK)^!mgXQtFlp;_8|Kv@R0 z-Y+9~BG{~MpUUSwaM*7J-iEKdI$ioGlM?GL0AF9XAbS$2h)#m39Z5fgOsxCXsLOsQ z^*7eXoxDnpA(NB(ST%(w&Y1F^q9g7%v_Nc4U5_O7I})2xWT`@nY342`S`(?GmZ*M| zkv$%x_WqFC-@^0iOC&Oko;W!|I>^~5;z;j=eiP(ud{8FN-W9g@)o7M)!ah!8f9wNv ziarqO>OMAP*rS=g?{T5igcKDG^PhIG3Wll&sGUY;>qg{!Oo_m9?JUEt@0++c;K|8g zC79IFl!ntSRp;fprKadQ46{D=Cc))QioB;{s|U;t)05-WQ3zm3BdW~09Kq)87C5B!FCNdmGfXuEk%&sV zD3`+AC?hI))%(q&5~HtB`8EBhZ<2opddt75KIU;KPp*5t<=-^cSN=`bH_1x0{F~AC zl9h=38^V{l7g-5&kNo@2>?EiyBx%@8{&Gq(`N+Rbu6nQsuuY^n-!3Q7{A0!?gG_&= zefsEgg-!cZQ<*PgInp@J6HMHh2W^gR77C0+(sI7j==DUp8@<;@&TRC~jhxu%y#vw7 zvAS7VO=<|ua;<(oSm=d3_{3Dv*tEZJcgp>O*fhjm!cQ6ofNw~o3#(atVIf@g2||T4 zrM&h0`q=&}7T+29ek|!BU>I4kWMO17^wu&)F+Hb_R zlq7#8#aHg`pL~RbZ*Lb8UC{@NZSf`LH^^4Li|qVpB)02t?8AexE%~Aen!1&WqV>8G zPpUqaoEE8#B?saAV?*RrX?s{~e?P0Gj7_w!%h7?*=p27xck28gdWfg-`K*;+4oaOW zPs4fg7pm0o3|tYtu~LJqmAi`OxyHVna`9Zm@b%bR22x>AsJP{HF6GQlE%#J&#t5$| zas#;`|AXd?t(N~}F@O)9J)n)=3E1nB-BY%-N|!CrWjCsRhGD~7>T0wKcOR9ig_Rwt zL4+(7y^i;9XY!$40C58{ICUBzgEUD03`mCtv06$^5BdXE(z_Q9Vjl_PhrE0??lHJ) zg4x#$8=qIrf>)MPXBQxWya{KHaymv{5vr6R8Xh-~_;qf1oiB%j@rFt<{ zn*+WOpINA$%JqiEr9Jqfa_n1)Dfoo`J91fg3hvXq6V5>PWhFkAem9`dQ!ofA1ga&d zJ-3lH-U#q5|J@hj+;hWb9 zbw6S?ImpvKmpa5P?p9u1h`2e0C2hsZ4>(e&cox){x7|`UUHa=|B$D%pa|Fa5 zejwIR1}F1YWCYhcGAgYl7Hg0>J&}J6qp_SEp4MVb)%ccl3*x-5f|eMlAMszW`07m` zZ-)x69otbmmN3CLNMIN!D)spL%yH8ic_$(%{+nf;5Fk(ZL17Hb0>G8jbai?#E>F2L zFiU!Xo0%z7Sj)`ij5L0Eu;C-v^?tCW_(vMj{PLZa>A!-fkG;Pyb(Qv->}%eSI-|An zy#ezEs+aVu1F7+B@I1n-kv(vprR{svU-Y}fDUWP^yk+vPaNZCeN)Y04QQ%MyXoy2P zYbfvkSv0SBg-6~*^_$)Lc#vo@T4JkxpJ(Oj_L?mB8%oSxj~GsCFrGT6oiBG?8UdMH zm<4x=E46q1&vm4cF*lw7*0W#3S)=%A{3-j}uB{hsl`4n@-{)2dvL8{*7ilL9xz?Q~ zH#(nvIw|&CBOS|+4#pOO_tB-Xo$rcTbMOf~RMg8=k-oFS{Q;i|hZS>VTjI-nvt)SG z!y?g{@Dq{XMeDhlnla1z0Wc`VR9Qo@BYusI6w)e9)-{{0YdSu&2IFm>zk%{PK&jni z2Fhovx2a2N52-himeRTY?1=_4KeMjked<^qMgFF1r)u>_9`x`yZ)}GIeQLl72_^WrJ;5XXQbPo;mRKI$um3z zyoePNl6#Mw!o28r`}O?o9~sNv0Y;~VL9;sPZ)Ra;#qa^#{|dSn$$e5P`8Gf!$rn&9 zwZHM3;6_UGdcUV8eAD|;yhja0DgpO6Ql7p*Ncl&Lq)Z_6AxFkkxepLYexlfWl8E}z z30gUV;%fXsnJ+Epujja#ujT7U+++CdZL_$@;NR9rea~ga!<3%YbDO~z^ljyjzR8d= z1V;MWGvi?6^xS3u!$#+C<{m>}vt3PX?hWbJ*HwMtW&GK}hq(G&!ZR@11`gXz)y075 zs~%D2=7R6evMs1Zo!Gwu6%dd#bE8TjKp1ira=gr)rLWfBCKiPDn^>nn6b)1U+l25O zMj17(Nx+2$N6;1dP;k6Q3!O!3$W;^WS=OsVtWN0h$m`y$o{q_e)nXP_$*d}a-1lmzWXnc^^IY?MvpnCT#{a74tFd{L+V_~fWU2m4 zFNPZmo@OOhU+u!Tf2^}aHa{^pL(WxVhNF&noL3-V`4YwEdUvforaZ;=Kk18d-JwtT z$r`rSr%U@;LZqsi7?NaBvX*P2T6}>H^e1Ya>hU>DFUO;sU?3lFgM5!2a7PBm4irX; zVh3E2{-RemIyEM2Kfg?d$f_S5iv3Q~4j`jL0`~GJNO#>Me`OX#50+z1URK-1J4SM; zxDC|du?x15cCPnsTXHr!!2uKVkK$7&hCQVYa&FO!Fa zYl7sSEktCNr7^gOMa1ITftq>`4)$PMFq!zX=*Ii_NevR~6!t@YdaJN#A@4hL@wsz^ z-atm@Au-|vQ?Bg)P|ncfw2y*i?KK=_)$WK>ZkiiH<54{t+tE}#H3%6~w6YLlVOI5J zWl+7Juig}H@Q-J^gmDky>_qV98;TWsUB8PHVMiID z;R^R{jqS|S>Jzby4M6P7@9AmVj&WDd%7^F7l;iDmN-(0$wx!<54lO!+)Rx3#|TQC-s;qw7-_a+pvB^YEBI;q%10kGaE5HHnFyR1rZgkGW$IwIn9) z>{AD3&`k6zb46QLe;;_1W5&r*1}VV@v9iPv0R^2|L!}h2HCU(+gC&0dl>d{GE1rtd##i%|$+dt1ikT$}sU7@>^F`-WdV+RgJkLoHY z%R&AO$keN`0|!xRg#Pn%6&yU?)MEQwsb@?j2vF#6Z0UL`KZHmE(vMhNOheX9EO^1k zKu7G!>=s#k_Gb$Dh=d8@);o%8qRlD~YBxEI^@7UDyKG(?ac`=|Uh98Wx?Npm%tigq zO*PJsxlpN$?Jt;{Y)Y#`a5~N6KDj9^>g87RKESP~@f5bPpS~n%{wEo-FFoQuY;z`4 zPHvXfa8x4O1+%D`;}LojRpHtqu?+E8_sDT1O!&!FGs;hnoL*Iz7a46Wud-4nu<-)FX>o*Raq2+my3>HahM7k-AG#3=y&!duBPN~_#z_t z#5+PPSsbyDOu9to-tGd0q=vK~B|k;&E>0`L3Jm5ceo;{%K8rAp@oU|o#G(=~-HLwH z44O;6!*BJ7o3XgCTG~0Bge-VXce~fXn!U2p3cRT$%`+2A#0p zUsV=4tLo~!$QWz+U~9QQ&%5=GV?YUKkMOrS{mxhY?mWq@b#C%*y`8!5%&$5ox}_%8 z;jVSQT~qOK^cm)@X}1hbdWv7H-7XyQ>@JQJVm){4GiGSdn4vvmhW3mMEnM+B(=7oR zrX&`e_QgsW7FHD4PH4-5nu=HP9K+I>`;`cyWwBO?q~VJ5WCO5^0hi@w#0+AR#u+F$ zn-gRXczZsaN4-be-&NZC@g9&?3m%c8dsGRFEoA>fb{#b3^E_c!c}q^y0PhNC(v%ilv2Yl!UD}$Dkuy^umR11KmBOU9pb}iR zC+(IY6_=AXLwfTZ8PbR$`lXXT?^p-1TOqePCo>NlzY@T=MdfCr`ZKWVPxV>4I8`u~ z$=j`ihUa9O5RX&EmZ?s&ecI*_8Y4F0Q^+g_DoadwS`A>`cNM%v7TuI{R!9lCFvV9j z`*^x>-7tsCIV`PrstabJKd*-;%9hi2##YKLZQi0`;Y4t!J~UYnpK2C&`c5I)?)LGX zRXHD01oV+`xq9by$-3ZR>Z{#}v_N{pqN**i z{Vo$jAaj~~c!??(4GR^Z$>GaTO=rk|{0~EiT+VdGP!n7p^h5?&%WFq=?sOM+X8WHD z+bcdJ=5Q*a-rrzO#!Z`I6HYot=A0lnFUXPWjCFb-{s9=PCW5E9)!uFWg5gBh{SpCctFH|?8 z6^F``)N-^$G82ySlh3j>qa z!Gw-jt;H%t34x--wMVS08-kM`%D;|eAZ+(n6K|GDj1Eq(_A)&S-lN8Y4yRSx+m_|dmbPQ9dbyuBRcRj@{>)t^GuYk7$PLcMs?=}Oo3oA!7kwh zjw0H|m&3$O;%3%o#Xca{*O)%T7+WN!`%tPwby8689l9~Jb^!TWLzhTH!Wcp^OFgmx zsOLJXS(MGe>%PSF3r$_>3|$d^M*8-4fT<)udOekt%=?b787p>b852dur$=pS(DR^n zU7`nnf&gG*%i;sxYVIUa!@A8w8_2~n|5_`*zE!$v?ofTbJz`?Asj&-Y=Flv??xZA6 zw{~W)Ubsn%b*{sb-zT30<+|dAqvT>wcC7Xmni0wW5{wi|#2U%Y0{&tPda_>woG*Yd z9drdVHR%BSM&Fs)jf_Ork?B{CCo-y-etpwRT%{G$*_eh%Qpfy3xFTxSvYipqLcI+3 zn;2H^I9$(Q_>{Sy474uf^!5-8Wp>yuZY^Qq2p(ahH4cUeN1By!jQt#kB_!G`Ai{|| zW^?F#2LLVadPxnRtx-*jpgp2T*Cei`#vy9tY|7x0RNsWQbifDemURn33)j?%f@7%! zRhKf|QmtL9oA)S*w*%c$M{Bj-)Pe`mZ;C>;URozIG0?#fd-WcxGtap?3RQk-ox17_ znb^to!ekDyH}cNy+^FBxD=+WD@x}H0n;MGWQ5TBe777t!{xuelto7q`*>A1-;OPQ( zDOLAvwqEVj@9u5o!)IIIw~l|IrY+gzh z&JpnDN1|42#_uQ5c;TezC4s7&@}eO==#MPXq!!GlR^Oo4{s`e@+^F%O%G2Ny>7+9d zKUpU4a!U6e9k@uH2jdU=+oJYUng}VO2{A@7Z!}v))OUw)1Pe0yjUJkG4DIg27&1zx z;56Z4rjp50R?DINgrrg$7)iUZykG_()KPco)z-X8zvK85649pt5NEhnN~>|LG3*fv zuj<0cpqi$Gx-SK__CXv5B_<4lbP3E4DPtdO9I5Hp6C2gL zW0-p;VTj!A`Ha%3te_3#Bg4$h-MyIVabzHJU7I@NYrA{!_hgd#?)d}klubhG-dDa; zMg_ThLj{dBt&ydX)40*`j=>9keJ4mfMwSt~g+Uek;l0c<(hihBXyJzUWa->~zig_o zSViMSOt1xW8t}Kv%!q^qx@olRO^=!(c#1KMkuD`1vUcdv%cqeG0X|!rX<8$#_39a( z+)=O8(%gfg&iA=gpXLpzyY4Z(unl>d7SPT*1nykI!W1+7uVU}z{S1_VHEfuLr)VctjsvI;TD`>U! zo&{2m7M_;Vgs9jXf<(qgst%w#G46U=6;P3Af5h;V+rk~M1NJtc#rAQ+&e`|>oYC6N zuP@-Ida^4rGL(4;#qZOZsMGop^4RM`5aA16OPv6{5jnP|VQUZIJyJLej|ii{YH5>( zCJ#k(_b%wTD!MA7^-4y>ew>Tojwg_D5Sxo><#(f+F*YOGS@%6zsgZH=Zbh1MvqoYJ z+L2$A-W27syY!wfSObY#u)MIN>J6kc+PdSR&iMU}bR*S2_B;8dbzD|%iR-@E_efU! zUVVwn%_7&qOaczHrX*a8p)0N*C>VcBdgv;eMrU2$8p-*K7EN>HH^~rRW0nAP;N}?^K7s3pY#j z4uwx|UGfE~rtg+n59a8DOW6WjDa01|?{_j=z=Yz`hx&fhk3F)f2dZ}PLUODujD}T+ z%UP=dcPNP>DfZ`q#H>7^tmZ*HLW_m17Qar-&zpqaxk=_4$~$GS1QmVnyyXET}(rxM1-)vWK|8gQ8n& zklU|=x($??Sz&&`^IKX2!z&xw999r%=W)GTYiUAN1o6nLbwE2_f%SsIP!!+T~nV4>EnRQ)El2e_>aVVP^j8 zX?>cA&K6c*wg|2s z@gnIOJ2sjjgyID^yse*n=4sd-2xMCaPKg)f!K;~OyOtvnKx(i>JxD|HdwMeij{RC% z1Yc>DG0y%>)UVl^HrH1yS^7Lj2Ma3|oA9Al(I`;w?Uw3Bq>;6V#OAH&aUu+p^*Vk6 zE4oYApzqHBCNaaACu)1qU5UwgE8PHuFy*=KF)7~7Frw5TyP$*G{z6*z0Ug??#!wnP zy~Lcn$QhxAf9a1dd#_xI#`{Z|d75h?c*c6w_8oorPtjat0~>0)@OV#C=?rOr`W`=# zai$OBk#w-w zzmfmU+6#x_3hMEvPPhSo>_$OSPzF2vT_P6JOSFGDjuQJC_t;Mwy`!Ro(U|(gw|tc7 zOQl!a3U|P1T-V9{TAQ=AKPKze^0D$}vpNaA0j7e;KSu)a7C$kO<6t}ggmV>-_KGtg z`b()~z`Dt}smGjS3*4; z4z%~utJ9s0-Wd_<7!^IQ`Nil6XtY7FZCHbhq8haOaEUbbWiZ)89rm7W$<*NV25G9(`*+i4d&Hw+u@|4PTm&y5R%X_L5k9ug zpi_u`2xm>?urf7@QToVkklU~o^i=)>YkfLzMu><|aIE{&^De^|{HG!Bwz(|Q7uBQx zB}-1*K4Js!(ln*1m(JvBy$LKP)Paw#9N=2T&nNE)#7Cq^=YlF{_P<{A-!4b-rAxP= z;Urv7MkC|*vUct5IGe;jH&?9rq)v?P&80pTiC`55C-Pev+J+l2Rzm>YQ|t|e=+zR# zbZNfKn6@~)O!GiLlaUEa9r5cLba5#LzaZKi+*cZUIbGUCk2sK!2X0myPk^MczW~Xl zOV1=9?3UsMPa}CUo1CF$qo0QO;DYab;_|K!&u^q^s8Xu)?$+F-IqZ?k2)4~B0aRBb zpG@4{A@hI^fZY6loPEYYg^Tx2u3_O|t!81|!d&g5J!3Tod8iR9isEp*#?3X`K^>lh zth}gO5Gi(twDZShQgi&T@)Nt$Ma;>)q*hi+t{qz{KbShB?~~shYWk{!=LNf(s_%C= zI_zhj;3;Z##pG8QjpT3n(^S2Lm)Yzl$p)Hi3?m2{YoMFSN!`^{{o`JFEs{5@sTv)% z9NgWKcX?AaEMzY49?83)sTzS)E>CFf)M-uCf6MR5qaH_USX1>=u?7&2E;Y&^^uIDBBD$JMps+ICpVQX`D$?X!o$-5ppdVSKP!B%e) zJ8V%t(RXF$rrR#q_bS^H3b)b`dg1I}y>t2_70f9iBZ@-%Xs2K&uT!^ciY{Gx#V0cD zToR^BpGb)+-S(QXZ>39zlATR2C10ScvKjwLhQx=h3PWN+0?2>loVY#IJ~q=_*!xb{ z3b02;e4o~8_Ps)CwscExTM!bOU9pq=13@mUwLeT>Y(jtTE!i?Vq9xGWKmJF)2+Zr` zZvRSYU2Y`TT_T$Z+9fL!N8rwrk+^SEi)ZRiwnvc z>>6-p`ygxJ0f<4f2HZV!uqyn`b09<*J@{d!2YuIo``MMj@&rS2MEjgbUchDTClod zC@yn2KKohA`(vjTsJgNHh|3?nsZ)QlmRoY)qjoYf!LR?xuUy~+?6`U{yinIy`g^@XpI=|La!RqObs{v&|R>DI|Vs;?2s#6`rBQ4C|V&Z z^*b1DIJcyjteEeg(LGTAxH$8T(PiN>rBycbL{`GpCpj8>EhJddjNXBqcoKH#YdQ=M z{eP@U<5SJv>1B|O8eZ`8E#NS%!q4r_elB77x*g~xsR|kj6C6dp7?#_3>k<6RA1p3z zDEELsYZZfqwpR|c(B%=52IbdlZszb~<2jn&jGiw?WohHW?fNpXc57yI_Nr8E44=y9 zX5R$2X08d_W#^|V_pZ*1zQ1>Meq@M-*uu4879zFkYlUR7)^>{w?h;J-wJHm>pvvOO zsj-;zp;x2H;(QAQ7ul1uH6fD}l8-&?u*h#XAG7W%CIEG49+2R?4}~QHfc4V^wwJ%fqlV zXGQz@(|%X#n2=aJ?GTilxB`#$8^Z}-o{BT5ECrztT1^LX7GBdYY;UkHcA=rSx?MJs ze`Ok*u37$01rj@PyPZEPc;c$2chP`H?~HwhLflpmUYXzfa-?vjV?3|{=c-h>zdgN} z!3<|Uioq!Z{3E$W9+{WAY-L^shUi%K*3Nee{hc3`1`4+Y3Sa5mS?G`FrAGSg!IKiM zqwE(ulP3gh-fZQGH;@s!`i@}yGD5DueCO-4f`we3ah*0R7{6K^-T1_^CX~3PT;lz< z(m{@hqD|TLh%oT0P`p;I$oW^hk8s|gZIuSOupRcnmxPFbifJ&*w6?!LF?BFdyJp3M zyVWabwpq{D4&e5_Fm72d)b{5lDo!kBB1BJiD;J2HVV=Ns52p%3fZ9`J^9+XhLR=*s zx$au*Q@$psiXb@>nWEO)Pre5h&E3OgWJhJa;zIbWr{_-OD}6U0h=r-uB=e|3h8erD zn+@Z4`&XPrW5LAtpyH;(8`bX7rlGGHUh9#SvJNgS2_|ZBwgAD@0|1)&p?O=z+q3(; zE!?hV@|HJar3ka0nC$6Y^M&RuLcq*hVsT=UnrU~Wd3$c|ZDsGbPV@G>p3Z2jLYqlg zLfSl4x998q9+FONG;gs!%(NNmUF)CB+lt)VslDGmz*}8w9rcgtTl0UIvZrf^`o3?k zFmKPu)jYd*&F7l8XXf5Q6Zg!-(dO->+*{Nkd*1G6iPIn4*=9yGzxUg>%-dl0?Y)hB z>fQh=4ZuY?09kmw0Uj{`lXC!qmU;usHvnANm=4_ga36rH0FYU^cPTIEYK=hluHigW z!^OE8mi4Kj)BsG$0X)$M;0wf&)Ev$MhlY4lkY8c0N^Py(}W21dIS8y09=p*kb9Qi0C59wVGaPJgd+fE8vwoabZ5)@ z08BOj1jESm!P5ueWCJiU2Ov`1-hC)A0H@>tD*FI@@HagV+)8DdsOkf-8UWonp#e3b zW7>`vaJ+JZgVe7~4JYJk2mxr`8NAp8fV{*jSc$`->zdjJu?|0mz~3dmruta5Dh9gR^)z^=dQ8gf|t-i|&1>kNQK;kvWy`vCmb0Gyud!`40k%?2Qc z&hK5?2cW?K^eo}+eE=pJfE-f4cV{1fkpSqCc@ObtD#eNUen-g#2b zLl0{2?gQ|=0q8;P4kQk}Kh~oLpa-=}u=?Hr3k-n9=(@9V-`*SGI|iT!wU_k)7-s-_ zP`jrOz%d4(2epsx1JL~!J9i2>+A z?c#F1_b?*{pa->2?E`Ry0q8;Pb$tNNHUJu<>yFm<0T^rmdQiJ)IP~tr9(00PB0Z>m zP9K0R2A~JE&+h~9X9LiK+8g@-Bn*I$W_2H1d7qn1!J4-ifF9Joq)!b~48X~`o%L`Z zfYS^>4{Bf92Vj5!7?G=C834Tzv-%%B4;q>4UOv$WpbG#g-3xQK_W}70AcZIyY8|z# z@I}kNO@J~jrTzf+I;&HP|7*9p-A%>zloEBTNtCG@Ov0nSV-lm)usGb#d z7JdV?k=ouBs|Qj8bL;u{kbQ3*4Yq+&1)X+p@jR3Bs>$K@t@wK;XO+pRBj+)b^MuK%C+8<7=NFQrXQI!& zeQyI~p9r%)4aGz2v2xj3ef99@(bZR%MRCcU3g9(5ak|2YAi11Tyzns7V)Q+1EW$Ub zMT?}LqLoDOTYF7XmN51#qxBEEL0zhdd3r%#M{H4B7b0ELSDv^NLdU^D?^5gIW!2XX zx0*|InJr3etBuBN;E&o03Cu-dp0fvULfB`@} zWNlYJ0U(SyXQq5f?p+~QQ46Rc0)VMkPMlz!0Zd%>g>FMA{mV6k)T$v65J)qL%RbLR zR02`BT0$wjq(&oN^2atYD=#Szl5p(TlS2@Q*VsoBv14#s{`krTgU44dE*?|aimR(v ztB#GPQkRYQMn5O0yT8qs=Wmq=>S&UDmUV)QzOW z@jpwsaD2IjkN;WTNTfY3+7S*{;u;vY!1GAIpHtLatg{U+xDlQV~$W|Onfs0&HR_&&(?Nhs0v{qTH zk_EG{FDf8af?B=fbpiWG0x0u+f6kqm1hmi7=da)Q_51yJY3AN@&+<9ve9k$a^I7FI zCQVwZoAM)v9d2{McLRbMwu zdM=DbRs)71(vc3<+_+?piMn!up2}e*bHM3K{8kp-2q~`aiqeuSDYWHS*s|!}P&tap z5yX&Lk?(wu^F6h2Ur;$_lOu1hlvi8}@;sBS<5894@^lV$86wBi=^Ts6p*pC%p4j!w z=4>TT{o(ZYE7ISu?)QB$-|e~Xn}=H%p%2Mp&%+#*Lrrvg zrWTOnqjZi6l|$qoeRV7$M<2!vRyq35c0*|$`N_lqlWgdNCj=Ck&QvuGrLx?}^p|v| z4l?EK)z`_K`eO~sw~ZRu{?q(7ctB5qMS%=7Tw!0u`vcW960m!{u*yo#r@)K_1jyt7*;bNlAXJ6m?@ zeZIWU*E_Hzwly$aDJ^C4l#uw}?cZw8e&)D9qc(T*w>-0dOS$C!9fwa(l z{ab_-N*~i)E!Y=c1(RwsGeMWt?z!tGREr@hO z{`K<@M}4vbl#4&0N)L;>+GjvXwV0o9El%t(N5dsh74exbwpEk)Dj!v)J|nA?=*w-s zo_!L!I74K*il$TKoYK6;!4F3nYZh~TUk|!t9g8&c$umVpoe#)(WS!^JRv%ScCHitc z&C?4x7D;q3=IATfyeAV^JtCb>6kWDS4Rd%>WJ*Ohgkfl;Y?dw8D{;?ekx%TKOpV}po(1qV>5bP41Ch~>aMEnpP_qhtB#(n;38^!l z1`Q>IN*g}&hkS&{MSrgg?TAjZiub6~devN#`tXGPp+tQU<+2w}?UR^-(W}Fv`=Ew> z2E+bOzq^@XKc8ahVV~mHRzpvBg*y$cU5iT${l}aY z#YF!`G|}bc>Nby`E$8;UZas4EFm&VpLJi0!D^=WtT6fFcGsQG$2>2T+P^clgfC*N) z`O*X#3(>|fpXE_3Zw*^r;802#0;`%jzSGP3SeHVh9SLE7ln9?~)>9&O{dh#z_g*N` z>X)PQaTDvZzs|=T-ie|p`Y9da5=Na1Aho}x{9Gt{G4`j@ase6x6m})Z z{4<|QO1T?58CAJl?vqV+fo!t3fKGL2pahMh?6x=JJb<6MMlXM5`xW<`*YW?AW|Ymy ztQ!Z_m!`RqqgK#y!o9GPB>ZCt^t z|M3}d4u301#y;e11XyfWAYh3V8k$IOhtPyT4OfSPKao+qLqxh+oJX^Rw>8fVIvctq zWCPldMk`sOWMK*_SBB4)(x*%5kv=hXYK^>=T++Md`kfE8NU0;!D(gaDG1r!)!G7ke zUk)}gcH^ahE#1Y!?iSg&7P@#;scZ5N@$G5S{b1x8WVF;T>E@_>6gL^%op6 z?D&3>;YH88=>@(>VD#?Ea~BUGqV2|M+Vl6jy06N1L=K{!p&OfsZ^4e%?5}CYThb-k z^9=(XS7$p!vqC0eKK5BIt>F?-TQ@|%Lu;r3vIH8m_q*)h3zEP8Os2n?{QY_Iw*e5y z-&iW3070bpM(u$`V1w3hC(uTEb*-V2*GTVlt>INBCerJ*y`4mQXJ`%Lej(C(MJN>M zovSsxrrxf_4!-vWtwBP_MS5$rhCfj(UdH9ZZ{+wR9WEh3&hr_#!eE`AscoR)_5a9Q zG~`jN%dVu5qpWpjZ~O?C3>nKS;~%K&#+Yh!Vg)U#F@lS~Di@lbm&@rM3ke6yd46Lo zAv28*gypy)_u!*sUHmN?Jf)_-lA*KEYPqZz<&S2$ zu=|1>EWJ>JOg^0$Nqr*Fb6A?bYDO<_K6z{Ml6hWpyb>5mTOw(C9m?{Nt!&csnswu! zSi43~XMFkWVdNe0>(pT1q}?yds*&ECSL}-Pe#ed{5$V0tb{HJ#z020ti}WrC)kJz1 zX~KTRZ9+Kd?5%eRmV{Ka_AG&EcA^s<^TD{Zz$*&3yhFaS&Ay#Str7-82H+4BDC246 zCOSQpgT*eOg%D(MX3>tFOUgkfOHiq zoP`xox250oD z_X4ylm%98t1XEf6p-XDbRkbd3dX47B#L^t)R?f=PBmd~p-N#Y1Iu6ZnqqwGv{X)L^ zdiIX)ny`tn37{pvy|HU$wkT|lt=L5#KLkV1VKnw*)5;^gRVy-`?c&*Mv-JvZrx zkaL7MwyK^vb>I{k1@KW22lD;IpXB@8{@?4|li>BqTreQAs)eSN!D3~}_;{)aKdv=i zOV`x=oiDTHT9hLwH;HLF@^VI~P+rQ^#4#WNYcQ#qA(f@XGEtWec$9+{s$JkIyOB!E z-XlwXc!)A7p^tF)o+&YZcL(EUcpq1(vdnSp(vDpSaFm#*gS-@Sp{{o1<*bv7mYcUz zl!kn2Fs=;U+?ZHVLySE1tp@pGKW>(Zi3OjuEqDpbNrYu{T~Bf~gwHePJjvlPQMHZw zIqu1ng?vnvQ>{Oq%$x}S$(pDeM?8=`)zFJBO5qvh(e8ixc%CgCH=g#>=7Bc;Qic0iNY@ z1m&>nFiM`vIqHB9KA6%kh4_vMWg%*97gN31WfO%??6QRdNwfPPCczhfXbOLC37yN| z+a(6iXN$uR3X0@e(YlLo#}gOU%e>~t7t4Vc*;*LC(|#z43$eK>Q@(1GzZM==cTdIz z;Xx7zS|!Ehgq&QV(!ZKc|5S^gE9p0Kp2;uU?dkH%COcey*;NP0?-H$Hfc$d6oq&9j zGig2t0&0xUZ&3%7`IFsH|3?^&G8r`no9n5*YKuAGHbE>I$5rD~=e{BdWZ|Ejbw6u2 ziT?3=TA}yngiCaL2bJY6C&o(oV4M&n@WdDL63gEphjwh4Z-?ZGe@kUJCQy0em#3;3 zt=k(X7&R9fAzCRPB3rY1_F5VfJU5n4U#P`HExg8y?SdsC9VjLcRk7}~ve0<`PQr4o z?z3_%Yp|4}23Pz;`yng-j{T4k-!Bi$Lr#3Bos=2xW#c# z8Gnq7=7l5DB-puJW~wX0kHqgif|6q`+Yo0N1h`RfKkNhSyiSDk&F8L{u~d9lvHU{n zA}XKO88(?NOA3zNmEsA3L1!Bpi~}U5YM>NQ%lYLq)W|32S9kq-69oT%!`);|*?F_GQ=ngvUsU0|GpG zvp?A&h`~5T-UXcBbr*d68gPCGOAdrN7SF^3jSz&Ch-ngUo7O%jjF^Ph_5yBkf=C)u zMk{d7>Id%VF9Ek=SE6X)8U^s@bT!|}T^fkX`XH|UGKf>WlH`59$Q)(oGCvuX>Z%n4 z^z+>X7YOKQ`)YRjVg=<2^!}Fs?)5E4JcU4xp<`Ad5n*>w10!TUnXbwF6`O=lhD^T>2QDfbq7! zaN_v@5z9<<2UI=&I&hqP?U5c+O<^Hkg`bv@7d%(75k!r)IBk2*0@;R5Poac$%k|E&~ zCtA+L^yDB705~r;yf;ezM9~4VO4S&ssrL@vEe2J)wU+m?8D>yzJM5`cU}o4;#0_a~hdY%4xAB{7 zrR?>d6L@!%%*(|MxyBK=XjT^ z>OJy7Qf5d>tf)!lz*|-Jn`qyI-;ylf&$7SIPJeG#-@8>Ke9y7J7pA}eS$%I#eSeP! z0w7ZHZ}}iIFrMbR_6b1S5=D=s@@^>Z|__4|G;{&FaUSJi! zDIAwfg_6msq&(YtP@uS3ppY&J z>slNlv}kJz#RpC;z5Xv>S&PIFPISs%{XdQQY5h7htY3%zossP>NR4bO1J|K=;GB#l zk)xR_3L;0H;o(O1bqM{faYpu){6-5dmA9eh?_6Z+kB zZpF(AG7dyJc~%Z!JWl{L_4q?nCDHKjoAt>&6WT z;J_Puro*`f|6xqS5{50g^tBGh8*k8Hq>boU;v<2KO$6RS=3#@BDEj4}6^UC#q#ixe zl@Pux(r=Xk%5GU{IiTk83kRK}!5YZkj(Y(fiTgxRL=v%Chw7`OnQ*rG_<<7%p)+uM zSFzPx$Q#^Db|{9*V5nj@6{aq7$OO`Kgc?|pY(R3c_hB|g)IF2=fSgFm5hNYa6mnUD z0aLKVyooatgIb?>_ks6Not8YL&nvd6lza>VT{H{iR)9PuJ)g)`Y_m!N!aY|<&}ZoE z4zDRn0G`;*8PVl?@mk7Jy8Mv0VQa`0UEZs&DYSAt(VKcbt2oVrd(CsMlJV0TC&Kko zT&TuF;liGFM|BS4QRUqma(Z(=H@4aFz@fq%&H4m91@TZ9`X*$#&i|F>{kaImDkFIb z^V>Y7kp9}!6w)&Wo#f{xT*psp07b0gF%DTHtB!{Na zozpp?#rDw}xhM_;uHr=%FrNpOK40S=@UN4 z{$UN(|Hd~5yqLd{6$cq!-f`x{ zfvGV*fgWs!LuT3hCy$0B{s_nfhXZA0F9PP^><$M6hXWH2-#)c!Y?U(@hL- z-(kj20eqq}1+aaI7MjzW+bd9uSk%+B%thQ?+=qNgpDOG0RUM_iChsd+{+OPP*3P(Di@DB=5a%q>d54(uRl%i*r6<{$gZ07dl_$K1IBWJm(UAJG@&G zIM$1ck2p2Zj71xVqY#h`ls!0qkp#i`2Jx1xMtFeNR|RHw9tf7*#^*7r%G z;M3RRYq<s0f`B(%GvF8f3Wg(i?9{THzrZO zCeO3#CYT(8;(FaXD^ofF$)#Ia=W*TWs`S<1qM|=*fm>a>!P*{Nxw6G9Z0^o+c?MS^ zAb%xHoIv*YQD44p0xnlca`)+w-A8NuF<|Qrhmzioao2!9pZ}yMeOpm_7h+H98H+Wt z3G-jrtzg)mMA1NUtMRxnQI!28B|FXBER+RmM*E>06eFh>LF#_FMz&9@yRq1Hf{Fa+ zhpfFtNW4_fHYv&JQTRPbPK|u-YFW8PNBrEQ!qQH~k5WX1xed-qKOLKDCBYXFGhQrE z8o;9jJ6W+g!+r&jy?nTU!sQ%gN<0Qf20|hH zwg4-Igu(bF{Gy~E5C|#!J$wS)(Tz6`G?A1Yh&Ya7uP*+AXAB6;Wi-WkW_qe_AW9Kb zopJ=e-8^(DYyDFBh}fmf{4gU;xxYIUr2c|85c;(;exAyIB(y_+CQEyM1TrgpfO#4M zxnn|C3j`yy=Ouv6DZv>nxho{aba6J)@)ly8j@rB(weHIc7v}SK z$l?LD&dY~cr92N`cshTJ7Z;N<${M1rX+7d`jVTPdwC5jki3AyU90Pm_q%qB2X$HL{ zTBi1m*7X47sXUV-N?Wthx}1Et73ik1_F7L~Gl|W$9=EQV)K!!EFsrQAtq)ike zkF9m;qb&R;tZBB}%teN-+O)D%X+p-2;3{VCel!bjn^}uD@JAHIGDS9OyHQDdFR_sU zjvKqdveNm&al1uuLt+4k-;qL@c9<-u#Ck~0xAObK1s&4(sN9O=&M;JFA?eSh3Sk2p zJSP5JPMY3F&tQ7@36`np9fXE(@?w_5u9$){t*!zWoatS!^2_u-&Gc&a^j^~7894?C zJcB$$rq|d)O7WQRxA4ng&6Jsy;of2usHr{Zs?FjXqwqyklz3P=!_ef)nv(lX9AeJA5wn2Ljh^?@rn z&Lxoj#7{8}V`StotSb2cZ_GfAg*sjJ1zPbOxNXo}b#Ue}Wu63HT181d&9KADw%c0I|h zLV6hS<&raZ9+@v>%J+D5-71V`K^{)05{igxhm#zT4$+; zk`wlOrZvKT|J;7hz>T%ER`Qwr)W3@d5J!EWzY22=o_k4F-3;keoJ<;NHl_05K-OIP)^Z&Sw5OY{lNk-#&BPB_H1)D3lXLlAuJ@cmAJ#(^tcS@JhTd?y9&W% zZGAS^y4zP0k{Q9EG9b=_1aI0ktQv80Ub8G9#A&J3B zl-x(;32iP;N0ZH2UCE5luoYwA60IyYA4XrU z!!e#xR-U;^y^ND8BDUr(bx$<6`#8Y6r>4?uUXi(uRAX=C?Wty$JfR|Nn^q}V6W4fj z`IIs)>z19-TqT(f0a>N7xo2m6*UGv?VnXMH<9XXTWXtQOO!+yO9y;SlOLeHkyo1cv zi1zG>lE}GS9yz$doGUdT7qMLOHdEe|HYj5jvd8Rt+eaLTvd ziwX}a`dWo^uIOViz%JSclcAfAOJtaXu^CyO*j-MKfq1-!{cb2Ov_gYc4AI?NSLEPX zZ=n8sMj+~4ZJlo}MqM{rY0l;+xqJIzOT~u*H}*PuWo5WmuUwK5%F){q3D_y`zHo~S zaV)q7hma^BUABul=B+WteCq{?bC7*KKQMvLid_Mfw&@GEu;%JY?q}fviA{tody^xs z>8H$k{qgUf1{D<%5BoUoSucoC%a#-i6lc{&gGaSBUFN$!a3<=N3j_L`j%! zisq=Jva!rr&315C6l)Lf!<-XJa>4T&<*ElJJ8i5K8nsfslzVuZD1@>M0Q~ZwfhH(6 zzx{JY+z2weruo>G6HtUG3>jO^J-w(~-@+9nGc0i>eH>o|ZFwh*tM%Z+Tx1hPKm4gG z`7l@2n-fKbywxnf*~(QuwLI?bFs!ZM!_ecjWN|btf0*~j5=BQ;kepd3v z`i2A#;y8R$w%O@9DAY zvf13`?Gb)-p6MdlxmVtCZL2lR0cNf`M2G!5!aTZWVrM5^^Tuw^PWA0Qywv-C&3MAC zKgS&O2dNnTQT7EYty#32G-QD0GNQc%qEqQ>9Cy-~qxU>JMdJUSwJH;Z{fpZ|r<+l! z-Y7cut4xMJG$6x|;i?Rb^xpIuC;T51O4!C!SjDKwgl#ZXm9_$EE5 zr9R z@nF^D($G}hctZv!2+k-2gzdhvJ$~mLhv`nj!U6`P@=MKMk{}MC?QZl+@o=Z7YB?cW zp&8i^<{|$DvuV-TZGOf*QMw|Z*v+5%gUBU!i^<5X0LPfs2r>%}U#>2=>eFTFQ)ud` ztKe#QE?blb`@S#cG)e4xx7W)gmD71#M#P$Q>R0W{iX4MqKs}rX2_XY3!vj1`Q{}$M zU!$PpF2`{tb;vJVH}3q+^nr4z3q5v_rNLR^T4{k|5>UrW77`4^`@vYavIrSNhh zB@XjUep9^j(*X)=huJ-?FdPzL{KpaGdxjrK98*8uaVz8{0VJ2tg|?B#z6{? z5g&%d=5bOxgrSWO0QIO?pu1o+C{pB9`CEZVu*3y%_%u(`VDlFgNdfX>08UXbqC0CJ zL=vo(eInc2G}zi9Qn^)xrbF5(cDPW2$T~li?;79D%cQpPfMPw3#O%2~xrv-i#i_d@ zJ1^en)WvXzS;DbUaYU|Cm1d!WU*u@!Kwv>h>SR}+!e|keMf#RX7rCIa?#!x%uJ9P; z_1b)E8vI5niY9yddTYI`J)bAHwhHZe(TW+96&k}Ls;;aEJ)%ADgf3_~=*sJKA4MTA zHa%gzq^)Uj_e%fTC@Rl3o8SvDNi^?T=8bbW4$T8)kp*T zH(K<>rfQc~mTFV_BEuUdlyM?t8Cyu{ns8@k8{ zE)Z|x;+WQbSbHFgUv|4&gk_j6PhIMQguV&&#Jd?UI{u~dTfe(=G5)N+Tz0!@PqOc} z>{e^|2{onVw`&n!B;~h*QvFAMn~4q$l>43iq2VHgTo!3)qUcU$M*`x6CL7)bBE?nC zvPIG>kG2$-z9_b%RRA^1h0lA7>JCGhD0&Tc1UB6Lg}J+lMu#`YKIv{N334wAx#vDc%lO(SZCtjJyI7TQ1?$a5@f~=l-3-HXz3K^{g<|Ql?StCBUM1h*_Vq+F)GPrU!AQjS)dJzpx`4=RswEEH$6dHF`@ zF{R97Q{CH=A+Z$3{2U?zd?^ZuoLC4ZqQAn#m?VgBvQ&T524)JfD5#|RKj-b=pZEFc zd9T*R9cr)Hz+Nj8F7wGeKEjX8JH}9G{K@3XWCH}VyP0*%2CZh;3-gZ|o|~NE+5P1G z3*_ut!m%~qmiL2lbGU&g%_dQn^>9x~o{R&e_1N_pdi`;c@h*m(_jdaX<=+_@eAQhea2r)n`Er$rheV^ zTzHJX`=elX^tNJubWuqk%c1@Q89nZE3&VoP-0p0{c5%9L)E}Moq-+@o0WpO?j^76l zI;hcaK3#RqFs)%aA|PMvJ6W+s8CCNf;RzTg=;i*{tS4cQuFERqqjG}s%#Aih{Z1x= zZ;|MmH&-HT`&%6&%DrBsNU&z}yj?95~Jgk^4{oZ=bXQd1?Urz@7N0?9WE?y=$ zoab_-&>9-43ZEic!!q^2MQo9JKmd8GdLWY14Ln3y+FOjarVJKbgLdrD%b)kfzz^rq`q>P_F`|HtY`(wlxMKUIY2Sy82i0=JI#P@JQNVi-d~ ztz>fI6gk?Uf|=Qa@{iP@?DEHcEbZD6+9Zn8_x^?tgxJF}8v5FmG~nl=a)(O%N&m!c z=3Eke#sk7)lxu-Uyu?f;LCgt#^>G2)^t_<*h}j%X)|nUB@4|*v@1^#;)L|B@XYeS> zxZWp8{#c_l$y>F0yN|x`wpz6iy-#ZO$G&eT-Y0c}g|nmg2|ZwOhr@A#*|uI>I&3vx zP`}&FXVvdE^N;)*Z;I20U&&LO#m-YNZGyzM|C?C$h$k}~DpZ;;`l$L8wY?a`vSSGP zNwndpSG|j6_db>3h(05Y`=a#%jS%Vg+v^`5$Vl1|r|GiJ@rzT5Uz}=AI3FimnkQoW zRk&h|tiPsa>8!ipAjD&zUxglg#75d>7XiL9BI`X2+H%EN3j7;qF<11!6yIW_a6kwTIduceu<{15i5N z&?-!#Tev}5XT_#H#szC~_Tuci)_qoPY}y8%opr58wU;*Os|vl&u1jKpt{n}#!l>)D zKq@GHGrb9w7E4sqr)3n4?9mI-+v?*gWuN(h8jH=4t!4}|-grPvJ$v@4&>;G{CFB~t ziy=N#|5i3yeD$rFY^8~!UF_!-`=yW3$7GTT@MHzSdK(F@Wce0;pvD05Ir}-R*=yb9 zq0=QeTytjRc<#bOkq4w$R&>pc{6yEx=dYoVx5Y#(gzpML8U-Ur^UfL@X`~k*4SVLI zk|ar#lsVJWTi~uQu$k+e6^4l`c8&xTxntG%{$_PMP_4?9-3*%Z+wj&D{8?P8E^;F~ zM|6HfjMRdTRT(f`C+o|yCSSW^z*laVJaTasm%g}sGXDUD>)spMq);@_j%BWkuX!z-&+i#2n|8>qQY9!{!Z`gu}q`0Qk&J?WXz z$7IsYZ+*mAsxsk=?TW3h+BooJMtCAB+_8dt*9hi}WUN|cuZ|oO%<0UCs=Nrl zpHHcfDBYlctIvbL0Xg9Q!tkF2^_;ju-8;1PYK#ZkO;oL(EB(yZ)|)OH0Lz{1ds(u$ zX|V8Y=ij!g4_}+C@*5{X)MhG(rl3X| z^JS{FX`{D7XTj@4(E|}Rg(Hpoq~cUFbkNflRY_l5UkVplUk!h?*Oy?cy}oYiTVI0D zvW3V;Pj@*4r*+6hVWIPP^=k|4bq$=JSVb2nbn5^CkxW z3Wco3CIK-;#jaJb=$h+}>dUh0j$XUMRTsaZ?&!$HPZ1>JA2#0)_LFd*-)3d^hY1$o zIsF<(Lo?O*VTOjLvi{AcHb_&cvxhxUG?rm|l?R15ejh(L<}%cue6dHW=?(TH;r#kp zg~G9jzb(TUyQCUhC0}&9@}4kCBDfa?V!Xu)9Dm7htXJHzOM3;~oT}^0+IJ+^jXuLJ zqsd#YFvb?q3Ckr8n`zm zEclGGfU7HXL0y$2be{Q3wuy=^HA%_IWDuJ#UVUx7u}Ogy42pD&oE|{ zrgnwd>+>9PyhR{cWovWOBILzv%Pn<`2JIVWjF4=I3lI4ezSwxO#q_XKf94?}IDs7RAubm}LhQOBA)$*{RQxO33rj zFm28B5=3&8nO<5OU5*Uao2euetH`l-m2>gyycH<$JHfp&hKTGCVKZIIqpuNS(-&P04R`y+*NDddkmR7wJpJ#S1O zV75(49;@)Jn&pU}D@Xi%R#`chS7mPqdDZVkL((LbWJLNleL>0|#C<*ONv^_|s%+N(JYB%h)6WhE6IeJ%ks~=x6$wopyOO+qZXC-<5h|f7z zL|)VX8~KBMTBlFzLgU45yHXYi#a`2=6f`-MD@=6OW&S>rj+PR?pL z5^j=W^ne`*IxTz5P06E+>*cCUob?0@908nWn?H*UG`1GkVE}D(S>sUFYvw<-x*0Qn zi|Jg|x$CWIu3=f2>tMKpJafNwI@a+Re5LXo%t2Afx%;h#VOim^{zQv+Rfd;*uL~Y= zzZMzr$fr0FL|YPWw-p!IXws!@Qk8r_)IyXkNr`S8)nP3Z9*`ph-VzY)*Bh2O$Arg1 zsTN6ENs{}cqI*qf7ldVovej9dj@SkZwI9PNDJ-$M_r^|GBTSI!NO#3W6?DrMiN-lC zH(oRjy%2%zB;QK5H!p^AS50y#y@%klfLDQ1M1(CU`J8! z4nxnZ|2Q#Ko?p?{WWc3q=_c6D&F@6Ld+QS!%?P^AM(e|9tGcdn2Qn9DyWhQW*(dS0 zfDaz$j_AC-FL3TT;yX$37zO^N01ylA@M0QdWQcR6=<;SCb`7i)_(M@ICJ!x->J#=j zTM4G5Pv|IX)mx5bd&>6uYg&y!;|`DZ#6B&e^o@($z3asW;Pk!YC?(1#GmIWuL{byLb7p{oO+En82wk1)B3VZYFTTr<`^?O%?P}hoL8izWd673^)`E6w_;$Hs+_5PS$f)= zEfZ@pu}|%wAI9csv0*Ovj)mE}d*`A|W1E`XE1y!6TMBVdB6BOmX1}SGY^j<3dunE< zjq|+tKg?@@&BDn3_T6+}EiYyW!C36B47jvAQaIR$+%hG5_1*7!%1pfn#wC-o*`AcG zOo~3C{Yz%$-SD1Z4fd}cOv=AKBW=ckxWub9vfJU)+JLyH{F!I%{>K=3d&MP@Met8s zf5(oo*Z?zGEMx&u7oE8T;U#B>H?W zjXqh?>t19Toml#Y$H*=NEaOd&GkXvZ%JPE5;R5UW@Q$%EViP(%&JO*>5+Jv68(}0Y z4}!ZY-~@D=B^ZogZ+^fa2$J`-ERfT-VFod?j(pZo__bS{vh^Ycpr#Jt2;eq zQn4VFm)(@8`(GW!IS6R5$UZC=NaBtJke=csFt}Ry(QutFH`tN z!9#Mky!fB?jYQp0gx7w3+SWN4yZRIh!pH~n*~;K`s{KxRd|Np%_xtGuVeZReY z@j`nqU&QbKJNEM5fA{~RdwJdOWH0|S4A}o$_wpgYmfFjY-SvOvUcMNx)Lwoq52?NU z+XCzVReO0S?A|Zk%b(@Lzqgkke*Ay4m!k=A6c*j%92#us7tT#>=%VMi5~rlm-~%3_ zokH6E4%+mq&iaUO&A@`@n-IF zL5|B+j>#pV{K*4DuF1KftjPu8?4Yy7G7&@Y8#$V-JunK=QfoK{h2Sx|L^nm$uQ3P*YdyD9V-i+U3Y9!sI>0bbnOQ-X|(Q`cSU~P`#0(r@6_(A zB4OPC8jj=OB@rxZQGR3M7xQ96@N_vaSS9JU3eMS)ZVD4{XR@)2cP#(AAK@m~u!&U3 zMdaeb@b}b}Dh^*F5u3JsvTO0sbx_>NuF+O}H($O`s&JiX?AfiypCJbw(HeWG)lSom zuJ}*wD$|2R-2?QPKhygCsnux>i|Gr)U^{zbC~OzRGKSB^;icH1Y2UwrY+o$Az%Cq` zBx5*2AZfTeJvP#XTVw#`I@aO+k)w`roX%&JAZQi8P|ClL(s}T{x?=;i`^28h9u-{5 zqe0Q7N8$st2I2OK6M;qYXPUwWT)^NT^Ju2lh~BuPYTn||s$guc^hDs8q&5D7PU9ygyYAjA0OZJMM6B0T(~+aOq3h!T3gRr)`et&Ho&|4?T4MxnE{x?ruf5b7 z3;A}`pO`(&;aFK<1EpWQd-_&2bS~Z_$OdqVms+%(TSN% zqzvx{NUZ}z#&C|70XCLr_PnV(|I15Br!#zW)x7({&a&`bHq6=s1~t^5c-q2w$NhKl z9EeRi6AcD`buWUJixlm3t{6HXj$75PUFX-fZMBgCjgvNL^b{8=sCRbg&aJ0|JHKxG>o`dRr zaJ#Q1UWfv}UbZC~+^MaZmaDA^V*W61H5wnCwfclsj7Dlb6peY(Isr5QH z_INwMqm|*IRrA`ygR16j2oI#MUGXZF2~~NOX&PGkWExm?EsXQ3QP||Km*tJV3WLAe z8@u&0!Z_n$dd_`;8ax{BT5+Xre0FIpRInisyT_$Z_#Df`0XTXkxXFHxwz|{Em=?Rh zMbvq2orf3UgW`?o`CfFZvTHpV-{JZ(?>;|HH~o>1GreVRc-YPPxbg*W*{d+uGQG}! z(8S6Zx0E&9##gV+Y^FmVr+7qVVqQ|Qqw#XNVydTcPxu0Adlr8hUgxfs_s1ew<`PM? zY2Y1A*V^-1B_ee+xH{^67WmiHQgCI5`g^N2)Yww1=e-QG zc(ruE%0km2=Y0&rz&BoCj(wUni4h71rv`jkK|j?Zde->U~ZI%o?PgpCQf0-S7h{em4fW%?mYZ zGHlGIzt%)vpTT0+YXH1JyTR~2Oa0!isJF$=W(}*=B{&1PwP8X>wxcew619C+O5=M7 zvry^R*38*p1Y3;dFJnk=U1+41@(Z$XtTT+wtZoH>V&>I<)!K#kX zMW>ekm6R6{CQGr4lErpEk}UT6HYMxKT`OBf({!ix)l(~&FBK$m> z=Kywwcnm4ZsU^=;O>1k=CGKK{eb^E&#tt1cjklOCoZf!p#!P#Zyx(aq45H65%x62k z@kiqG5!4)J^zL|1e@S-B+ja$~#qQ6{|L|`g;7({wY(=8wXrV{jurnHbOz(Le8#*?9 zdbo5j6<-6-okXdIB(1^EH@wPN#+>(b@lV!rB}^eX2!LfZCR)G4cA6}{N8}6$#x97W zl2!*wN5k7|y;Xx2B9|Vd8fg~I40kyVG`(!IWQhg`vRPcpk>G`dyyIuuntdSL zc3+2g4`b@6t=P+J2VV~GyU!ELcd>atEc=HRfypQ+vt5haAqT;RV6v9ZqJuKxOL+7fCj~afK_K z=dHRk3v{eHH}ogn_!OcXM(Ss=tgm5ViGdGJmaj72494!HuQwLFr%%}LtvEvOvP$_A z#Q3V6gF!bkZFXc9+(^!aHa$9o<~bO=8P{fc%Z_89!@2NbCg=Og)_@V-&T`Zx8K=Qc z9JH@|fL-nYGqNXjEOe^X@}t#?m&2Q_lWQyzQjU<{9f{+rUP!c)=9dn?(Py9r0tvQQL~AU+jBH3j9# z>)xi_H=QSaeUeJrw0Iz;K8}A~K7;kArPjbuSp$Pu1G#Dq#3!f>N$hW2o(SiAs_rA z|4pp`kF!%SSyn-Nauw|II-3<~v@5*|yv{AM3R;+Mdlh*5uY#Kxl`TuJH|G89J+6fM|CK3-cyn!+<9 z{4uu2p4e=B(_hxDb2jLx;IcAw`1!5QBQkU5Shg-W>v@NryMkrA!|wp5#X@72mwpxE z%&R@WlLfcpOexd8^L@4@HYK(sZ`tmhA4x&^ypn;li^W2v8CX(f1&lp%NBhAhfPprB zkK)o?d@K#`qE(jF*SPBBPy^Ji(aLvH$wGn?oa!PgcHf@Z6|fr@!UHk{K}!ecas(v+*6p8Q|RPac+}c-sqtI8Qe3(;3!WZ zfruZN*D^geY^t~HpI9-sn8kEFi;KF~@-*G?iQbeMbnd1XD_o3Z{A=uPj1aMyPe=j-j6JQfaC`-AVQ3VOO&1Po(PAYQmHISkpJ1+z5k@GXL#4<$?k(ahcvH zLq`P^ldnFnmr7n;9l*L!gzCX=+Z@LH8~Qv!gy>F;c!N-uBhCL@m*#dohLyIMFvQKj{@)^xR$jC`J< zH3aRaQtg51Jn=C+QC!sHG2=ziV6iIe+)F|Qb?&*6A|P02>Mx@3;=n6n z3Ie(5+Un1pN0Y-4ldU$fl1y3Q)A^ebo@}(^lqlq?Ptb+B#REl!RCj-Ncd2;-O<>zG zhhz+8N46)dLDj3;OEZt`9i8YopgTWP6YGykO_`z0o&#SnM=?4qJgC~UkB_ob?O6JS zY?W}W_x4v!$0>p6lHeKDAYgB6RrY@%&Jb$_@GCqJ-LGjJl&E2JncqR(Q|Wn-AI~K> z;+%^=1pHbHbqj-|p!a_9SkSeeH|&R~B2n~a}X2mXJoB1%Q zDhYj}H+*H7XYJR1&;nSAqEZ0jiVY&gu11rt(<^cRuD+JGsf9^7-@NmAnU8#On>9RQ zevZOa^(o5jKw82oC#HOxx)IXoo0RYv-5lk9_W>WYjuz}7`7U(#1-g9be+9{k(bj(R zY-)~FW{304#BA~69(76v+rJZ) z<7k??=ALw7%zV^jMq#ffAh3eo{UPOU0s*q%52u_ z->#D}(O%l2?>tt2%u&rso69e$As@dETr*CTU+f@CRBBF$}MU1DO*G2r??7)T`AY?*aC{cmO z6UAnIgn7?j(|hHj_7jPH!MGI#iw%)pRO$6d z3pPo)1PV!}I7twVv0M(|5Jz5DXp5%|u2V~!Qr=42X7t4%NZE}mrP5Dq2V>YN0fy}H z_?I*Hvz?1wbD6mnE#0ITqy1%jV^`tXQl7ci%GRSS=fs=&4u3sVc4FmXOa{H(ktM?% z;c;l%Bwf!j&FW+bVI|XNNInBa1MT^IuJd!c%>h?{9nTXL2j8N5&E9W-2Z?rnVoS&c zw=N6Ifmh4+esM!;OQ{+rwX@K`oZsK!nv8v9${BJm4S!80VU+nPTIV_J4 zG3P&+45PSsuO!943p%pZsAn`vI51E8*Qeh{J1*_drfm$+##Qw%=^F-Wte>@r%P%E zN3KOBy`O(LYTfVMYxEaka+rOpWb&4uogF>T^B!1n>WQ*K^9p-(;!kB>lva z$dot5lF?_3+5SzADdTYH4w;G536ijlgwXZo0oYee=v8F-lVkz$RX9$s1mV0@S%f;Y^5S4Zq z*}gNdZ;w#HDQ&fLQ0(wJ5f_}cYl%Q!vEpJA-0R73-@uVjjW-D`j?p34}3Vdmvjoje>b z;n66zMzNWw-~^rK2N=SUYq)AE5IxU^@EqkF)GCruFX?0AnYGO)5)UTNR&nbkI@+>B z5Ss(FWbu_`<`}=e{v`oR*BX z_#YTA*}vazN8)~E>1?A_4oTv_?W2hR{FQujp_Cdgn|~*}`w8{vZd?3JMOV;hWfv;o znlLGd;$m#pft~{#fRGyIZo(X#5&EhOeU(dZ!s>G%>b0ZoZ&mf4)EX$msW$~Ff&EH9yMYJ0hy}U zy}14Qs@J#`KX#qmw!F?}Zgt${xNs4is!iSYkW^hz%56+8h3Ok0nMtZp_rh&(@M2%d z^`hnFjm~tr-$#u!F1Nl8J$eok!fmcuPoS@>6i2=84*9lSva_Iabd-)MZRJxKuIqBzg#wd%klofMp4jbCVabAaUF3Ed)I2Z= zaA&9`lXJ4K3hJg@2oy+1hGJd$ke6JG1qmkoEH3f-FNA9!p7iZ8NjhXc`->c8Hti8K z4vKr*YdDx>V;oUA@6-!M{8125Y+ojt=lwB#d`*1uUr{I4?XpMg_SIt&1ZlDf;=_zA zW#oni#Wo%FKTbwyXSNJjA>?L+pIq$b`d1V% zP^;c5rKL;CF+rkedq8z+2I|qJvvH+X!*~!469rM;<+RB&|F)bOZD7SSmHDl+FI$QNMq%&T~! z-RdZFpxi3xqNYhm`qzKpwnBlf|S#nwB(JxOK$RVwo%lKDsas{B$i4Xca66EtId21vDS9v5T`c(dYPQMJ*O^v1#4@o*r(7nG3JR zT6=2Ry-9h>15VoQ&-Mptt*jevbZNOVS>o=K&^xk9lg@u$cv2|VZkhk2cN6%}1TP^_ zn#%q#*^^MpNd%P2DPut5Y$b}i{4z>f!v}nVb;PE(?MpiI1i6$WBdwv0j41~`DbGdX zp{37r(Z3A9l5O`y92nuDB@wze?ZBu9CB>uzqqK*X4i$jqxMai!rVUxT1GmdK!*WX$ zxl{*2ryFy!FqHPnbZqSJ=BS%_V72sj$}{O6ak3C7?Q_#JniRz>oG5O3K3obkv70LU zID|M$JeeNt=c6U*o2Q#CwV#ic#~CKs5#X_4tj%u1a!Yz{;YGahJg7$8j?Y3;iFp-Q zz<xgI`Y;#`n=7?5rzv@L$Fr84dKh@S zjoF2){9&l8clR9!8?o-TE;g#sn~~&gbWI%xr=A6W0xj7R3d|>n1aDuZtcHKEqPec$Gm|FTo&$$Zq|MY^=Sq ztuJ6TX$@;+hTPp+!=HKZRNjJbfD122|G;aYC-YE;&wb%6-ohcD@evGk8BD@te`;Mr zAX=Og2CB{tf0}4koLMAGW)Du~qvked=)E*B!)FYVi_{6dY$F?n9Dh^cQL;tK2w$L}gEed9=yl7o9aFT1Ujde0 zi6>FprI+1pO=hl>n%{=437dl;m`+4KI3P%9P4`nGP<00dz9(I(!O>&jQ-?YCZqZ*) zglYp-w}0xusa`DbDI=JXz{e$4hf&X`4p<`UW7s4_pij_R{*CpycW4oOfzgM8g*2$I zReile=8K8B*TO{i!fW~U{;)NfyMY8R+v&Ekh z1~hAvAy4`-W9Qo^h%m!^hea>=AM<6HS-+O&UK!zYeX*-D)DF^V2FU_Y**N{Pifx*$ zuSs(c)a@3VW~xs4z9@%gf=^{GUJrxS=MvX#W(ChI;|s)CQK;#OwjD|zOeMf{D3Seu z2iyi#ri{7{Jrt-@aA3Q6_8?g*9TQ>Mgjtg+_$PFkO_-NpY_IyK7~O{=*)yplv6M>2 zyb9Z4gI&pH^8>iWFcGL^yHt`coVJIL4vrklT0R0dLB80qI^31~M7S2Q{2k^xDVieR z6c4USWklSUFB$K!Gj^Jfs5X+m!5MS>B}6`;YfIUD83u^aAAqVpV+Q8q&0Nx9L@&z0 zrL!+O!-Z=r$>XcK-sRIg2h4{gCp;5e^gC@l!|1C}0Po{>q+wTK>dniS*eiq}jz#qX zlZwf}nUuXq-fZeq#wrEZNNK>Af{pbBY_{=xf7qtG@PTT>cCWy8+{85}uq6bxoFr`9 zPlZj!DOEE{X4Fvd>9ySDmr1`OXP7*z1G(J%gM77d0(~;llYYwUrsM%OBfl>^l+u5s zV0(5i-!sgM@=1@5`3*10Zs8N}DjDr^pIq^&Q6c?&n8HHciSMJKBtGaft}n)vpST~! z3<48Q-b>6SpYfHwcBa|pN3vQVZcd#auM1zKRblD=r&8T)^S>k~J1k=`AUOux7z3_) zzw#NE6`FIuE1VkDa+j)9g1SXEmP^nxH8#fO@X>dLau5<7nwc7belpC|Byp!4C|=^- zn?je!vduO>XWQ~c&g9J5AF8NxGeXD3Cu;}30>Zt%vf~g}8RiAQB$MJmg|q;2AD=kc z?KGbOEF1g94Prm$S>GD6UDC^~%Dh?tixq@sFx>bmLefgz95Fl0IeqB4n~dj5Mj`96 zv#!BW0U8Ri%PPsgS2dejZ1J&_g6Axb!dzdREt?BW9g~Uu9ugqsbfc!&5veJ0v`;VP8=ywU zIofp>&qYTO+?rpZ53nmy+1PBBf(RYcOVC5Gv&M@4ayeNBk_BvjldF^sQngtNNXpFw zfjec3Xz@j7sZFHXyq8aeXMLL7f#{f>{0Nr$%oTr+vo22tq;DP^C=s$#qs!_Lv^&C2qJc@@=Vcnq6b8 z;6^|be&PpE*v!uyUJ;P9D(?E%%{NP4W4H3hz1{rA7n+Y>_8rc^6kmXjoA;1Fcz_Yq z9*Cl}E>nVI7J*Y_z~KIDnGOEn3&8yOEh=}0j9RQ{*HQT)T|5w!m{;6!O5La1b?;vA zZ`ECG*Zm^7sr${7>+WH}oCJ5Po%?|=saxm=b(Y!DDy}2vkv6sPwwsFpNhYK3NZk@Y z@S3VSU=$3#Mg?tnAI&f%fq`hYiv;4AhKBlaYEd&g>(Zzv7Z!H*Az6Gz(P6a)cZG)f zh@4R4%OdLKVEMe${``9KbAJENeuakuqkRU|INDeqo6X-}s;)$XtpjpoAIXC8@TzP!rP9ZS#yT8kjqVZ5ep zs#pGA0M!~UV0#7e3HqeUg$Ph|e`!565JW1kMP_ILxHwymprrqC!Pjk1)0x>>(V^!3 zJgpa~l>5i%|6%W40OP!>bJ0m+2g`}U1P4r;!k>_|BiWMmvaLwABgc{>gCD|{6Q_xh zM$$+cEX|0SktNwND0OI)7`JYl_N1JYx^3EPdN{42X`9dh1_A^UsLKsB?WL{Trl)cP zt$S`t1BCc~-&$+`uNgfY!sF)f+nW9Fz1LoQ?X}lld+q1HX~FcD{~48Q*YJ4L%W|rf z^maye1HEY6xY0(G$dc*jvdEuGktxD*@5@iekz1S4CqMg|HX0g7{tEMcyOwqw`APPh zUq`=*d5F&!5%9+8s|G1nPcY&fB4G3k)A2?sg+rPb@4DFA zc4=bzKmHRaUE4NWj1#h8>i%V#a>;AEE>667`9)LfE=s)TYW4vbC?Mn8T2U|-^oi*= zFDE5WOs`;(6r0)W!`FItZ&GI<(N|2r>5o*R|LzYtk#y$SOOMpI9qBch{w*%9Uvl_a z%Jdl&^<%8gxL|i~HNpNH%4zFeEB6#nt6+oPkhd z`bC6VrRvC6F`J8$Prv8)Rj33gX<2vTsye&ue;S$MzlB;}<=Wq)$d zCDVTgdIHJx;dmF;6B2cqRKeK~^izQ$_$*I*1TJo(-Gfm}c#Q~3-8=GaT%C(sS`Poq zf{l0n!xS>@ICA!zpK2{$a`+4A1{cxAZlF8=u?21z;! zq!`*g@hrM{8&MsJtbvdlT=jVJ$+PDnxa`C3dAlcl+0ljgVk(rw+rbZ5mt5%gg^cJZ2(gmzR~dum_hjlW&`jED{Nv-Om9Q=vLKbxpr2)P|0kJrz*z6J zb@V<(AV&Lp8CxvjJ(bfB!$$kBr@63=;VFh*9p|q*x`ubwoWJfXjp}}8wYq;IF4G=a z`1vLpf~T-q3yIE-SAYtR!xnxR;ka)XkeGdVkU&STJc_OT9rrBwg5W=Y-3JhP{<>rG z`*ZU90y2_umE89zZT8NucZ%RFJb4uo>kYAtwdb1g!6 z!z=lK28y+#eYLKEiX53uQH#%CJ^NWl_Sh9LD2FpNA@veenNFB!N{lU+LO$~I;Kg(c zFGIR#Qxir1FQ84E-g|zcV!94FwKU!Fs_7(Njxya ze)fTq`HWX#+Qn#FYO8vqv?MTfI{iz(MjE$GWBCpEUi0%jungU|_q8}Vb9ehvT>ivw zE-n~*11D_VhfDoQ+h^Z{6*;E}rALy9Skx(&Ks?TICNX*H*_s_c$)@UXdg8aK28)02 znm=vFeyMF&qbtAUlQJi~D!lvXns2sU_EY$z-E02zfy=n{#LXJ}`;*wdA8^-ikM8|+ z!_JAl%!D(w{x| zZ@P)75tOC80Xg{GCLL42_5qH>XD`9X4cz(N6u1-T-_eDKu8wBVarE9Hq&j;N zpPOZd;MZ~blR!;>tKoS?`21Lwk_tr>so`4@(WB-+q~nFzdUl|(!-Bq!}5WS#MmWxJACF1_uRPjy*Do7 zHrIs}D_+1Q<5Kh!JroS(BFZQEJ@u_<{*T4{3%=L7i%K~~%kUvG2|IvG=>3`~{eELq&|5)H3rN1qJzn}gG zgZNtduMOgd=)Wt7KSqBs=I;;u9gJUaxu5?8{m)%yerr!pV6pH?0p~c>qGp@?vD78{%)84^P{EdLEGa*v-j~(95ZnGrTCwI`I7{E z2#Q?d9sgbbHQ&aY|B}!Ar*A9OXA4|@Do=QsWM4Ek2%zsUPjG5=%c56PQ{103kQ zAa5Sd_HAoLowClSzm*Nk+tGW;9}%Qej8OmC2S4NUQ|fn-(G$h5onsnb8*A2Zi&&$@(aC7m0sb;bB{&o-E(uiKW_&=O6Wbx z8Gt$H{oqJ>dTS4#;Z{EIw-)pM(w>FCzX5n3C-~Xp=on^mZct?1|ka^a8)p zTTA{PBY*$)4I$iQAlkd6A1a%k{9Fq9Q2!XJApLL7kUm^Lj|`VDZ%m&2`uQN>!{zns zXOs~pzg?e>*3ZLTCFPCPPc7;F#_L14t-O@SFufIFdchA52}M5c|95^9&1~r-tpBp| zb#`AU52IJopJaryPd*i;|M^{W(a(|4boD&w--2wUEm-+lDG&D>ey7jxj`=5I{-Lt; zgv%SW|93{q%lx$cGs3n1Pe#kTs4V`H_O$-W5bhc3>sDkFE^oX(uaCtGKknYC{C519 zi1~*C|4H)issG{AKSX~=5HIbeDdtbc{L7vH=)F%f{jy68-_d)yIq&R(z&}d=a~J#Z zl>f8OIR6oaPda=>v zLz=HZ+-9|ppZmJeWBvU@*ZY3q|HDClE&dbEfArpClmhsd1T2kzw~PO9NxUR`e|h{WkK6W4Z*b|~ zCFzgeEBq>c%;&%GyFc*v2>$}V)Ms1Zm->I^RaV}k_ns#FQ*ZM9C+T12;2po5JRITo z=h6Rf^Z6(K`<>s$N5%8s(e@UrpU(LH6|)Th+{gJ=ErE&lrgzeoC> zd$DZ(N6D=DlJIN);gj&65Gpri5!Fw|pZmA^{AgwVcbnhu&v9pO2S!nQrWZGUH!wd! z^Ec(jTda!dZ}o-xDCoN$1fu_2dUk3Wm_q(@zYRe0dzUV5+Kd!feyFFTYhUjowA1%Vgo-{ld0g)NHooc) z<_lP!v(J3jmzPfJD}TP>h|K4N%Uk&`(eg_AWdXh+;#&~-lk`8g!1!VI?Oi{q^o8nw zTPN6wWB*I0H@|Rxqb%uulxe5$!n%YrNaOHBfPXj!-xY!fKRST}V~>YP#!-O!b!Gf% zWyEtoQA$s%(X%Ip|G8p$dI(Q?k}>$lLh!!)C_Ue8kI@q@A1%ByAPy+M0=O_s-W5Vw zt~^M2lGQl!a11}>vzFyu_HAwdetu$4FSYUIzhnH<>Bein(GzZ8I^TwpE;`%82%CRv z`!m0OTZ#WfM~t6>KWtx8%%=Z#aR_&~K8DJsxA70hzxZAe(*Nd6>62yCb9{&KlZWx0 zrr$eL`stg>(Z9&j;}RWB|5G!h507WA?+(=g_$BRO!83k)6MtJ+U$`+2^kL6@mGR_S ztXS&&@z7fXW$g01@1Okqr2g;vEx*3bfsvs11gcG{^`3V`=@s~{nc;5*ye~%6e}$Pg ztdenk!{%(+ElGSUr+|;Pw{nRF>Psf%Ww{3LcQbeHz}R|3Y+f9Tm_U~>I{h8#)erL{ zAv|V{<;jlj>Csq6JxJAy#BrltN|AEUwdAHkm0#Pr+CMq%G@VtP#|N-EdqiY9%1DBh2nUin7>kxnE+ zD@fDJu)X%MY!*tq`?Bdvx!Lya1RIY|IQfVgPBpbR;fQlQ9CbK^Kk&94f2`7^RU<1| zYjzBud~8o1o>;zMY?Fjec;3Qa#pn9Wb2UBKC=2)r?;3cda#Y~wrg(ybXbt=4+H2{17P(PL zKpJaZKOOK7TZn??KM2)lVXca6c61#1O%i=)3q^YDdaMgB7;CumpceQFyhvdy@FE3W zjt!T|>0L+;EqvKWAUjV?-^wbRK6rvU=jdr5>$vCcb8x1IH0Ws!dY%q;C+x-ELBE=` zkze*#L8bdq9Y>bDWf6BTd=WPamq_N3ZLQN^yS% zue~^N_#fWfvG4PHwj5b-YN~(Hq8E=Wp3dB+ERU+mBNr*tikIKn^iyD-ochu4HHC}9 zOdpXO3x2}rAsY0KdqCsa{9kbE%)PCA|H6OR$jB`H!=Fr^y%g3GU^^JUnXopi){J+b z9Y@+*{S~2B%ILc0(L%4Zz#UN{kX4r3m}i~>On@2A_jY}uzmU?B=d`TnuJTRGnlBn z+s^iDUQPa40y*yMc5fghHU&)(`_UyId>s%xdB+QAAV-(rgb*<_oqY=sK&-IO=uX4~ z`T(7Ku6$h$9PDaEM1rr}+=}H$~(FQ8CyB;050S;YdJfK{pgZ9mh+i| zTF!^G|7bef0t77Q(IsyL*xAdU02de?UTxtKjf}{SK-_a$Xf&XkmlS<>0G)I>F z^Xr+_!e6`+^xd=YbM#@`8?L$f+ERoZx$=t=f(s>}={}72^H=s=!H6Xf2B5bx1hU5c zEZcA+5wiOxWA9n`?{LokP74V};H^pc9(zLNlkfNc+jo?&!Fd6<8UL>VcI;VnX1EGu zVG_Vp;#aY3aErJc1!o6d%V2C?`x|bbYMuTnG0Tp6(D9!ev7PCjg&$wa?KSZKbIkvh zsDH`FNl%g(jx5~xTHrl;WiuTL&e4Ty--J@#v*5LOT>#yIvRuc=dltS4&e^fA$ex}h zfBiG?JoL*_;qNBCr@tWhQi|_q1APBNB#Q5$s{~)W;@d!c*BQPo#L=(#zKHgB_HN=s zT|avbDA_d4+c|+`?D@Se;Vbul{gPSzc~MwQajK|KYm`Z9Qe?S zun6#6bza@e%}tp5zb*d#=6=-N&zk#HbDuHSF>1*C9p)Y|_x2dv;suvVlIJc__kWrD zRdYXU?!)H(j=8^L?u5DTFt^Rz8gs8Qcd5CTn)~gGHNSr~_bGEfW$uT}{eZdeGk40| zVRL`h+_#$BXl~NnrRKhPk>+>K+{ev**xcVY*E4?nqWRx#ZqeKUbN870R&&>x`_txL zVeX3wE2p_%HTP5IK4|XU=I%Fl$lRUgwwYUN?wicL!rbRgUe1{NxVaCTd&1oJn>%Ih zu(>~L?sjuGm|JiB=rDJWx$iW0%G_Tu_tEdF9G@`%!{&b0+>V9HkFT14+T3rOyUgNi z&3(V|(aCkwe<}Vm-%|I>=6=@PkD7bJ-1nP%uen<-{X5Kkr@42S`}5}BZ|(_m|J2;i zn)_vQ|ChPnH22cym9AyxCe3X!_eOKyYVIC$hs@2JJ7w-MbB~*Q(%e(#o;LR%&Hdlz zzG&{Nt=w-iH)-w~b2pp2)7)Nj518xt{%=;V-!ylD>5>`!#cyTRtB(|62|JJIu|SdzZPtZtjBy@A&B47v8*J zioDuM&Ylr^ht9Y&22SztGS8T^YrM&LeG8s z)qSS=67l!Epl%F2(fL;zUL05WczoRd_`ZdLZ}bfbaU1t)y8DMU{M2hTeCde#JFigx zb^VFNb^Q}`LeIGyUijdDe0f`M=MA|l4mUI6Zc7)(zcgDu-%PI1-J2?;o14?Qf!<8f zE2N9W6NC?>GQ&Yc5Z;^W4}zIQp5vB}E63DsrGILdx}$G5x6|Am=59B4o4O}&R@ciZ z-KXEEe#ftY)Nmn<9E?-h#qOm@&4%@s1mOfH)+e=(QyhI84$1ZRfW zwjX$OVd5gV&tN#n7J2#m7A8End>HN?!ZW@D@$TWYz-SB}+&~6>so}JjO^u`_AEtOT zmhPEYI^w4;di?0Z1nHW9y9n+yTn{cE(nC19d)^<=L7mq2i&41a#;=~Mm0zvqcA7i- zvl@QN+@-gvKhBpsfR}VV7{l-Ac$&9E+Cw^?i@}i}eW`-T62V+Kul=PU9n0AUzefN( z;d0&=f+PM1%D@rR?Z>np8{u-6_b#{|Ts{sT;hI1%{Y&8zt_Q9Mmyd&^8!y+H7~JMO zYPWZ6U?83MGTFY|$mnpom|i)ODHJGo`83L{(VXl`B<6tc&!@)c1D`4uQ+-3}elIhU z8cY|wk+DM28%pg@dj-(r4dn78sUmZnA-_4&_h&}Z*#g>2L8+b*k1MC*Ln6r(`R>jZ z#zseT`66f>2=T~KHUnJ&3%w}Hz0DQ9>A_6atEs6WO^b04HlOyLJ9q4C2J5xC_h$;D zso_blFq-bm3}n*%j(*A;EyLS?#pDAn7YN=5w~2`GxOyjC=dUVU5-#DU;Kt!7hgaPf z$S?IkBm57;P}T+r0DC9&S0u`^|g0 z2S>*Uo6ApjEByAGx3q29=(vG)l|vzJ&h%$eP>=ck!b+9^uUW5{pY-~MKyTVB=Dbb2 z+ctV5x&Aan-75}d3e=|Q#b{@#UepdC`b1*99i!>&Od3R@7Ml- zBJ6b^zxc+V+xEG9>c)nfjFPFSd*+7gMm5CO)4xt>)rWQ627izFDYvIbwO^jH{==dY zkH1U9d)}x0N~gI?`_w<$Z!SJsNjyt$SNGH}sM~X|x}Dxd5>I*v_fUu)w!=r^_u%qj zJf0iSYVju@JM{a@tNvS14yL;n{!T*UVY;tOoL&D5um6n=ztjGDrcHDdULN|7S$Uo| ze5a0Sxmw4feA|x}k;|{^+JGuG69rVfXVXRJ-~G14`S;wEc=0_uNSAwtEdA)uE56b9 z8sE%4ZSE;^J%ejCw=?EHXZXBdRyEzjxyrtUd&JO4=i-fyVu_||cT0A3tmv#KXj8Vf-pn8s=>SGO znJivXo^_k(?G#?EmsqbemSs=WH*X;1pjjbp_eOtws)hYmkmEh%U~g0!)MnK64oN1*R>Q1n^=@3ua64C_4~FS_-9 z7+%2a_z~ydaquI|KL@w+c)&N-TjB$y^~-TU6T;oYw>>eq(Q(t8_=JV4pA&#{`P>&u z&wL(;rS}lVXVHY>S$e0Ai=BJY@J?BL?IW5`-bNTMAMdvUI+;%revgRpkgn$~{qa9H zI-UDjM|W#$KdT9_<|J|u3#K15m+$Cnc&$R~#H7+Y`UOoN*PlzD(D0?7GuQN9kNIcP zpQkOp=kuCwCjI%m%l}IX=lBq>*GAN{N3!vd4-T#;R{ujWxJ?-0WFX61bAw_7ayB3x znuIuZK+Es+`GvZ!eqXBV@{6ZG_mwC=o{yC?@!O^RpyUi8ocw8p%W>kd7~JAR!Un|X zv$(<=9PY;GX0R<)Ox;$y7tIoVdSenZ5G%brtk6`pkWB8#XP_+5-gDW?s=C^m+RBws z27@E1%9bcTgiG>4vF`F+FbI?5>CE6z5$J}7Dl3toqGFaTv!m(MKEOKcQQ$h#SB|QJ zXLJGmp)}@J4$huUSsXPJC55Xnel4!Z3?y>_7rJTNRx%SM7-aS5ib*MZ-O4%+iR4*a z!61|z03)f1A+O5Yv2#oN_AVIWJGOUsb?j{4)v;r1TZ`8#fW>s7h$8f6vW45OXJO^7 zc5h3CJ&P-35ajvUqB2S%NAv0Zy%mZNAK`gG=yk&b^XZyytC2LoDtVZPJ2i2|g z8e>8AE4`*zP{T^Eu09slh_E%Wu+=NQ`gjg&5Ejp3EyCh?G_CX+G!Mr|=gym-6(N3B zTqr|Vdi%iofU}P2GxHTat(QbE>}8m+1t zGvQSVwu4U8;F1atkH}m_KMPk~TeBKcoD{*#NWyA;-&=E_(pwdYS>ea{FymgePtACx z*WyLK`o216LQ5lOYyN z9C)cLIN%p>BEo_LJ_{y%h{=Ej;MXiHsGE%ilcg*`(NXbUDm&>VVg441tWSLq6&GbE zBl&PEm_YN;1{P{F4D|W+(nMskTChfVGr2;-e7;PC8UwgE3uDmgW^N1DA zNxsn%J7%K4SZ^GR@=hWk5w2UJT$HcB-V3Zha(^aP<_BX{7zQpW5}_}&7B%NmR+eP! zh^$YRo-5a0p>)JX4St{i@mP|W9w2n+kDwv^G`(A>FzQ8H)wg&K z4PzQn2)N1F{-gx8(01}tw-*B)d8_9e8-@59{P0HqZMFZl#(!JuzcuZp8AvMX11g3Z zYbGXDu{PEm7)SjP0P&*ojLf0>nhAxeubITSO5};w+U6}=yV`elZ`#q;o)il|7FNd` zV`24W!WzniHI@lO$5euEO_{K@Wx|>m78;XQR0NjvdfL(JB33kNEz+se52HFLZ$@QA z<)q>eUH6D_y|`l4D$H9ocq4Vf?qWj{!yXD6h8!5h$P0!ke6L(pzZO=xhw3d&0sw#^ z6_PZpR0OdUjoygXX|XYhQI27i7vHOB)GL?>w$^eEcjnma9)V}5U%~5U>=QcjqrPL`CNoQHx)*B&d(M@@VrF)LoxXMIc&}tNoBIh z%7Zq0u_-r<0$Q7-g~VR6EnVo#XGXCm;$;T2xjbeybG88HR{4NEkq9X~Om$$oRaSpX zVRKzNcPEMPGnUP8QrD(v^Yv{zxg?3ww0PyChyYE@Me5`MX9l4TLuE3h+Si8y0UH-z z6*tC{&@w18@NCNE^8J`2s)3meoKjtwhmNOUmMgu!KJ?lI3$qJB0GFtE47=Ebuj;dFMeIFw`x zvOwdDL+K(#g>dURV7YK%6O{oRsA6ISY^Zmdwf*_^ zo8((mO+fDFtR!t$fTSLo0~rch+9sLKwR2sd8Op)BTDa?jsZB704 z*Q0x@S>1%bskWxBt`TNVndT8|8Wr9P`9_M=7 z$_E2I&b_UTzZ=!#7gAooea*8fAX^xzQYJi+s7eppb-3%HC9XF-fm**FKbCRBFV|$ zs`*GvWevNDa8Nh2WYWXNyS=)m|Q;I zhhY|OMO=eWF)Er-;=l?A8Z7Xk;Y)sq7FAuu+R%DZ2(%1Hjsip=tCXIHp>>RjZU66D_M&d6BBYkO;7nHCvL3?P9c5Oamr|(}jX8 zI{^!NY6pdtr%KO@*lvgh8UaHmzE~#E4=MwSlcOXD+o}4BD4r{Mzobx`%?w8=} z$aPP;9k1C-CX<3>9XL_x-EoJf-VFe{zOwQLF=+r+%`yT#F-gDdFx8i0Lehp9stx# zP{0Y8{gLi+e)m`ui9#ohSFH5xb=;K$@ zdUXaAVS95mlQ&=iCs#9Z1C8-KBoxkIB?a0K?bT4&Mo1j4IA9=g(iuW==eUY{APs@4 z!iM(oqFIq5+NUQKe!4KDfY!+ZDm$}<@ZDOj+7;5YbMKm)Ym~Lt?w;#pS z@q>&VkdtBHv`I9tdal*y_M@Wbt^fQ1yHuMSP=;g~Qw{8JXYNAh7@Km;$2Q89@Om%D z0k*FUN+doQ(lS{Kd(&Jm<`unz5e3VNccj)}T*#3YdMea^KHp2lKuH)(Q8yg&v`bY? zSlxqp*g4m6r1*S^(ICDb`sDWfDus%%KR~U%{U$u zNxMK+D#tm7hyn;=O>W#ofS}!v%wLjy9_!Cc+^~8UZE?W=1`!uV@`gc@uIO+;{{Mb1^ZjO={%^sY= zfSd4g87@!5Ls0pDSdB07^Du{JZv{1AqJq zM|_5SzxZJm^Y<Tm|chn-AkTOh?q3t`2^X6>T#N1YoT;Pf#ys-W~BYeo&rn}SgJ4IoPwZwxh54V zV)F^{nFMEfi~5&I5m zSUksmIZR6;UAdW5OrXh{4x$r2mKAcWB2ftwfNTGZd^6H2b>VZ1Mre?=K3a9UGFW& z_vODo3*X6CKVxBgv|iXl`SBRk=JHrS*K)+_kM)C5lH*#m9?CFCs;t-8iMcyJ;T-h? z1In)=DYs7^>RU)KoT5|^fD)9+zNG$9KfOZ3JnN@L@UwC}6)m<8^1t=}ipu?mz<$u#5wtTuUHe6KJOL;1*&@po0XdDqghm`QI^l;8t;`1q`Hjz*S9!k&dFPVUy+Q-#t zs2tA2!WizJoGs9dS7MnXVnjAIy-L|Y7VM^-YGQjf_ukRYrn z<)=Uz*+#XP#j^Rt7lfJkIR*ZP`6>M106D@h44uRN-jwh6`7>F8NR-lv-~lM zEXM~TV|l0t7o7;?V=4SS@OOsf1C`RoDuOF##oKTzK_8Esz_OWncSIuLIun_4lL;Rc zi2fW*M~)=?AMCEJ(8(F<*_qN|YM^XF(#EGWoyeaiL)Ts4P)|V>9s~5=TyB^EIMoh~ z3*$G1Q6J|?AWN`=tNc~h0Zc^>V5(`LfRO-^d6kvxWTholO!`Mf>adUjI{^$4Awa{C0pb9<#jKXokaAR!*t`^uW)npaou(_M{0(LfN*`<+i`T#AG zM2U!PM2&95o{5e1s7{7%tOaVRgMkUMkDD_FCSXfd4`A7b0Nm(h0F0ZI9|6ET7Xj)6 z00a+A@~0pGs7=kd!JB9R6gf%)laahQWe70s8nHQ#+YoGzUUH&=oAmHIi7JJIEe!xg z#R=So^yhkMAG3pkWN(q-3gOLE%fpDa&eOo6o+*sFwKfYkEl`w@7 z%uLTi@o0z;z|njNG2&-Mi%na?pfYHUFgn13JXc92Pkt z#Tbu4x$3bR>PfobmDY zHv$Hrs}&Qrr4r$4)B<6lpqN(xJFc*7*hneP>KabH`WjKT_aAf{D+W<{LQNU%B@@Ym zP{#Wkp|kF|Bd*!`83u$ve3O~71DT0&7ZdDG7Glb(BS93s|qP!8H*{F#I{kn zBr>#$&*{dJ*hVUsr0hXnSlK;Ah zaQd^Zf#d~}StjzwV)+UcOA!lSEX&k}eS(sP{L>XtNO=BRa>`_*)Znur9sW!eZ)jg3*lUHnDU;3Ag`imp|w>9@l$qJO;BL zHVfn6P99(%KOuzDl3C%kA&_A>KnmhvfN0pbz%h)rKvlAh0tsl?4q_%~i6$nxM~BA> z-I|(sSI`(|S^+5ZVYhtu824Ks(ub`IdjbY2H18e(Jk9Yc;;p9rj}($GG`kl9!$1l{ z33euaP8&RbP*o30D*$%p#A4V}LR>Ip0xxY2C>V0m4wnPX!;Jy~Mp%c<90USm(ED+i zg=rbFq5^Ym=J{6H4UA^PfqQ7(q5S?Ef{lzKKM`PQH>fsxBKs1laH47s>H&4>E6>9^nWL6DD=Dm!sYm8a5Z1_? zkSLo#1j{!gMb>_Fj}>T#vDh8g_S;?HaJo8=s|1Ne89F55i%)Kqd+4sgydMu>UjD&g-jDnDyWmfT=KaF+>8MoE zq?lf|{lvEV3w@;X6dU)cA1f++}Fxd<9Bm-6jo7+CZSYWZMaSAu}OVwa;x%mM?FlEa=YcZ0bCIVtBkgR-|}(mRGOb4 zlO!&@WWCbTv7K=JxfLyBMT9pMzB#MZ@PWfk0!FjqEm ztB@h8k0Fw2BR`vlSZpx$CVA-EyU!H4Nl1y1=$UyUcN2vmXXc6AY~)Ja7$YZ2?CMzT z1@S~~MlznPi6M$+vo;o6mM5XIm2g2Su*tB500Tr)9A_}~rs6g%8ZlD`lRDel{bKsH#yu!L83!R_8Tp7Fo*9v76HkE%Nw+C zxSyw%`q8&gYsngsk3o0^<%JA2XKuS|OzKWdc_ntY4--X@?TFlp$sNTIUT9ogA0W$l z1DT0*e|Ijp5DrN7fWrY>UUd+*#(?2xN52}dUC zR(UsV+`VhpmW|uHZ`o`-0-}0!V;m6s%O(ywxohLL&aLeL2BShrxg2OqT-b}08IcTl zWW!&SK!ei}a6||Jhn#9As^nKrdr3VhtkxZ1Xk4sH4hj`j>PU^sKoFbO(8Ppl3aLp5 zFKB=mal#jYAcU=vOP>DbQ>{-nSPnEeSQdk@niR=OXFaZTKnfyUTmd*aEk^6u3`c=O zVXK*U5{Iwr6^9oDG2^waobsR{Bf7E6iM{q{ESt{_5A)VPevg2Uv#aC#@g9U-9o~;u=K9mNjs8aMV2ZEpM!g8Y#HVfJ zN)JIgU~<}PaRu(Y$HeH3oTYLCZ)#n1yR-=^r5{ln26~If3!DX0G`S$fJZ2)(Y581{ zT4lF3%I8=P+i?@~qE*2orN8t&8-o|A7*v5yvOg8ny*eVp@NG?UP;)?ucd zHYC$yvk-pKi8;=|4$wPy0nchl*70l}`KchPi+uCTfYGgC9IOdpPFI0dRpV4b7;P0xh}jYYniNCw%0yp#rFG-}X|3sAj6p+&hpejz)}Fs_u%qB8Z4 znJ#qX4X|gbG=1F#q~{eEm}(M3z@^8&7b!Pd^V|erAqLYAa&uD&0rN@U0CQDQ1O+ex zqT91%e-sY(!h`|KXy)}5`q&;-5+N*FPrU`ssS*s8xDy9GxDk@QPN|iKzsU}57m@XYHI-_M&)er2!Dpa=~ z#piy&(Nw+&yG8~VxNROgT~WKG^cw1y=abv>}y3*#15s zP9qY~pYm5Ok55ii`3XhuDF?v}vCf>j3oAa&&o8g(EmRurlz=J?saVN_KW9k z=8RAt+xJ5kYq(gpSjbZuaJUv%5Hph?+*?&JPAaVuRz)RWq!#)s*;We1@+%UnhHz*eAktgESCiv-uNZfsoJu)1zd{hFpV zb*t-Y*Vfk6uDQOcw$8_cQ3wi&Va5U97AY|UlyBv&osc!-BM?E7o9~Sc;g|-=&PBv* zUTF|A`oWFaLKd|zMj`$u4@45QVY&HD$qC~h6`lBmBJ?O{R3Q(s7+%F?h-7+%9fB{Y zGA+!M(CtiXk)mmJV!!NpKW-9ZZd3snaS9e+rs?A#T`|_yG+r+o!Pvxgw=%>;%b?9Z zeCvmnk!nDYR;iy?CdcJmBkFuD#0$o62$i4H{E5F1y2Se%v$OYSMkpPu$^_aZO-HJAO;_dJvKiI7Y8CGEO<}`4KgrRX_8O`r zns&0Yb4%ur|3`HG*eM|G%h7eq933*_kqcf{-Uw&Kib{z;D8DQwuA$HLr*F&b)d;h_ zoHfAt7fkW>PwWQ-RtWq@)2prvPq+XK!YWk|>diUp!YpL)&fdIkW&KKa?bFGhl~_8=3vZmIv!ae+ zu~~oZRUqkVR0WM$_*!oAKf)KGGRmh&ybS8X@eDWw{{4uKt<`RXy40TBjj?5|fW0wS zFxi9SVbP^_kOm1vU21Y?8>0i3G&ai_q~KCP;ax}sc#OYch*kx!GQPyRIEIlL%2(B~ z`mqev_DJpeXpAn2kK@b85(McamC_MTS}##qi4c1j8ER4tl?s(&4f6O&v_s^liAvTg zlm}%lI+_vK#*kQ07(@{{QZ|M}))N9s*mbeTRatdQ8@ZgVYe zBxGDV4J?N{+&pmT-YiV!OOE(t3U(?oZyf)?ty~sJGG1{@Q`Aku@flX8L_tJV6L`wd zq|AH-t5c#`9rIuI=V$8#t>JetnihGg}~#Ff6zyJ>lr z6b%Y#)Wi1`?j1+AAmQ`HYI{0;tTInBBUT$D>dggmLA6f}XRw`TlKY(JOMz|zZSfPo zJmu1zpaIqkC|7UF@fABd53C3miuUI|2m<9`mijLr#e|{#zscZg>kHcT71pe?Y$#K^ zsK}3IMRm4EI&94pf+Ct>Us?{Ri9XQbCP&uAeRE&8q!@5#8m1>GcXVZ5ku&Z@YrVd| zc@D=-(fD)+8mHkU0Y$lN0v|R=5SBqC6Pjgbpze0860;GjDf_VME1LweQV@)p5yTUA zHi#%+Nm57Uqh68q$2tQGe{?GlvsclY>+Q|WxBZE16F$`JN>wtKR6>$3uOj~QF?Fyp z)y>8TrR=GAt)71!nYA#}A}=i~JCJliSv9=0tn4Jy1!dLv(z3E6Q1i^HcfzkHe^dcg z&=tOwW`f6fSl=)doNDWx@X0Hij#QLS=O1!uhm^Q^*%=!s-@#|AzmG26a%@uXs0)>oB z6Cjb}n47uzeZw zlgicXY(E;?Y&;_t4v&qGL$?_8jq$6*SmflFXK;@Jx$UUZ`wSEUmzE0z&ox>Ky2wi8 zn+GXTF`Rh806Mw95pme$z`G6jCifahnBH(8v82-yN3J^1#Cq8Q0rkEE|4k6mCQ~zX z_A5(}iibQW&4p!&i%a0)G*I;J5`~yFNZO#)1N11Quh-pyQ8_?L7;gU+Qp#LnV!8Uc z+ajVC8k*`QU1bv6ZjAd2t~&QErv!29N!iPY41|3N z$lw8Hm#FkYB8%bDrCmuB!0M$$4x77{h$=_s_@Wm$q47X}VB8nJ!O2>wI=sh8kzw!J z2JL74gMQr93>|?7&Esl8Z8u$DKYSYBxYDnu(`Z*5YI6klI6mZjwrua>N@XR#&rNJ~# zwOjLl{m=7)!%WbMJ?t+byBO?j=SGXNgoK4ON(+Vsv(d6) zezYvE*x8Ox)ps=}BfJVaWe|yzi6EboENmtS&^Cfwv}HdZf3=mZT9qmQIL*z7`Lu_P zW4Y|`B!1E!QjO+h@sc0v!%nl@SiS~komK}D$#h6FGBsfQT2o(_j*>KiWmDTx@kp8Xc^`C!+R z-vrOk2fLp87Vw#{ce%@h07W^3bU`LiPxR9kgHPpixUUBHNg+lZd}Bg={9GA9gAGE2 z!AJwuAr3xAky23JiB8bqPy=e%k5m{Y?8C<`3^X|CK&;FXSS@EEXG}hTRe^RJrd6|a z$YTVoiO?Dpgviy$JcNKXFeXDG1lIl;?nl+nzX2f#@nLp{S~I0sHNQa9o8Zn3@t-(VMnTK z!EjI|&JJ#GyQ#f<*Y3{Fo$b4}?AQ)<7Pf(X3)q=O((BmeO3+D|g2Td`%tK&XB()X* z{QH#zK(AQh>*RqD#A=8G#}l}$N8)&Il)&>#s6+%?7CKLFj)Dyg1J-=FJWAso0Pk(F z8=EA8Cb7VVu5Hp37Qva3s_5NK5-~UDU+Gp)g+Od8?DNYQ^3KGMM>9dx`|d5R|!rDvqI^-zLQPp_w)|2qGOJo<~?? z9LHQGbmk`BqYdo{8OEMp3_!etz<|5TmCM{Lhka5&g*u9*;QiL26r6`=>+@5@CPRZd zn`eawrjNYMGT%L8)b5$KXQAgVf0tIhb|kJW;UcrYjrQ;HJG07m^3f#za7Cnf2D>1d z#5W5xN%?V0G>LB-Xp-_{muM2-Jg_8X#{khJzKLLEDLcxDCh^S#OEQmKd{aS_lpo7P zbMeguOEN<)B_;#R1ysD*3>@EhXx(D{)oZT>@4O9(y7?Otb@MkQ>gI1q)JJHYb&yjZ zp>@_lPJN8lSq3@vFaBM2cKfvv}gNVBI zX4!AEW!C(pufZKF5DX}d=(5?CsMuNCR$#7=>IP(_4c!nHoaT-dpdype3WLijH8-DC z1$>AIPJsCZ)0@R49=(;%7(ZTb5G&7*q<^oN>z6n0-oB{|CK#rm+-nl5Kjg#cDdfXU zT*y~C0q629gFmwC-aizxVg={s4x;Ds^Up93yAX`)cidNZ0_%0|c@199#n$g!9L#<; zVRwGqzIJlqC&&d$HpfY9zOs9h%hGIOmp!1YlvP-U*<9E{37{mA5BejzcfKeGEM?2Q zZiK@1Iy|V#{f;t64C_Ssnk7S$fEVi*{NZld49->Ib{~PnPXJ7o2r$-mb z6Yj8Wmbb%XCw;Fwb}Kk%+C$H&+3W;PvLhis%Omy03X*j>v1mE+*Uk4ON8IPwEKee} z=;gi$4Bg;%)dzrbc$!DrlUOCeR(G7;gthC?NGu}5Clf)eDhpXV)$}4Wv1Bx4vB(h| z3@27T0i3@>U$@fRxpeS_YqAt%4I!thu#4Z8*G1axT)5O3FCT&c2q{S6j;-6`Fmeo~ zAXS!aaJXE^Qo5W<*x1(A-PXQq)6OlOT|0J~sSY^Un87hP8p1f*pbqbQnDKBpZFQ?` zHqDar>9mJsOb2}P#8dX1dIZlbhs{Iuls&Hg67V#XJGLIe0|On8Q?*h4(%vs!uCnnI zF9zaM=5RXNvT&6b_tc{al{uC!AfW^4I7$d~u|3m}8K{r&6+fb)Rd9G|g?}L3gvw5q z>rPONd}D{pA%b#;n34@(Di;D%wTJ;7apa- z2%GOQZHmSZ@su_y0k?<9#SJBvB}`-v%=uaFmc{tMk8Tr56-M!?rtW+SH}z1}q8Utj ztE@ScfXV5!NRlcq?4`yR8qhL$G>fF@c`0jz`lR_5v;G0}K)5Lz}HH~ys73CgK0xi+Ze z9t|I`5trE*lumTQ)H76J80uibC|Vd69$_pmqVNs5SC(C;*Mz`6`hlBwyoZPtgW3t- zW~3Jsu{N^o4s|<<_IzR@pz0gF*zHDW^>Vq9yJ(6SiJp2?U1vatjXh)$wj(EIv@C(e zzuQQ&*K3D}KyBcc!@T^lZJ8MyDsnP9uyyBWqkbQ3OIlc}Wqz>tC(UT6QsX5{?oYZ9 zbJgr+>NwwCbz9C}kryawTjwQ4k^7Udm@Ri8q4kFENz$8HfaaJMSirMWglmRTl#AFbpAYBG$)sf6GvC6Ct(WeR;Me8Cnr z()W6olGz|-=BI;E1P1T2$oivkm68m=IiJh#eP8v5!F(>i_uUA;ClLA_Z+o5XysSTb zg+-Ff;NI@VVrS?%8qwB`?szZ;JsJ#6Rc6!`8%a)JK;cqiuL06%V!pfM${eE2AE_|uz0j#O55@E^&%hr@gG-3xvpF-20~5^%)r(aa#@q7f^3ixrRLqZM`*6OZACtJb5l`Y5 z9b7oYd}|*`h|7L}{%2xzd_6{o>o2@NiuM;x@GItMe{o))_c-@%%lFtm&G+1hx+mYM z?$RN3r{1Tomr=LX+@7QAKfU^*2;JVvfFANM34bSCJ}j3j-%t#06#iBM;R(Y%7K3{d zeovqj4&Qy+K<`X=r#?JCyr%#c#!GxG$paq=%ERyDJO+O&Tt1}Jo>szN49^3PE|l<& zKVFz%%2BwU0TG^XydS0Gp@j*;KaY6M9}dCq!R14^<#(uDoZfly6#b`fR`>XJbv+AT zdZ+q-SZ?Abt^cLwo;26h$0?M{J(P#XW94*l$LCqzGg-~=_@uhEgX;ENqwdsxb-!Qy zI9X-*t(?c+u6}Qwy0vdI|CSrc?oQv_}wyLUKZ`;!8?by_{@#gkjRaJYND|V(+ zeM6b-pqvtc;he_hn7EL5I5UzdVlfr#y#19|+Xjhp>Dwpz(&>I8*p?p2-=)%f!fbx*EQ_t@3u{{?kVJ)rLCx0!oXUGIpx zQ+4XrHmd9DW6I#C49|@9u~yT$`nc-PqVjqBpG4cCGG>po?@Qsi>Q5I+`+N>?uZDjs z{7niZ;r5#7c=yZ8zmxA!x=)=@_q3<3;iGQc9zGmXzwcw{0;s@cV=WY-2nH(R6Kg_q=HV)&{6X+bh&dGdgB-0PMCmNT} zW1;laBTvTixdYHl@D?V*?k0Sc>1}qx3%7;4aL11aLwHHyL+~FD(e;+i=@hqD6_gD= zJxd>nmS;Kqa)ArdvY+tNdE0g{>AQY+&;DiCyzz$R)ooqhx%!Q*%%A-058>fB^A7kI z5dj`Lj?Qx-ILY~=(f-fDjUwDVe4B#*gas(t){XQMp8lso@TBK!@OyCiFy6sk`{w~( z!mWf~zvN-{=jsjL#n^F^F@Kiyca@}%)RR`8rKjAU4&_fu&cJ^XE+5iaBX3imaP%h8 z|GS6u*23Rv0j^xc>+FOg;+f(@aJ>**B5}(9o+Z=ue=Lf382&K4*IXfNxx=eSu-+X# z@pQ{R9!=-q_MD31Z;ip7no@n{`ulUM%!LmRDb7Vs!xtt_(^kX>oxwg z`OlbZEJ#e7{yg(W!~ZSCv((&Lb6cO)@RQH0TYH(ppELiNSE+yW&FUr${&ObkL5*h6q)S7zX*&fWOBU1nJ0GTBTqlN!cgRxHPO zd4@l&`QHauE@}i0>LJI+uOZw$)N{|k-)aGbD~zV_iFEz0H$F>PhmWKchrDz?pUcbO zSxM{WEuw>?V>RiCv?i|Bh(t6#k=UJ0^$z0{bq?z+QbG)=B`FFChPds)iMsl=H5lL5 z)EO;7I^h++ZaxQI+mr ztfY*mV4GuGtCvlWDPi<%mA7WiURx-U1F1>gHol#!8Cw!hhv`YCQp75j$h!PA+_ z7t;7ijd-`wzn4FFc8+CtZQ0z_tf7g7hr=v#W5O=Y*N<255PbDsrYT21Q!2ml{V!a& z3|h(P(HuT_VrcyMK!6lda}Cm~T;k4N5x@Y2!>{qV(R3EVi`ynfLbB`fqI<&h-_&EO z2YSq{{fzpjUZ;BC%qP`fYwqb&>OXU_#vlKL`cExX|7r90yh{B&f2;8)|K8GBeCwm? zZ#8^9<|h6@)78FKUDqDEJ`>0d+r#bfdn^bZ%1!d;RG)hvQ@l>kowj^V8@;DKuJPv# z?zp)pU(oPT^S53REzi>bSL09rwYs%mQ#WDwU4E`yJr+J{H*MXumGL{!(l+63)9$v7^t-3}Wqd?`JS3wghx>2p4ET zxuo)wS9@JKPceEKY|R}VPNxcKZw!CNk#6GXZkJ5s=o2q<@ypbYmSp!Rj;Hpk)Or1> zV#*4Le!7rDhn5?|=#$snvwQ1DF)-vScyC{IGdZNilOARJj;{7*euFAClE#E{0gTJ< z15>11t)e-=547|RK?o3<8UPF!W?j6r#V8A^k>%)c3a1vKhA}m?p{m(yv!E>UAR~q4 zTQk|QiB;QDeI%%m9!9&ssxmO=@~a?rHHNkt7hhv-*ITz|!^FBX3qSxz8K}kPPH#ZR(Ba$8 z3zp81Mw~50&^m_8M<|r+oKZExI`s4C74x{Y1&q%37tl)r;HIuUUVT%oG64LH;PO!Z z-Jqj?65P!6d8vFJ%$DyNaUu?1L{067Eha`y5?|RFOW`X!&5(GTk*gldj;8wdg>Wi9 zY!UPDF=kJrSw$m|d`t-{rG6z>rzc>5gr8=wok63jt4&1U>x00#$wky5q)|RVN2Y#_W2SDFwH9o8ezTm^k!`S)9%!;tDbNOsKA7HHBZ^Zg2n?3D| zhy*jSq#1`pGQ))$ZxXwNPFPR?nfT^ywzHwshnx;?&)kSW&^O=K} zW^W^;VgwC{Y?#DYKvvEy4<{Hjk44^)DKQLBDqpIlmmn+F*DA#-|I#m*f?tWfn zo$kvFWFTs28vW`08JG;x&LRxPb%e1BZ1!%1nX(VP1=jOusSoh-GfcU`FwNmxUTJS# zAbE`&3|n=tmeFY&mh+Z;2D(kVCF$%S@=)z)<3<}s1%YH*dm77TiK!G1B69f=8JYE? zJZgJ@yePWrLcbE?|3csH%Z!Fogf&<+HRK_iLstW#@E`D3`>K3uTzV>25!8>maflcn{V{M)eMgCYPjhl!&ux zn&)^1!i^4%9ft5OgvNiRekFkenS22j0EL07AC!3G%qpMmACsnrN7$JE#rJ)_PZ!nbsIam|IT3tYUqx9AV z?QPdsZ*?ct%u0{1yU;Gu-c}AsGY16MN^cuJww9^h36m%*N2u}L0j~7S|}M()vsy@aS!s@?^lG?ceA(MS9ZiS%#lEWRv~PkoDKs*IHTw& z?=ghZ-(*H`ksEkyN>oP^(4j%gM>^5G5$ZtSKnR~S;%09%<`BA;cE*T|6m?t5|Lv2h)4(zKhIIBU&c}Ik&0^$ON(a>7gihEG3*wu^H+K|(yV53=}emz4^ zKnsI`1rF^v-Qg^`f?v++%y2BB&;yGZT`zho$b@t&GNB>*Sd&)cn}eEP!6Z6Jmn>9@ zNq~MNWC$S?NvnsvYHlX+l@H1<(=)Wj0geuYU7g>pyHVNh0(z3>#s0k6qhAQdc^pT2 z@Ovr(GH#Ud^cFG0{E;f7ckbRUoG{QC@jVv9$8jk48+cCy`SPt5e$S9y1h?t1tyf-Q z^W+HQItSA;K2G;UEIr}E^QNZ|FXPxUaKv}*lL0;C!%Fx)xO^B-d<;L0aQaWcklhY9`}%c zjj{5%`g|+~_hcD3roa3z0(|8EweW}6ZCzf^|B(50y!yiVMR|QFRK7*py6uV@Y!!?ars@T5ogF zf5zzcniRg%x$De-qq)uMdTr{G9_C*gYWJ+?9{4@De4M^1rym}T!P)eFLZ;nh!X7oc z*Cy>1o%~`-^quVdIk5O1ro-AC-{bq>TL158J9Kb&AlyBypS#Myy%yn5hTwh@{;sF6 zenSS}q2msa-MDqfrnhz9vZbx7!>hwb24^^I&5g&HlgO|eJOTD z4j#jPURwiOQ}0-jxinnQfzTBzup=5DPgE%4EVCV1vJt}ZU25!i=PCZm8hnBbCkZXX zz1}4WX^iYH1neaAXu*1nh96-e>jFM2EZPq}rH_9%b`NI%h+Zs*?qtQOf?Y7QJ zCNE81{O?Gd-kedM5D0Q|6seH+$Hkp)W=k7Ve9QSA%tIe1O;F-JwX`WfB5{tUK zvwbISL&xna?Px-RkjEOk`!oA_gNv#vocIq=1UXxvQ)jBZ{M2l=o==@dhR`7Yqz{Wj z(_v(6@=;oN)Isw>I#S7x)~`~9m!xG8Dn~4}$IRa>z@?=jYex6^FT4VA=)O-1Mg>fN@2H2ds{i>@p6fWCM@rcDxt!m z<+UO5HRTlUi3bS3y;SWb6&&xY?CpRkd3Uu17H2Ss#%HC_6)-)oS`-&2utcT?sz_lh-+2#J6=SHvhtG;08>PqfGFg$-EK=r(lA$9 z7M$H@9>mAvK_jDn(W6O?1^7PgOXC36q2%(e8HF$|f7bjux9n-(s*Mh<&xF$P0Z1X> z6gW8YVN!?{#6BeTMbxS0X%b93MLq`r_L+?m&7%c|N(Pn+Y>6>`pu0rEDWk}t1|kae zQ6ajr9Gua`c0f4lTwEum;IIp*TPrFAlN^Y1Z|>vY)XUm*Xp~@~0JC0Nss0ykaj9=( zQ|;>7HBHyAxqfZ^n$>IS@rl^@C!xrbBr6#d*clzgz{_S$YH6^6O84C=bQ|)_Y=^RN z>52MT?mFFCH(MlY%UQCR(rNHI;sKjO0W5-E9ppvNA>$Y_2_-4!x~@*>cg^L;{hicv ztj9v%8}xTUcL$Xqa`ykX{%%ec11YL6Ez~Pj(Ii8fMih`!Fi>e!UvivSSOP8c164JF zqVZLLPL${C+jep|kLK+3OL(|juPUI72Ed5f4W*;X+!Hn6kF#T)jKv1~i0=N1&V%;A z<$j}+a2=40*WGymZzos1(*1UB01H(${>F}*Z``4|VxJWDsi9{P#^JJ$=Y-5(J z747gBi@5kDDBNmTc=d=8UHMeDfSp_M6wJ@1@sR+&P1i_bxnjSku0@t4xp6Er+z;jH zc0ma-_~HBq=baw@YB2x7lN?XOAD-9nFbU<=)w?OoMDo2lk9Oi6I$zYdSKZy&eOa)? zOXKX+sBUGGtU70aKW*WQx;6b(z3NIL{PYz@&e-&d3lf(!^YK8xzLo#2#QwZ;>3QcX zpSjX2)XV?dm(C0mYvEn_BBw~{(mybg>!-UZm*=(|(C@jt^M>5?irQMvAGS7MDttI&ZU;t`&A>W>-va)*ryrR4ZzWpEHv8xK zA(ILHmynoM623~pn7$LCd^VcP_cl=5hrf|TFMb0Gx1%ik%Y^R$bP;h5dKX|u5faC+ zF)@}H#9sk%eBO%pVNl{8`m;#oo{hkgPh@_ZkhS$5Lp=usEjFTs(NP^WaLpWM)V3CDbhCxsG?fs%epfj~T)h58_@ z3zQS85QDn~bo5KfqcL58PKThkfydcU+Fgi``bme&7y3!^OC%IO{lv#|5D)2QddyRF zG>?olQ)2b$$Ajg0JRV;r@kqqBZL9kGSE;8HVzW_22YRh9%~fTWemi#Tg#B1)&M|ry zw2O<_I5s#&_4?MaVL|@Zv8+`5#?ibqvTdn+pI^`E-uxK0K|1_zO=ZQ4d=tnk0sO?B z08Zk=UpBEVHJJb|xcS89bZ-J_61%wNr5_P^He-=l-@uolv3$YzfnVwVY&sOsHKx98 zxvW6#8Z$RlBp#+`ngrkyfJq=;NWJ4p)r%cJuX=ag=hg4@F82?Ib#4;1!4@%`fCj{a z4H3h`c*UIlr3om+rHM-}OAv zyf6eGtB^wBwBExG*lp zXOL$G@x%B%U~p_ji=~w*e!}%5zE9%Wa+#-x+^AnX#ANY12^yIz<3{n17&S!(FpGJ^3bt`p` zhjWIj5IPKOmGBJ!Ulx9rnKUtkl0bg4M5Ua{3XRmo+83~VDmg*QHlc}Hm2}nk^5p8f z-mmX0(z3*9z>!;nD65AYMv<016X{Xx!*I2<3?*KT83ClDK9EcikY;EOmc_M?9V@DHlI(%z=e2$*^)#o_!DxYKdI(7N2c?tQkWwFh^l>Aov z`HezGRR;D0R~|TNeGG$Zr7g*|&46Q${zf0){Q1=l%4suAAm1X|FgfIWlt@>LL2j;o z>Jr!c`IW2h9BP&IrJ9iKJ15$LS~CqDNv)L@3{H-mtL3SS$rGs}M(PLEIn-a2opODE z!$sHO3*?FPmf{-$*Qo``i6N?=p%=R11=_HOJcm1FFEw0F=nl*TBJxYvcxRIA!uVhq9W znV(~2KBsQbc(rP9e~tNOK0W5YO1+7t=6{pCvC z0|wt}{uj*OWd8H!UvBvD){|Q!K^8wr1ed zr?0P&*xj|c+D(w~y>89cc8gBz-o9nij<)s$c2bLt3^y^H8{}Ud$LBTJbWT`zqPM0O z0|E&jk@E8h+8mmn>L#>8lo1EWYgWC9nSf*n1cFIE!+Be0FnbnoF}u(>CdKOD}}d zmTelKK%v`v3cW08fPhFhX|_!uP1en(q=*5EgLs} zLV3l+Nt35co#vl@(u|q2W}jSn%A8Z@&YOQ)U_sTw>eJ6SbJ1C6pR@Sf^OgjcE?bUY zdRkqx=KKrRu3Nt$bYbnrx{EHpr2f*&-f{Umuh1YSa9=Y7_E*4*a?BEZ%cTh|57&=l86vn*!SIMKSEwH2P-HcFbV> zEot;e(&*ka`lD&|t~B~%Y4rWxRDBPm(LCG4F>3&x`=S2jcwMU%n~DAnjr!o#TE2qX z9y=LvbpGY7_#tk8O$(0gV->x_+5FTj{$+f7&@e6KyIPXa4NvZu@*Sc|Be!lxG?Fe1 zF=0F#ZNQ7>d@T#F&f(NM7PfdlGuC7ThwQ$xx8sI>;eR{MaN-3uY;EI3Exd4IE#T2LTYM_Pmw2fVd%O-uIMU1~ z=O=8U!+%^8)62=RFQtUi`9>Pex;$_>qhW;{(F24KY(4=4CwFkWIbXw+~zaPSIIAb)(3X-ee$Yp3NUm z?XY6RrS|(|hY!awPPA`H@@2dNG8ZXQmmp{p+?*-c6f2@s@qd);8?@85-~Xp@^^@ zDReX*JEIzWk%q<=@^5a9u_#P5XufNk4A``Z?|i#-N4zOXfFU{!Fs_Oi)UqbbDj|tdXeCBD~!H?&~pVV1!oHS z1t$yo1oH&51(o3I=Nq|41&;_03;s#)kl+h~LxRr0dzn~HvT4Ty*K(J4+Td-bm zk)Tg-xJKHC;2y!QuZ;{aDf=X~`rRWv3{p5c-`my3k{a1;72lFpAJTp~;ClAN_cY`|iUn>9gD*4Z& zPVAp6VW&#|^Qsg3XVd%tr&mAne{8)r|G#blp6e6;JSXUE4H9>HlK;G(6Z>b=ADjDs zHRSDj05z52pVdezdF@jtm|FaFz?zWkN1ey#uO_uhB^ z1K;@O-funl?T5be-G>Lh_sFCBzW>+{_CNl^C!YM#Q$HU3$$_U2{`8rjJ^S-t{GaE3 z`K#xLe*K%@zVPBpzdQ8%Km74efBwr~hyQl?OZ<+F!F}L z32hksSLgq~TK|8z{NHZFX5@IiVe?;||J?5G8@sRX#sl@N8@q4Z*YFbKI0HGn>K5buTfvZL~w{-iv___e8*vXB-9@5j3&ZOm=UMe z;dBx9QzqF|tFYaUQvS~98@FTnxZz?bz$D8#2=N1#J;y6ozx3UyIrK9kQN?R3phycac zwKOsk4#CYt1ZzaFMuu(NfiDeht8J%FI-Eqtx3A`LA1Oj1Lh~CrP26ea>Jz6nn*6iz zI%#9W)^?o==djv43&56WPqdutF^dUhCJUb#^qUP838ZZ0oohpAZqb9arNE=N--S@Zk(C z2j8D;BfT9T>bRye(rOL1;hixYw!vW+OPhT96ZuSpM`E2+k)ZKXola==iXK42HYl-t zZ765WnZ}296Nw?imTwz&-SF|Q1dZ})>WnqmY$d{>6YT=!nkEn5Eb3@z-QLh7r{=8O z$mUKRW@$7QG!%X&3}1ce#8;a(8$mW6Z4tk0ZEHVi-{$t#_HB(VR&Bx_)K6;!B5eA` z_STl|V6${XY%BKIk`0C5a6^elIwJELV-44|wCb<Bm zLDN+&KfXVRo`!nD_|UewIib$d$H)^t6nI0Elddt+%FLSfPD|OI>BM5tnoQDCAT~bH z-}z}Rr@z~H)31~L*oL!j(pmaSv{nuUmTrl$Hk0vay)BK65i@|LgzF(Jkq#Nc+jPlv zD5w-U(r-;7#58r{8>jOU#i08+8wEb1-h{8!wPW*0r)U3XI6qC+fW~92Gvk5@$96wH z`y|^;2{{~ZoZ-fX6XBaxqC&J5e6La}L|3qGN+!bE?KCAFVZ;4Wa3V(8a7zcTFzfD> z>o3wlm1(pujaCVIspdcWPLqE9+GUzPB=pMl=0VvDLSI%>qvMAXG0Onwry$zNZBHum-==+&CPJ3*WJh^EnDp;y%!dqN4?*t;k}oBUKJXrsSe z=oKqX{#AlD=?}fbveos z=un->PmRzkHyU}1610i0Owh(YUxMDCSv?L&$9`7g^u5!pjTi60|9wMG4xJw?9Ff_Tfv=ro4_`YV9}me<(ql=gfl%`eLnr zKeu z8h+3v_uYp)Xr% z%4@jZl=lVFUWbIX@q=mcdxfs6GwscmuPvWF5^w3Dk)^p{#zq~^bjxv0cRlYAsnV_$c2;@gT19k2#h_W#K9S+AkrD-+!YiC;wq{Zh+~$2*2D(mkqi# zEuEEryTeEMcaOmLWE$VWG`^Y-7(0%SfA(LrgKwbotK|B%WLQ-5L1WM0D<&WI zocjI|!b3M@=C}3rdRjeX|0%USSiZRkw~qB%oyI4><7ST`sYP3j-o2#svwfGs^NiVa z_>-STbu27X7x8r zp{X1oPFJ*4D^Y2x#1aBO@L703|;)4lOG%5#}j|9s1Kwc_u@0_{V5T119Ki$Yh9;&&Li|{3 zLLlPtDM;9iR9s8h0$71Bs^XJ}wt1>$vG%L*smmxnZ=qT0kN^vw9r*5D+f^Mn7YXt6 z+S~B`RsBs;+fCFO`ObMqBytrK!hS5>V2}P7F23h!zNE_EFXX5A5>~8fYlz~b!FVHu z-*aAb+6J6OX^Y3&Tm9yVF=p_ob|4rZ1&0(E1DCKb8ICu-{m0maJa_VxhE9Y(Yf!Q{ zV<447d*dkqw7+w6vo%|-*+wgIMup{o^g16{Yj?zr9Zvc4i?R5933=a-%>(?)u%*(_ z)P#rEhChULBtKZHOQtQc?9g^dyVK=o?M=w%mkaPYS-ios18K?rIri7$nNHf-<8J~ zpHIVP5kLH#XjKfy`J-4TUQWWVRob@iJa@Np#cbVvSpM2(XmP4aXLW-+$8P;j41g ze)y3b$D+K}WV|2OH4bglQI?g>v6taNc*=KOKlrS3`Jx$Ql8-WboVaw|RwsF%h3o%<>&uE|Fk*17gkuIpUAsw7t}+Lohy@FO|$hn)DZbFI@rhIOu9%5|Nq zw-Rak5#N)>?FcddZOA|TNRD%~kQ3j9>$kIo zvM8H*pmXh^KG(VSbR*4v#P_A~xctoHkQ09t*XeD6{FG&7yZR~Lb(V)=Lr7E8j&g<{ z$x(mUiSNR7dfR4N%CfRu{gm%I%fqm|Qsf`;eQ7){zs|oCe-zi5@=sY-wyU4=Gdap# z6ivp3aGgnR{RndXPFhz!<@P#pdq$AkJ%ZdWCoSdLJn?!D(i}znU>XnOtfPM4yOQ~; z#C6D_EgxlB*=~7JzUv0TN0$fh@^xj9Nj}Q#b>jM*YsOo7ygr^hYa98F!iHhU*`Fqh zan`Z#<;9Y=SK>MZ@9#*4SzaP#vaIM_JE+HX{;{T*5H%jMVg@5JrF^`JvrUMrWh ztG_Faw+8yHbNP;@@mU_0o6pH}x^62kJwNI4-13jYhA3p#IC8B%iySw5ocMlRzg-uBcw1+m+^|4IiIqKOo9usT+(w=hgSQo@~GyE`od1o@*##^N9O!iQp>kfg> zwjYM;`v$N#jnAf`%so!req4{#H<`wj@78;|e77Gn9qkChAFm%HI>DFTkKKBw4b&IR zP+l%yWf}Ou)0}~iy6ANNo%sE@9%*bOKV{nTP07C_gM6+XA*3najyg``A)j^R4}izI zpmWVI%COGWOS!Id_3mc=5#OA~gg=_oT}{v8?QbL|*l{;x*<(|E{d z9c2cl5vplt3M9 z<8-cF)ayEHJHzUcCeVf7#f2Zqk-x@?uXnDQhBB;k^-`|uT)o{$vj_3rX*}ut15W&) zb4@xvq2d?m-&=jvRF~jmPC@J6HrB>q59r&okpF%gT23Q@-mg55oqLrVsI5 zX*@1J_3w4!2XURL9a5H+?dqp|*O8}30s`Za|7(!{G#;0qsTYCAx@KJeEA5avGPOhM zb)B`HVZBJR7xBGmJn8&{PW+H_ZDm+~w>&7D+Rm9@n{cR3goO#P_A~xcn@aAtyfXJ?K}Uk82CkP&=AGdc2dG>SU$dvKj@)1VWdNe;uP%W84Ui~3y`1s`1rl)HRK)A(!}+UUD3 zDZ}qvlgG;A_3`An<~@HO4Za2s!tvNN?K>T}%?_^eH?-F<0%HjN(toj7ZYjZa{? zssCfvD^A)3FD__<&&iM9xz>ClkJql9N7Ll_g|9D-FPbiog5apP$BEnRTr=Lv~xw)(C|{b%4~Jb5Xh55i3O|F^_np{!ma;STU+Qxm z?dg{QAAV^g4EgviA(tiPnlW8Y{2pANs7(|^9huq_^}5cMCBrzza6B1I<6)f3R}b0# z8!-RNz(-y>mdPUUSXYnh5yv1fQD&z5JN3BEwS#@0{TsjW;5iQaOgt z2I%(h#^XSD`WQoA%CfTE_DA`yvpfvrvkpGjz&A8q7JcpV1+Zt<1s;4m^B6uGKs$S# z_&vDJv|mhFR<>&=<-5-EFsu)GJBoOGqtj(cxhB8w#$>$Txh5lJSm)}cT-UjJ!!uDn zkb`gCxhyHy%y*9y-$(wmXE97bSyr~IpYmO2c^EcWh5b*&nsn$ z`j~&j<1CEJl5(xb_75iG{kR^f46ICDW*}TUsK<4#9lmPRKjh*Jjmwg9O_@DTd>{EU zw13L7vR(a@?>fuFuti8SjCh>YaamHXSq^zOCF3h`J(3MfNST?+fqGo$+7U*YF382% zAeSZOns)R$@%?XO2W4imgL?iIHyz9&e9S2VzxQW3YI=@W<-|v&hJj}g2X_7d-})i& zfadk6u}Di=r^!tXE6!EL2eVcFKKveEb$&{E&!{Xlsxd3<#ZkNpe6=~hdW_27l^cyW zcvTRxDZhV$Qa^|9xALotQ^Gu>y=wHm-cUupSLIjbhPtlwD*sv8s$zPcn%-2XCRZ1z zF}3-Fc>}po8wau4v2N&(LUvGxm_Gw=W$E(~p;6~(&p0V%-`>=&8O0O_6wQszrQ^8Oh}-XlQHcg~w)Y%n@U ztNdD{vqy9WG?zktvbCHLkaEI6%83F)BI7C`Wxfl@xc39e`$-^e_#%+*+d$^WbLJRz zW^*>mb*!3RSE{C0`_%MZQ-&uERSZrT7#FQ5$WjH%Mul<%m`9)<`dJT7y=Zzm#+RLC z1EtZDu+QN$nv5OPegDll+7{YF+Zfh|uprR70a#xHB4`7E-3a4Z5!%aOi%d^vF*wb{ zeHBQT=QlLd)7=at?K6p;3mOvzJIP!1I+hjG;v14m z(R4$*WwGdNlueOGWjC6#B5fV@()ZqynqTEHd5fN8>H)VRh0~sV6SUD`cGagJIY?iItoLtf2NVQ3P@Ks%g`m5FHfi*tMU)zMsvd1 zntwO=S*8Ke6$H}OD3IZ#qmuq>K)OL7d0qf|fo}?}KRyE5JKNN84v;cR#-cCR<_2ir zfavRn+EKij+!PkvaWf(Whs(ew; z#rf*u3}xlk)gjnWF)dF`L)n$YiiQdXM-PmO=7wn>>RdOJ;{uxJm=xHJIQnM?yj0RvEWl~Qsu8Dm>^7aejGEruX_h+H3 z7Me8Q1=9Tm$TWG?rmV&SNymWH|G3cVbhBSm3ZyLRr~46*SFZ|0dIlw&g z9@;!gjXhjAm_Lvg9Thfxl5y#IUi`7tJcl9E<~e|C+7SfO-jE=5lBUykB0MBEe+EeR zBOrNx2V`43EcAvmOrHCJIv$vV_*a3HMg4T!fV{d*=sSh}vd|T07O7L3OVzCE`KrP* zTa6hm95S;7j(8~RcCV^OeoLW*atD!)I0U5LVPF^?fMI`SM}T`v=%%xbZ99RqJ%5>@ z=K#rHaYlivtUgJVdnT&F;ryYz!Q25eG8?@;(EAAG!KrMc*9VMB`h!5a-wM5axsiXx zDwAe9;`(s?3;4WGaK7eM>fq$mu)J)Qhp_B@vr@t<{JF^U6vc5aCy=fC#j}t{nfIe& z2jEvwW)9|w&EC{7@bn`7R`}qc@KooehJnYAS4cYG@3ywZ^HRg|F(*VA=Z!9J#aP@o zck=yduO53UrsSvs&v>QF4*i+oeUQ&S>+?)gqX$O~Ss`zLaYKyLY06U4^fLUk ze6)iKRWw{Mlt>rwhS&~*!EE&)cvv5PsgD4V?I0)^0`^~QcotlO2)M;Srs)Hc_W>Yk zLd~c*^gJN@)2D?#3$HGb-!pBrnzq}k%6hWYn67L!x;aN>KZ3bOu~+5RdiDKr5H^uF z4874NlWrG~?s1_#v-8yK9MX$%Jg&`+X|+UT>85xxA%DPZWB5%|_39AYE>Y*gG>%%}m#Go==^(*Q?Ij zlclP6XRA3_C(i21)#FNWb*U;ooEP;h8K;)Oma}2Y>9FNg*fJZoOouH~n)B4yrg3U) zb(tFLDOH70l-b05H8DO~O~k!?BJSlAaW9`(HGaTz5`JGDHkCEu-d>l3dwjMkE5$uN z7v;q~vpr#cfOSIDGk&xhkFzP`o3hpTx-86#ylVUaWS3!n5YJY{O<5|x4sE;IquYG+ z{g@xa`yNfMb5g=k4ugn)F^$I!^KdN$n`Mp?)w-I-z}6zzTByo)!B!v2x2iB&5Y7*< zulYWN_5-i(WdAkSD?tySjC9}IH%iB+gwXJ{%}RYAe(r7HACs-dV2+f7vU9^dCE1)04ZzkA)A&DvdmnuCVQU-qQQa``41pev zj@C`N96MmE8|E2@`6}vm9Lgy%Z}pGH_vI$2$#vtg&KiflUW$He=FLZ!nReNYcGT_T{02v1La#-tM6fY9_wFj()A+U;K!x?e!`^tB9MJ5(_Eqk=>{bJCE-7E ztMHGoCWwlzKIrm&((qmir2DwgdTd4+hj%OWG5F*fJS_}q0-sXqLil~3k}|Bu^LZyM z(az8}Fc*%ReB`UV+T^_7zry6dA2#(PE%SR2NcX(Z8S{^Hp-&tB?LfL4ZkPOHZ?vK* zA8mOw$0t2*L`Ba4^b88$>7Oy_)&Qy3opZdCL^6D(1 zmkGUA=!(+|)XDJ*{O(sd))f=<*rxCQA#?8^KwfV`ddloZ8egBeJ_@9J{7yp;0qN%4 zW$5#OboD|{$8{gBpMiJBE;p=VZl0RjRE9n>R*gBJ@0I#~Hw+uDM0(l~g$)CDo9k7d zrv|Kv?=keRfpq^A+A|sZE?65+#@cu?*2a@}l@E^}DjO^v!1#7~zPcQJaV*BYu^9Kp z#)=0qfO?=`d^NViyM zPidYit;<)XXq%-~V+J_FrthItLPziclRg5Z`;yQlGxOEVx?$j0VlNGYIs5!^ph`NcSv|ao2p)w5M+XDWjwe_bjLX^or~u$QTlyiF>sObuy4? z%zZ6a-`7~*fBTkF(;mcn`;O%JkP^oGVACi}l=4(Rcn`wg{g}H0-qhgE7#h3y4 z)IA8K`?b)XN!e;rvzeb%;677;ISb|xdfpWTpF4L8fc9XX>F@F)O_s`ydG$SS=zDnA z_7SwN&%!30-x`N~ydt!fJhUY@E+PMbH_CY(<$F%fLchXS@F09Td*D92U5NXM?+M&O z;qU(yWESHdtoJ1IRSxbm$Heo#l$IvFUQ~^#b05kg`Xf^o9|6++Sm^XLm6FDfG{eF( z=P8q>8p!fw{e^#wXUgz*_a*Dk4b%0F`r|x?{Juf_ZWa8&1pla%FvFjx`AeqcV&5ts z_tE6up>K`JOAvPT{>0evAdqg{K|{NH28+O7q2&}oPC;Vdli_UB^jYv8_`1)fj?dlD zu?XSJYZ%wWD3H8#78yQ>@UZB=2T1n}kUYNzQqR1f8oCk4@iq=*TI!*@8Aw~cA@svS zKQ6SnSK>a2dnoUfh8_(XZG`tuy|6?2hzr`3;mG9mt?v> ziaa$++LNGPFd!Hd3<-vTgCh5zqG$Xsj2)){sehT!7XevTZ9-oUWSM+Q=&t}-T8{}` z{C~_n?NlJk@(3^&@uQzJw7dSu3*+7?;THnwwg|lgNIPy3`a3|{aX{!7fwbc_p^JWL z>^KQXy{7|tZ#`e=OyyU<1#Lv)KLn(^N9b<>ssDh`F9NCmO`+%g%IIGOq`u36EQd~^ zzYV1Rp9$^eJJcp^SL&l%uvf58uwQTh*#ErI^AjN5OG3X2q@81iO#Hb(+F2`f1jur_ zM(CS?wDV4(9{|$M9|~P@O0JrOwN=5rX1>R@Ywz}Kbs2b=&q1+iNN^Z9Bxx`IHJIR@ z0g`vxZ;WlX0ZD%i=tcY~zcsX;$26U(W>i8?|C5I=`R>NE&(#_XI?aA@^v7~;-Jva02!}dGB!*F;?Ac|5&G@y z4`AI*`@4nz$3VJSzccz*11Z1lkkP*rNd2D|`U^nD{{+aq{1r(3uL)hTew+&Ko}kX@ zDOYE7Rj8`wiE4g)iJH@Nu9{uFSWWbtk=z>!nYm&A4r5d3_eSQ^K)RWKFgC3MQs)_e zHa49Pq)pp|ZU-{{HXv>KCXhBgBJ{t>O`hfenWuF^ZvZlW2avKp31pt`6uM$*u?qA|R;PANQ71P~ zRg*mDspKqkL!BH&ORV@^#jQeF%3rHP{ft0oUA0}Uy0P{e13H@&1XwdHkk}v0|(K8u{ z8IU?z=!&z8)ZBQrs%%=QW>!~W?{V%i?Q8!>FjhzzgkZDlx+O2YKRFUnmqb}tkac3*8myc2BggQ11a-QK<0hj>xQleGTyAI z7Gh0Rg?ah{tf}UzyrJAdTerizvQ@3*%Llt$7X(ciAs}^zf#iq^O=Nr@uwV4O4y3Dn z!{jFpWZIv;Y4Y=XAoElBmZ8T08UIBfWeotCpC^RY`@!+k)y$@9<*#0-$}pa~_Sf8D z`j{Vg3#Q+u?;1)y3S=945lHzrd9XhUyaP!6-xK;#AmbanCVdx>`rjw?+m&D7i>CaB zgnw=p3KMQ8koxZiQvNmBM*l59>i@dX_W>EdKF8>f0I5GJbozX`<^faY15);bfL5JI==m#M+l9UZ$asH&Nq;7gWw1f#1^a=0qWAYex+R4s z9}Pgt7|&xeDs*~V>^^MD zZcxf@NN^Y!#KR%#*aD=xUg(bl8S)jOe-5PW!$OxVAFq~UU;I4mi=Tyk@oMag2k^XY zZc~M-tSd(woS-Hg_6?62GSA<*53i0IJ5&+IAv_16-n=rS_pb4VE}4<9W?(%%73=1T zx&rJc315aYMtV;& zl;)_?W$2mVf>3_IJQwXd%cC}(?NQB4%jZZ(a#ZmP*q1vv3eQgRlpC+p?=<_6`Ivi_ zOfSGQF+8(+B>l;wdCuW`&9uF4)GJ-DV4q;W-~iCS*yQ;rknX8-O&@#>NcZA-rVo}c zF?17zJIA1Lw^UP`{^5_xuNRKS7*zlt6#^eX09E zgRpZEnU)*b4M4gsp&3v2OCV#W!w(>?XoW{T1X;SDW1r6r^W~3_i9@iLe9d*%H-+%1+Ge5$DmJ#U3b1~+@4I7qxvCWcoBoK^X~n)`H4OUQYdmW215)M)BfaB%j|wmhXa9XYC~J&MIT)8*eplD< z8(70%U~K3I(tSf{t7{PSJJzP^B7NyP+!HeBa``=lxvCJ)t1%YxoQgRQD(83nZ=(K3 zILpE~mLok&mX_sxBekBpQJysjXZeM2O$-Cc8x_Q*riqLnLU>@k$?tVQx~~FxwI9ej zep=|j2GjpO1Z4apK+a_j0x667>23z{>RUoTBJ`6&Pe+_Dov%iVD0nO_${4nxl z`pl?f!?5@1rt=hdRYBFLuv>qDw@ki=p(}Kuu`3Fs+b(p;$)nZOofC)4hsF=e3C~b5 z#zu|_^^#r{8#zAUfTUZq(a4Ddncw?>4EdGNB~_)WswYd$>&nJi9h@J)d4TD4WoinZ zSNZNu-e0L_x9IUhe)mO2-VcCuH`g1wq_R*|qP@;Qd!5=;j5!jX!Q;6O&Isr^(xAu) zK*pe?DY(?gI2j9L+E{XWDb6Znsnd3+o@ppM?CdIpvh}>dhuUPCAmoK4{U?ERZC8lA zg+8^gr$Ei`DpYfti_}z{y%{@PGGv~P<%dTH@6`+wYyJrMWG|G)yTLGNcY(`Lsu*- z!MwE$^VV_LS2!K}3e}jmF3h;Ea0v3Mkes%3i~YSoO6&s;iOg8L(QzG+SKk4$znrw) z&~t%wX9IP%MzkExe_v1I_*Ei!*4 zIx4R=I#=v4I%|PiXX{kedbB__9V%3pKU<{g_K#6(_ZF)adwgo~Zj?z+sXDD|tU9&1 zOilBgnz1eyiT**+KLqR(`TlE+zG@(^@;_kca-h~9@L@kI3v}Ta*|8=|?e{boq zZ)nV5;Xr%+d57GGr}- zE!3}YPR0Krll~(>x*Km6dFv;rC3|wz*}HSq={=)Vpes+!Z61xbF$K?1Cu478682`w zlKW1WA5#9H$R89r7v5szKDA5oyu1wSr9ySi?jrP+G3YDB=qo<-l^Ge!*XK3$>w~Ry ze%kyoBdZfgcM!;{D{nV+7f_ck$Fe&!k7YrT8-Tn4;jg>Hr0)STeaid;{O0^{;W5vS zn-OOnCD8XglzRUOLJ#K|luKTV{e4E)=Fb`3-vBa>{B-MpjF}Gq`@1kN{k%sVh3x5k zXJU6jY8apA_23Lr7oM{=k5S<}JvzSZJFxwf0;~m3!T$bKm0XSTE{o@Fc<#Cf@=NBi z4P~JXWupz5_XDQjd980(+W0JG7iQV*G4{R%q}%)@L(|@huXxm_zv@wYpx@YwwX7Sa z?ZvsNt}(E;SoI+-KNB2jP!?*WB}YeHZ)-r1Nlg zFa8l7pAuGmb4t33Nm**N=O*P1n>r1m2~l?Uw>;|0@Bw?KD?TbE%&f`rEV(K-On(1^ zSQo;F9ey`VKVPj&JYNl)GjOAE1`g{boG;^<{ONq!_yTsV4;Ek|UZ_IfL0f=-k}{cJ zm*zVP;LQhbP8x4TMUKjUBzM4`sbyQ`JkIFEz6j2Plwn;~R*Q2fv$OHM1#6fpZ`5;U zjye-(07os$3iBL61UQj-mrS{Rrpb!b99><0PA$b`5*VFdiaBo zt>^gr@>0So%5&7{o%RH@X|HUn=6Qw#6c}MciAkpM`x$y+^<}>zHHuZk(+R+CZQW=S$gNhn_TLv-C&gQmO^W_P}_$ zDL}?loXUO6u{e{$XWyd+m}AZ>Dxu3h`woD&U(#nkW#sJxQl2>zek0D7ntsoI?T3oQ z7+0Gc)z1;T8Zn>QjJ<@X)nw29W9F9Bsq(Ntgkg+wgJNe$>XrWt?G@Rb8eKmQA}c6{G+hW^6yhW;Lq@jnGJ{qsP| z`#q2n{sE-R88Z4u0m(lFNdA+7GLUYG z&>nqm7Vp#Hyy_IJMW<9%L_MW==K}k?Jm>D2G)hgv9s|#>`!K)Az7uuA`#<)L%dn@z zb7NWt;)JRHHMCMjcJ*rWac|14$9FI4kU z1}7aEttK7JS7Y}Ts66zwY{+r>JUq{TVC+yy)EA%(|H~e=9A3Yl!985q!9B{9aPS3Q z@u+tA-9Iq*US2;0f348GzUJ>9bvfxdd1?;U#FMZlF2Y`VUVNq&er?LllMi4Er8F_fkquvSsBz0$d zRd!mKuCJ3&U(<4-6X*Fc{^(~1%>P@M55wzok5}ZXE70!ryCzMeQsUhETV)}}vq?N# zQ=p1!lkWgBob7}jmT6qOKXoh{LOZi__(5rVVAXAq81Q)Ybsv!KOG3X1q};7u6W;

LIeTvX-K0?d~<|-At#toxI3>FU*MX`^q_bM32{!ULx)K&g? zYF@&SY4Z}mHTzKzNZybjE;UUY5L?G*nY_&el6NVPA!~*HJdk=9qAnSKE0D6Ok8T@~ zSGNg$r_f&(x`gu{%)cry|8mdJb|FucGXOr~AaFor|1ihc^eZ6a{tBe-EW9&9x}?;j zMmL&0tnfsz@{R)eV{kf75&&p9q_bu>xUv zb@Nig^EhWeEk&N0pU+W=`6i4kDZ&^nw)Mj{+BJl{^^P$%eg#PPtkAU4U+h)q!e71{ zHX^Tl|H2JB-Z|%IMTZ(|>O%|1p8#lT3IYcqn|92>^CG$r19>&S#MJGTK*qb}T0RNu z8wvk6Al*|!R}|%_qGh9_dEw-H)x{W)+mbkP1@#D^r!O+dO&3e9r%;T`5_@Xb$3ImfZb>V$c) zA7kHD&XmVq=5b!GnukKn{82OQd6P9e9~4M+>bCzhhYn^ z`+#)c22#$GLjOtPDTi(lNKT!%GOwBif9;E=o%4DjeBFvCujqKKAf57Jh2<{nJ3RUlc$q`q!~{)1*m24eK4%^ zlJkS)`zk}w7aDKUeH2Ldu+Z-L&w3UI#gomlT)j&~jFjybdG!@H5Ykc4T&Qg%@w>26?HV~@2(%*u!>*__WJRx4*R#KVE=Yj^$gf}?EWqL!Z7WGOxo2a z_Voj!(~SNzXBm6W0&3fZzD4M~*~aeiK*sy#8Gd(M?Vg&W!U$*lwGw}m(7OIGXBq?@ zISu0v-gT9C=%N+n*ke0v-h1eRjm&$v3ikqdH}7tkZeRDBvj+ZYa?e73LJN)lTY+>B z3GK=AsJvwuqx8N*5$4Z@Dd+KPM8+Uw&A{)L(Ytbn*cY|Fv%TtS_&DWH$iw;0d{y`Y=C%7uRCbe3 zg`9W~<|l=B;tWSL2TzPlTL~@ps&~N;i@m)54*av4Mu|b5dZ9q`B*OWw48nZ~=esf~ zX*kC}I2P-IQsv6l^J-Zud#c9Z{4t)1#Y-@c!oE7*cbba)Pu(?fsC;nzK*=ohN5qeG z_GCBeBDBoZ)3<Gt@~CLzV&l9#)hV|F*clq zv7zdi@#_%e?L^~YdkwSAIj$Ep!~1}2Z~Z`W3VDAU`DliW? z@q9y10rKqcOd$F0y#Q-d;A24SpsL>q{Rdzn=)VEU_xrV8ZSxzzF`%ceGxT&|G3ZJl z`L+O=m-hgDpnHV&jLTBvnqhlm>OSD^0Pg2{om8ZXV##$9! zZ|`Hjw!AJ+l~rNKTg8?084Sd4&r{j1n&bZN|oo9kaO;`pT>rrDHb&%HW$=7Ff0v>FbmBxK|N3&R! z-dlj&CF`3un)gbJ^w~sy*K2Z>^ImDLp3?<9<=A`28n7JC7|L;PMp>HoPW8L@*cU>& z;uzlZD#N}%_ay7go^4qS&j2VR<_(Q5_p0)H%K|nZPh90ye}w-5@WNfDIK@qcK8I3wi(aZ zxPGpN?X}n&5S!T^ez*f;#5Gtuip-CKJ{k136dmS$bE~5Z@jKxY_LieQePly9QVO});ms^IUmb|uQlJX^B*9(k`<9e_{R zmZ7|V*X>n*gSR}oJ&xl3dkLN)agU*x&#MyWbNNJ6*Zn?}pFxfe=R_N%}0O5cNS_*yJQ`I4}8Cc zw{>s`*RMOY-cP#|@0sZDf)%L=v9SZ#PYm0Lil0ijaY)`4&{x>Z@pQ*hY|NY z{DO(5{p)hod#Z)l-z`A-&QSUcGWK#OV?8*zma~ttL_c`rLzuUH*sI>@rpNiXBFs%j zdB$SD-lOtirEMC3HZOc`2?TGUx@Evlqi3l4|^bEls`6k*v!z%q80N;D>{Gq#T=uFgBTS4{aCYqkZ;S5h%d>GMC|;N+{#=@+eI7Oes`TFfW{fdEpey z3#UNulwB3W69&f(@LpetK4G6QQCF09>hH9a;=2MRYT~ZMX{L}zk2U6*5YMOU@g@d2 zd`|TnE5+Cxh8(_YJm&By{XEBh!$-?6#C!&Nb-7g~%k7_!qV4TN9NtBzE8)Ev;{@NA zX4$gDA3^++@Fxq;uW_CCeXm+=(_p`2Y&E`pRaKlw)BKoMeFEO5`2wzg1#i>n?^e`K z9iBWiaj<+~d~{sc+(W1@_5|i=PkL1&_dk6!r-h%r}UGn~7^kQz2KCj@|fIdE<4v6+Lp=xZ@qwjOL*CBm5 z+8yq7QO~4&H3@T@Nf>t~q3=xErROCf*)IrqCZk^C<)~kL=LToFCL`}=*1)*{#x3a3 z@1)lj==R8b9XO16>C0YKJ4NOZ8RlXc=4XSpo(f*^sx|Q2Bt73hn^pa;tXZBdS*dq5 z(--}dU<-9%4uyGM;%uUo_rW*3>f7*li@Xy2reg`(YDpEp@R6UmPrTzzulg+fhpb+F zgAHefytC>`y|b!Iyt6PNOpeM%chJXjNbH#=%3!`XOPvLuxK|#>zISG2sgJ_HoqaE7 zXQ>G}SxNim6`S$f^FZ!Fd>C93p5L8xR5=E_z10qRE6P`gh< z`-Hdmji2HAO?Z3X@JztB*BZx0OT#50eiPS>LAWnsj#h#3Mz0}i#|@Z!0Ol+h`($2} z_^wXHY~0iFt-F-BN8In->3jS-+~b#JyvNhfikbK}4)$6T-`)wEdkV{Cz?Ms#_wB?Q zA#s1;8nJJ2mU;qyij?ufbFbhB2ZTtR3Y!86)CbA-7{%PF;O5^W)M-7{p{_rzJou$I?9~1r|q`m&yEOnpIJVS;v6v=NON?YP{mWp{rE2qUv zQ6DAxo<4T@m|gk9`c35luk^zpZP(*a@;ruTCcdFmjdyDgr{2{~e>KnMZGJBOP?owD z{wdp5bUQ%($5H=if0NK=CdJA^o=JHK!`TPiuP5RDJE^KHWacG_Glq$A=8BJHsR;ap zY1tY*(?_f6p2^A?wFBIBs5lw@4s9o;FG(9>8#|Hwvz$!+OVGZ3s`voDaW${#bha1H z0Vbfmj9oY;mR@e?{*ta3>Bgwyd(kiWjt$D%cdy<{3so#ALYteUrqxYEyQ@H3Dpl$C z%O1+nb9S6Z#5ghqT2*D6Gs-&jC{q3hlqsPxU-9DuM$51}AaG${3 zXTrUyvk%%80)NFE&V8q1Zd8`B@9a+SOa~9%y-Zn|8@<|A-ET`!hUmNL{Wf!%>2(go zoBqf?nyc4p$6fEUpMCyUS!(g~S!y}j9Nqc2-sI4xj<{xX&Oeo!b(Iok z^DMWz2{GFDyCc%|Vu1J#?D&E9TzP?ohrVJ)cFpEUzsaE*-KnhkPU3nQNMF z02bQkkNO>yP?OnjyaIWO4`=B;$4>$2M*q#w>F2DKl3cWv%-HX`1hV<_C*f@>0TGwea;u&V)Aq+`1fIt zmiwsB0O?*4+U4&N{vU(?%hL`2cY$=j6xzI#HYV{-n&~5$BcRQd?lN<=3cP19t}cLk z#A$d>WsX^|hfP}zqEHg|M4mI_9tYOaMF(=(9aP^0{>A%bf5N0E&C!x!gqQyr`|I${ z@FO|?_Bt{{D1W5#3yQ5ZBcux>JX5*zw~OdpyI#nU@41hg zve+|%{DBeV^EZOhsigBAIb!;tj*vfplCZ#Ek1Zc^hY)^f1Y0PAzm-Dg z<~Q`p$a;2Mt|i{HfWZz1iW;Ur%#!s@}->x9jI3-Jtd(pE7HIx@L0Lk8Vs z^1u}eD#61N|0%%-1s@Z9M(_neEd+m>g{~32M)2}9zWi)crbn_2z9jgJ;A4V&1@9Ex zCD2LRq!&w^98E~D+PUmZ+eWr zVZr@^dj;9&6+9~KWk_(p;9kK!f}axX7Hk!~OmMy6d4g3^9yNmXf>#Q53+@tp2z`fc zkI;Js9}}#}G4=3_(1!$H6D*VXfZ(U39JNsVS@aJhe+d1X?ttK9f)5Jr5&V?kO@eDB z{S|^&3SKLCqu_4AJ%Zm5d`$3w;7fvk7tB3s>?#wi6kH^@Oz?ccdckJFm|(YHuV9~G zzu0W zZTf7Cy>z95HoU`Jbn|=zTz9pg+&#Kw3e+v5XSYw{IiG~<=9vtl-2J-83T;5CEB^~ zUzyYw23o-W0)iosjzCmdMr7_88>0BX)Hpz6Of7 zMq=C52GA?p8X8-{c`2<}9*ZpF2vQ!wkEZd7Di$yimDd;CV|QA^7i&v z6X|@Wk8EveQ%jP3ThRVpKG18xw<6YX^|H<_TOu*M4r$`oY-`vWsak<|p%A}1(v~pn z22Zj86bA}uNmdR0qX3rtrpOKPiaCuo!_r0di; z{ez-xjWop6L0pB}qozvKIn)wsXzOT2DL0`_fZn*dp_SJ^CnpLHai|?9szb+hdQF^ZRq4@1lF|v6`mI{Q zxM6Tne5heRKYV84=0_A1fSa(WU=w66+ zz9S_x6l>p#;#K$9oX6u0o0~1~(@CAe*aBUbYEZx03dXackQ*H^g;)L?qv4 zz_J$oCYi!ydQIDw_OaV9W523h}MGE4+m_E}bX`?hFHYeWk0f?#B*wzr02C&t(u0+UH2`*i83%95b zriPfIM3*jG^cmHr-wtGNY>h;s>VlQE>sKav^F3-sq$3_{-*E{VUHjFl#aoNpR6|E( z#d0;ngWdp~=3UpmJt7Qhn=++u=#^bdRS0Rewsc^aS>D>v(ZL|~X~^ATSWwEF5pvPS zm9=&ZsZh(C+uPC5I1aBu`)i1+1tNFF@}=r_@5V^nja0v*ottAVjVWnXuUI1@Pf{&% z7t&)fM#M!3VQ-Gb+R6Y|-~FGp5hgylo=%6i|sf zC$2ZdT5ywE)!DWgpXvf_Mv4`Y?Q7Obv0t*WYLm@%o)`A4ZRoHz{mfevX^5_hMbMt^ z&#G-`>4>cC+8l|Rwmv<(Hr^WAT-Uw|Z4r!37{#YCY;Ak{Rh>~PCF9DrcnmXuS&X@; z?P}EG$}aU6wX$t9Zq|16e*&fF(uWbx>Wa0*TW}-V7}27XIwxywq+xsHnDA~|zHtYt zcpFFkr5&_MkN;2WJS=T(ZQrccl1tZiQxm2EsDr5t!K|SZIcR8WYK>ficketayIM3G zS)O1UlNnH5GddGyHA=n1v#PZNP2l2&*3O9fu;yvswhbaM5=EA(-(+9Z)~p30w^Cv- zi8nfRUp5ks812nk)3E|KqOF(-gPS^F0XhdCUq%@$QqRiSQ>Hyl6p46r_r0SeY5E5ZZyn=8^OH*Wd zb3?4IUEe#G?ZAYl&(vvKM>{4u*ss0Uq0JojoyZ*~MwF2lH6P95>`jYRapsbjwvnSB zFK@-5BAJ|+YOgjIFz)>OQG$q`{~OJl4dwgc(9w0$9_ zQEj|lN~_PKv<)lQ@wyN#3pLi#w45D}BT2=v7GNCQ>S1Mak{@et+oorDoJV(Df)~Ic zEmMU0V%CM7k=PE*uyliJ+l)4eibZq(6m`_KY`#ir`Ul!hv!amphz|7&-8yO`rm-wE zwS#8O(!WG~oF%z2**bb;BxbQK#qVU&Ds)+$FWc_(axQ6TiDT&A2-{mD8yc^~7=o3F zY0;t?x&0GUYe8`=7aBX`kq(5OmbG^Ix`wD(L7;S_%!Q%q(9#fc7eg-Uh{SLkX~2wY z?eaC4&*w8VS$mLF>RH*=xy=ksCW7+E*t#%5>LX5$E>R84FBTyiw$L#AS`?*<$sCcf zHdD(n2WW^z5*9X@G>J;PxFOcUN%~Uthmre|+UL~9ik29Ps6Dm=_FYD4GU~bETrYk# za5Y*JaFqEi?=x{t--A7!rrcZ=L^$86i{hQ6@H#vXnuq6q_~r?}ox*qXeAB#Ya3P*m z{t;mn;0YiewDrEn@P7Tb@O%V5{HjOwABBJ2qx`rIzz>1n|E5QE|I?%T-~&}SvkD%* zH|8TB-V5)=^F0Om19HFb>@PqJt&n^gFKdu$tNB6<*5cS|} zjqeEfAv-`Br-8;hNe?1_KZpMde0Cn*@r192uYqrXe-He};rrpAfFFW?4SvDsT=iM_ zhvA=xugK3;_3&NrcflWk_Y~x+8SpjmSHa&1zX$$j@UOrZ<2#A-;n%^x3;quHC*WU& zKcy&Fh2TE`|0VdR;Qt0+HYQh9!PmjR8~!u!--n+H8#cl>!FR%c2p+%ogLhQ&@IFdD z*2wtX9=usnjCCWPda1DrXEhX_@2YY zFc`dW<9f}^6NE?%fBEY`XcJoGIUr{ zMlzxbJ+HAnkz(UgSXjU50xiDkIKfF-DM>c1%8>1PZ33d)EW3e8VJsybqlZr3`FCKc z)zTD9q^0RKH8n8MjD4>CYKaZQVBHm8y*4cw>@W;#akrESwh34txmq*`hXh#5B(yfN ztvw#GoJ+8C5N&PPv2o3+I;+W6oz4WtxmDQe!JKA&=Qhg`4C*YfOp@^z>#-`8r5;sr z5p`V<*%9TwLU2J4LF-sINV&Qb_xUEqYa?}9Yp&?1L2+IhSYV)P-GX&+EIB6EQ*3<~ zOVwH4P}ZPcR*)+Etj@Y1FAHEu39Vl8oO9?*=8T-*cACQZgZ zRj(}rk6HLdaqH~R1(n*Sw8mvC{}pD_`d z3F*4QuEo08R&Po>NECtXOqn?|*B7i=+t9YP6SK^WMZzWko^=gf$8;ELM3RNg|9DdB zTDCUFk6%St)-+<~ot{zA!JTLum1yF+q3GsS*S-}qNEy2H+Uyu+wl(Nz*|I~{VwpzL zC7Uu0w#gb=$AOxXHf0iZEFbQo|Lamq05d$yER28U2JGA8ARTFQcA-0n(;z|8oPp&3 z?5=Y#wZS~Z@Z<`1^Ceq^&yVga-%+8Gb7xOmCTc5LQF8n!idZrOw>MXUj% zW^lztOJ;7y`g>D6hBaG9aD@@+{ymP}jSWqk8o0mX7&4+AQSM4v1)1%oyv^7IbHruL z7x$|+Z;z*xK?c1SZH{Bbn8KA=?}?Y^rZw#yjlmknzzljXUcD9%0rVJu{CZ#A+TMtL zrFcv0iSWtf`8f4q^tQ3WgTj`8Om;4riB{CId6Tq^XG6Ak%owT&c4T2OGs3R{=Q)3*hC0q8a-pe<3{$F;wGi^;)>2+U*`HYE5|M=!Cg z#!P(J!$3zDZfO#k_^iCO8`mZpP9{EE&#)d_JI5&zj5c~)Fk?)xD(G~W6KF?lkKFgt z9f3ENj?`TxNjiFn7L%O zOX5>BX0qSpZ(T#%@#U;*Y@7d@(*bxiSTrQBLai zgl5dh$CC&#)Nkm5L`yy{pHtPD_-uQ}M4fleky_%hjT;soSKfcH|4X@Z!kkf0vRsef zpY`pr1^d>JQ+4O_^KLs8?|i<~e_+c&eESM71$4k$Hy;=D!xg!Del;X`?OBHYkK%j9 zy&!VCMee_f)?QU>>`JFS?&OdY%(rn~#Jfq}hF#BZndX1p|CYf2mcYNi1lZ@s!?Q2Z z^V}klYev2jkps^&id;MLj3dAA#N}h2Aj3%Wdrpkw_nd4T+;J5>jmosLxO7sYXpOWiv$CLenFpLw&2k-jGiNc!-9Vj zJS6yn;E>>Rg3k&b6dV+MQgFZEfMCDi-Ga9ZTKOLr`on@Z2zCiZ1;c_N!5YC8f{O(G zf<8ffJ}>3|lqcaz@aXBLJcb1i2@VN9E4W{9uizfRUcoNGX2FnPjbKo4kzhcuQqV70 zF6a}?6I6mntHoZyA;JBEdj1rd+%0462I_`X#?ZC&|20Fj{G7?(I=lruj7Y zi_-V(_Q*J}ThQ8J_XxW=xc(=)^s-5((^aR(r_=u`|G%34KU;pfJ?V71{{N{o@6G?6 zoab$vH_c^0{&Vd4e=W74adT5-%hu+WE3azZ z*47?ZmhfL;!El;<(+>$XHnm~>lb(VP4qE;uYc171qB~CLH?z}{|)f}_|6yZoTy&B^U$5|7+L;xpVKKW z!~*!jUtIAEzuNSRD}PaSEdSHb{%Yt|{O-_o#QWg^mkt%ehtPF%sV-G}o$hmkZ5xIbEI5(&^M%p}GF2 ztC9KnR70v7iRT)gZiUc(O`$%8=DL?|k)-FEn65|YafVcjgr945x`5CXhE$b8bB#^s z7utTdp`*V`lujM7KgSQtZu0xL&C$486DYe(#x9%Qqul)~wx58X!Km7QlTdH~e zxj#So)TBqAULW)i-|;~7)IDGM{%2Z(gYSG}){7Hw*--Jv_XXc|>(4&1d`!;Gr+n>> z;HwY!t@!ixr60L|=eL3nFaGI0KYnY?w5~^<4z@ow^x@xkKfe0GkL_9d(EG2t=;X6* z{zr7^+GP*@@|It{`k8i@R)9#BneeZT+E1Pc-iiV7M96$=_W z3d}?WK}C%o3-(w*?5LpFgB8URJND3fv11Qb?6G5yiXN;d6cq*a|NYGj=A84K_kHgF zzU$t#?pjae>*u@o?3vlKXYW0;vy+Uqf9)zEVJkvrU-IeW*DWt$Q)jmhS^Gcy)+gcB zNRRU)-6HmE9QJc$ydg@e9T>N!)0mszBZfufHXga^>!S(DTS|(Wo{r8L`9w9%_tMU7 z($%H|l;;|U;CPv9SFc?aUZva&4}78`i2f_#hK+Z7)^F{$Y1Nm0;?CY~>0jrCU+p6| z3pkyP<1+7h-l^vkK63K-!xN`uSe)#%;vLt{<3zW4ukLO>eeZYf%=hot$1JNh=W0qG z*V=dZ^FHTJZQkD$GL&AElCQF$lEFX*9rGKd{F zzrt0=fpeDr+z{rm;aitF^Tsvq-bS11-f-1T*CpQDvY7Ap-5!VgS=HN@|8hmVncIbB zUfnDYZx8A8s8yGCGwg3UK5F~1r+QfD_?4%$LmtL0iH|)}%g{0!B9v*JIlCww^mzgY{%S+ZmG|F&e;cFw^q{%;%2{Br)OlPpX`9ZS+q$%s$=XZD&Xp=!^=c(mOc@h` zFE+K5PQm9Ir_J&oPfI-U<;{?(bMS43mQvr~@vWp?eSM_^dV2c|{7pK*3*X8c?Bz)p z4ZK1p3-v%LQZ7;^QX;Zsl8`P}q*i32NVUjNkqVKnBBdfFA{mi}iNbdEB6T9QA~Qv5 zM23qD6sZvDEV6vNcI9D_hCng@B6CD$iA)r!7FoWXpudIv#IRhXRHT(i!vtYJMJcLMrDl!llISw$c{co-^K zZNX^4*AS&KfS$Oid%O+nf`=j6mkxXjO~JZEOWb%W;E8*1Q|k^-OoLj&6Awc6@LJFf zH+qyeIe1+3S}+7RY@tY_0*^zKMhiX_y#zOKUJ%6xfqNm!PbT;P%EY?F^SF`P1y8&J zt%0ZC^eTYpxafDlJRn+E4z3iv8ax0|8XZ^>H{g^;0(OQdo_>>SjOc?vYuuFcX~2n4 zsupqmK{S7Ej(5bC(9%Wz#M8}c|PHu;IqzMAMcEENvqTIlyoshRp*giPa75j=p`G8Zp z!6QBhjO>B%W|RZ?5VG20!E=>2(jcBV(+7EmC$5IB!mGipkX&cM^C0{{tV`5Fp76vv zJuw!6CpLhj@KSIV6bn!M+6(m!o*3wha)c+&g971I;5UdrkA3jG9Eh*a;JCgh2c!uC z$Mq9EI8`BfaB6?igEN0a{%M=w>_MUj?FZvpjCd)S3GrnMP8o)Juoc?}k3-b1wP5Ap zf|r29MXv&nLbR?Pv>GAgQx2Yj_&NseA1Ry*E%<0O&X*qP!Aj!VJrPMXv$J zjz?Khe!vG%EW92(<&S3}!fV0GfjAa;30Qw3@_{_d!L&&@o=DUgaOq_9Ifz$-Z>OR! zf;WKpdOZ^i9|W$Pf$dWo@FGO{*MaV{@r*LW%fV?7#Rq{!&>h6f=Aa!wR90$m!2;w5 zUIi`)6FoR!Dasc4QG&|lf)4_hL-Y(aVhZGhG{mD&O?Vyn3ZgW`$}4dHhj?Ng$Q51+ zdO(yn1vp*wLEu%0&XEpWx)Nh~q*sHdA&S?6W~+pP?LfJKXCQZY9rzZa zwrl_^MG9U54uI&~DZx#mCuTrvurBc;6bdihj?WE1{C+{_9m2XYus?JTX^2~)On71{ zq=C-_d+$U(;T7PXUC8H7^oL+J#MgOn|8C?N@mjFu9-$m$ps5CR9q|%ya}4^cU04^q z2T}bbTF2tLf_UN)NC&S4TPC2NhnInm($Ht_#(4pIW}rR5E5OkG5WEVUd;rgSg%1LM zJ1CrACD`tekRLht9D0v52C%_l@!Wy8j-YR$ehD0zh4T)t1h+y|Zkgach>lkeHasep zA-ERe(|~s%dZse504l?}#0JMuhvA98K{|NibVv&y1cpO&t~0>`NU|5@b{yXghA3VR zhCq}D4fs;@%n77{`1$}|hLl)W2RfV-)|G-&MIQw2h4}4&FCaP68^B|y(2j7wp#}Gx z#(f6jHQ)`1KX$OaR@@hO8RGW^Hp)hsBfSiK3ek4-V4E|jPlzWjK8t5y!V~+SLpj5% zK*#gKc4XjZ(K8qDJ#vW3Rt8RlC=WrP$weVv2adReGQ@V2;DpQYuaE}Z3(>k7(DI7V z9wlHjbnvMK&k9%3S7`99U9jafoFi<9I2O7J9|S&u?2yI)PP&dVfe!-XA^yCB4&e(Jc-+E-DTCL7{Vfrnf;@m*E1NJS z@EWjxReYa474cxBYIwGJ8ukljLv)UGUTMjzdgQN4<7_>gKD0$ z;HibzBAz(i!GuYqG+;4A+r)z%7-xvQ46NQj$iEa!g7~@zDx@a-Ju@*FqJEVa1?6Fz z#52$xcpbR3A@=(UW!1=pxzGeW+se06Xz66Z;t%KXM#5&>VNd$+?F^-#H+wf zklj8Do)ual--st#L3!{JupQ)sG{mWG@H+tT#JP|ZUPW>3O&C9T;>8XoOeDMxEQaV< zhz&cMF!_ilc7t-^6<`#^pF7a9lW<%?pq;BwCQ|So6Bi zsKf9wa5O~QRDy3}(B4m4@C=MYJ3<=b*?1faJn?8E+9tdfj7&j&LwXHZCk_1%JaK0_ z@&-@*nt?KbCpzypVV1$mz%K{UM&R)?b4(~iV-pql1ZsH-Wdgb!Mn2(XU z(-u4}kD(636RSg2;H97sq=YBlgM8tMuOWAM0~mE2{VIGWSnUMPJC0WhhKXKr66FS6 zMH=D@C=;Ie71F>Hx17SU!xMKw6t4kyp2qQ#2c=q+13a++GS}j`veCvNIz|<3Z(q2GMj0fi!716H_zvV&KE zhoN)uT5#?~^xN6U1L%~4b_Xv5*Fdgka4ta0OE@lgDd-2?fhX>RwD3fi%P1ds8F(F{ zl5D=smm^{0vbVqRUm(H+bTG$R1t~rd~rEhR*~I*O5ZsI4Dm~oK&Jko$k5Z`{mrx1Ug0!u~Dyf9(DL%~ST zyhQ#XN+SbDi(Uy{foQ)v&^J%;3NQqs^eQk;^qJsY(d$8rS3(*I*g^Dia4JM)7zCz3 zd_F<5*MgUUGoWzfO$BC)@j4JcE-ADfunENH8B{}?k)C)RqHP*Lk9_nGh?j%8P!_x% zocji4hdgM&!*9_R5Kq7B(-{iVBRx1AqW4mi;1-DTtOX~%M}4CASip*Zm@w_(C13|g z4Nu%}!2J?D@dPviUJJg3a^Z0LGWik3pNdJb3!;pw|%9 zc?0P5NywWFoDM}^wBUIHegQmj-e=?io>;d8Z3><^8fpnooCDdz6PG|$;4{HD5FI=H zW>N`6^+Ed0gc%84MH=E$C<~tW35ta$Rw)%c(NXloE}|#)7d>&j=!pwNuLsS_#JU6~ zLU$-{p#2Zwd?~;vh_6dv?Vm#V&~G*cLNxXy&Sy-SVysJC3gyA8K^GI@InFZhq^T(* z&B6HswdSTwK6!8ze*5XfC8P(R;WwQ0kKwIMnTGfcC%ztn{Y0+-_d=4(sAHf-6;mb> zo_N>VlvxH(>{8W~nF3GrBf%4cARl<*D##UH4L*ZX;q@Rr2Zhdy6!d`T{V3uI=snU9 zZ$OXXb>NC>rg)Pc+X3%Ff$)0Jv$`ph2(JLIL6WOT4_2sQ%9p1EYybr!p7$YYv3hdV`#xE3!d%Z2VTRyE;tO5!xJY!GI-)*ND8k4H`?Nv*zg*#JAT(I z51xMCt5t0r7d-t2*d)mQI?4yU0&RvT=0PfWVkJDUc?vwS9y9`;m=5{EXM*%QVN_>` z_eD>?+x1&LQ$~X{N^n(uY!_Y)9)zerCq9R!+_2zjir)wu0Z+d{769@68%V!Vrb0Y% z6J(BdtpRU9v|k3+K04Non zet&HjM0wMI#T{@gh-W(D`F5Rf9&aK&=-`SngqMQ-AUa+JI304mg+2<@LQ;5QNM}52 z6`m;Tg6(2mq6d@*PwWBRfmeWYpkSmYy1S!}!4rExp708A24sLIo`iDYiT7Ew6L{ig z4$r29=Lx?VJ{C-amceI&UfuA_V|WEv+Y{x9{zM8M>49>C*MfKZU_0=7uu)&3d}QEe zsO4Rh6-dver*a_HP~aRP{?E^gr{~G{?T_-gXTei40ObQutOZHn|NMM-dVc&SNQro2 zHl%>pf!7D)*y-90+K$BYn-T8=Mnh7ZyIAlFMCb7;_*L{}AU(r|KVRTHC>Uwz89eb2 zrB4K(LzIS|QDZS$h_?c}LKN=_j)kb+P610H8P+WWTPabe;bq`Gh~^h^LF+N5%y`60 zz*!LOi=J1t0ix~5$C@&up&-O7!QBw$O#`+Yhq?-{0G9=rGHQwkS5H9S3%>?D2hnz} zg3|+0Ca)}*P;l%-)M=y{56*=s%>rM?VJnk`^zzGm-GZ5S%`d#2Rh{~`Gd_T>Ual*P*(@mLe z5IwLt5^NEKGJJ#g<-mmyZ6_D3H$(9DV3V0>3s~0~M!XE_P76|LQgBKPepNP)^hb%(7gZBetAS$<5aQb3g z`_`ks0^dVAq#-s`3Hfva_dt|h16Bw_Jkm%&Z}K=6A8_$fQ^xrru1#RmWu}ZA-Wl|Q zs9*H~D=bHyM7$MvY6be@eDn#R-Ac57#1k(=l%K0$k8m72;(b7eRX88;EkXHeZ1WN7 zIk*~ff?or^fqdZ&pe_RS7@pXE4a$MigT2-Y@d~i+I;2DVcyJj+$3l!1J@G9><^LXR zvtCFu0z3*){0VTTS}5~ia5Y5nYrrEAZ8Hlj5aWx%N*jbUPT&O52ZB4HT;yjLco(8F z&jnX*6nr>Xdo%iZIu*4ZJ}J#ZmJ+gF3GJ8{2(G{jhF0j&$tGbc6h#Ho9*3_cV*yBF;p{v7xT zqCAv<6Ewm-VIa5yqPh_d){heQD+RqEO78=n*oSKzwnKE=FZ4z3-~-VYgB=ebA879i zFzgWO(-WLWaQL}mMC%euFQ5;4ihd95a#4tP2TzJV2P_qR8Mq=xXj5wN2t@lj2mXX;`^+U% zrWr)rcLzftiVp>MUKaY&UEl|Z)-`~0ui}0Jc~}6Rf1JQLs13Eqxyc3ut`c$y}GvT^JoC?vpLEv{iuFF`L zd5(SwqCEJ3+aXWH?*e_^;~wKR;=xuQaL80(jJr#So1r|9Pz{= zNctRg7`*ZW?E?NPX!cXsmpNEuV#Wkh8nBd#b7d?(VYsP3Yyh1W6r@#K~Km4uK)v~68Io+22=*G z0>dB%`CJBi)D^Zb2Y1#pW6Tl13w&=ccw)8sW{eZ!Yl1H!I>tQEynz|tZxA;?G^eu} ztlJ3BGDR9+aFLT4GY~!;)IwCA#1he~8k;fM&@!Ys2P&GF33D`{9M9p7MLh8vbPm1@ zT+K=Of-6e1T_eo1;AQz$&eTawZPLGql?yegW7>hBAV82G>Ht@M=)r z7I}at-h=3U_*`&bdmIHFkyv5+m9)b@B3q@ZH z&hZxFL&49YF9E0a#52gTZYX#XqU~rw%U*)F0xf%+F^Pz`0xf+-58i?3d=Yo_!TCd) zU10ydW=sye61*`K#|W1c%KPj|IHg)(hSb9KR9yLp(78qU-(|@UZB! zz*i9ECm;MO`ZBQ2CLwS3U?g-7+aVqil?Da03p>JK<~ z@I>Y#gg%_;2vPo>!08ZOPlLczi266;V~EZp(c}*yy*cO)(Ogv^xD(>fHE3cG;?2QM z5N%%$&V(opJ?~nIXIx8=2V!T4(zt{BA}SD^xWztMJN;aW#BQ0 z?x~8wy2Zlw?ZK%KtbRY)TTRo_szu`V$IqB2PYFNi)DyjhBK zgySj!N0teFtP)%RQCaPxIMEk_4S%2!}$WIKr|PS2{NYUjFlSKCQt?C!E3=9W{BU2J_kGimBBL>c&Y#t zyc6{eJPFZuR29q_9b|{|%UGE+E)c~l!BwKq1fPgrf*-W$4Do5ewW8O8Z$!^jGH04W zltuwA6n!T6K=hKz=8OkKX@bD*qGzfg4-mh7aFytFct*GZvOj=!1lFi(&g8&L!A_!A zfw#%yvkh{IIirMpPT+XK1n3~H7nz_Al4Yas0u4|qJkhe6Ipcf=eFZ3kd0A7SBJ<+-j zjt6y~=m=3f(L?k^f6-3`!$nVw7CrHV=!y44Pc%Tz=I&N@jubs{mFS6Sq9@)K zJ+VOa#47cK?GT%ap4d(F-r!i#6GKJ61WXY<@vP{HN9=`do&^Uupw2wC;5ikZ%7hpp zdSa~Ti6=!*d?b23Xj)%bm*^mRqO0gVKtIus1Vcqn+$4Hpy66vp*F{gP-9RW?Vt0u0 zNgO4{M}s%Scw(98iS|-+zVGq_Lq$(K2T>nLv}kC~yhqe6aGH{vbv%o5js2|u)KG?`fNW)~}Sugu4FjBmKs07y@#dBZq9-12Ld!ho91^=Jd z|GWfz!rq5tD*-l(&O})Jw7~~+&$wB90d1BQAil_En6}VH;@~A=m_nPOxdAq<9qPc_ zEX8N8u~ut`dK6l+9HBy+*Drn7+73fRksDLaJQs06X(551)n4_6f_zm5}kte%`*YtBs^4)*K4?o$*=a>m)<8FPJL4H)(GWI&{J^rGZ^mJ%O#|diVr^$!Zb8dEvHV?+0GkG$ z^u4lgRq>4vS2tTzEDDxZwyh+jzKpHiz@q$Gl-kaw!7^-+j#Oe%g6KE%@#SM|zdRrO zLi~sFwZo@70&Er^bjm~+=7f_LrpTt6r;{B2`#LEi20F<-BRriHo)NawtKkc3jyjuV zw{X~=ZWcojwpoU&GUFLx-4Ov#$IG5>)-B;8ELi-%%F`*xX4x{lHRfj9oKNhEHQj6* z@xDx0(EeEgeU^5++V%F{U0X=ONAyn$n3%f$B4t1a?z~&L3r#=xmzK#}pY|Piun!Z-p z^XBtDX4$^0mN{uq7N`Vkc+)dG-zC82oox?(8}F>?_=TJ;**$jOyEc1H|e#9=Q z&Yj0LaBzsZ2rqB|6Rvt-T|BhC$3=!q6 zz?t8|(DGK$0IB}k3M{bY9LjlBKBdH=iLizq@zg4?%&)O2ALr*#S6D_l$1=*f+QFmEo=``l5l(& z5w$rO-ti3TgbF2uI;o~q5tkRYM9t^@?KfVIS8SGtBQ>JJ_>E9haHR-~dI(WdaI@_R zXS2+VZxEe%Zz-&RM{|jd;IlO)Q6UMcwXnZ8^}hf78K7>bMON=)v2X;};JVs)QshHe zPimMa@S7T9m>TNxRD`h^zBNnGn&EAuC~<^!Um$<^1ave+Em3z8r5j3meWt zVe$PXvYo^)r9;^ZXS1vsO54r$EbkX0%YQGM?UX*kx(ARyF#u&VnctXjd{{7)mh+ul zKYkkzunj~H7NUb)+-%43@omuKdPZFKbhLJ(Md%YLdQBJX{>>o#C+5$#BBF74d^h*K zGCK9)IL`MlboRU~3gJTRP+UUmc=H6mu3{{&KW83kbT)%gSjbH%*2+)uNAA=9lAcP@ z&0+_Z+IU)Q1Azvic9w7NZz}Wq63UmVl{wND zp>udsSW1V=zsl=}ke}<7gj!(Z! z+y;w!BxDj_;9{0*L;C)8|2XhAlxn#{0Kvw~x;gllvw#D;oi7`YqHp`J`R5P&O&wam zpMSnRTVfO9`LFmZGnHujj=GSM|FUu9VK>s{Umb^iT>r+GQF;0NQ{rGsyq6M335owy z9-eLvaainSYA} zot__HGti|iAJ^KYIcT%2J>OCv+4>@c>ssS;!qN`DxRlx~+l30^8?0BOv#6Q zz$)i$R)k;&{1v-v`4t;0XCoQyAJ&1s%MV&F;8}isrdvCS9v3c$WqiD!wUGazh~v}H zmC(UYScYr;P+a}|u3#OPb2Vst{5AXy-6)!8`>eJ7!=B#>-8A~$MXJW@gjBB)J_nsa z8B#Haoue|xu+IW)=@Q~@!jtHJ<1OME zOND&8xbpn9?3S30(!4?;@wxeHPN;mV3%RhZ#8dK??xhx0MEV=V4h{mSbvEwvMtzM`diTH3|f-}$&%yg__$ zQy3kY>pqn&zQAC&)6n}^9H6z5X)SXqufOISrTCxM|5GJ^cFLfuU~tDEzEq3Loj?_J zOMiOF|K$HGtNm*t?HO%(${$Nw_1}=EH?d{B=1}lD931}^&M?Y8e<8o2($tLsc^6mWW%r1Tx# z3b^_G7rJN7EZ{<}On)~#rhrR68rWv#odWLOnz7+-6$&}mu_4puxfXIy7QFEHnq0^| zf7;;F?mdNEWwX5a<+lns+aql%{ismH{j4>9zmHKh-=>JOT$XFiaD26W0Y2Xi#eTp_Mzw%#az?o9q&FoUCbr4+&z2$*J5r-zukjd zwEx6CYY8xSsy~)sJCRtq`fgAL=sI?_cEW749P!;}1!+Sk4OpIjwS<{=**! z;-y+_hyQCsNPuVL(f=ywZ!bMYQ1rqWQ+OGEmOC*_EB@(K@+*wI;3ck)6!hlDpI<_I zdCmPf4qpD(u;FjZ|F!-6HNA1U(EjfJZGGL}!We2zkW6l_U(Ds1m1QPs*K_su-L5DNB@1? z&71YM-tT&V4%^_oum`>vRhi-PdxO39auoKa$NkerixMV9OaUczu0o*^IMX{e@Si zwCHn=b3GXK@>P@b+?`DcL9(CcIfq+yez&=IfvfmHV!A5jB3JE_)L*kWhdVg9c-OE2 zm$-3u2l>X|=JFC&tdN=C;m(Y* zKNB(L4i^ph(()dcQFCYg zoE7)Dyr>q*>-6_HOWpC$_dDO`qP?Gtj@x*jJDah@t>o=}u69KIhc~Ki2uV}X; zm&+O3ab^92T&}cML;KC%54Z?@?XWGoA8@{rr{6mkJ>V)Gf2EfDJmh*=?bz{t*F$b{ zsCV0D1rND#>#xrL;`xXxm2BGT7x{==P%mM3pLdVAK55qy?t45I%kMGwu4sAXjOUNJ zyYHAqKiWRw*w(8X|GxAI7j-V%eA1mKT$3pa>V`W#<-%9q8`CZLDW_f22ahavr@N9wty%^T;T)nxxXP%zh=IgSjsN!?(#2=fs%xL|bdwaL|*4cs2 zxv1^A$KTF<&P~5G;o$2%&$-6#Ga6)_ea`9RL8C_JKj)^UY`pon$_uXh=~MS*ZC-F! zzC>OhKIjG7!K&(`=Dy$}GSB?zwEG3OW}WAUH`*6mplM#m;Jg>yDM`O}_bp#?GaRaS zuF>Kpcl-IS-Ohbqa)-UW57nOWlB?T%@NNC3mz-nS&z{Q;zvM~>&zNNT;3d~>`psp- zOJ8#7GkfH(cgW)=T>tuFFPA6Qhdl0C_}iPmEzRSOvza4HV)M8zAFpQ2y^zQ48-Ao( z?(ccrHuQ->z9em|! zuQ>nOQy%ZT_KFLs<&vTJ@#E#)HGJ@lBeu$1dpFaKaz-%{>@YKpw0 zw3Ktsh&r*N@Ecbz=zQIv^WV5r<0dKpi2TM)t*eVSnfi^Z8`~9s-#F9W^S<7$^^JSb zPO@d)`>&j7R9@ekXTEYfw$tk?H#4hBNnX%bPXFd!S*FiduGtG+&r<1E?wj4N_1Rxb zxZ8HJwI;Vqxa6YAHb-&&?Uk8gc(tO0vs*C+eUwxhzo2Tb#mF~LT;O?x3;mqkkhmb>r;BE zkdwDCNi#oK$nB|~*Q_h<|7v80SI-PD#9txz?8J`H z!R-sV(9(usM;a7z5401icvmjuf*NEmH2+e-1!r+?f4nH*9Kxd0D%>dGntkXY?{&O@ z+d9AQn4<{=+^*;~s^2yjaMkU?s@a7VaC2vMTUB9N0cYJbWOV)E1)STpN>xYu6mZpY zI^eH>YbP7|yPqS%{0lVvkQja#IQ6dAlRv#Ta9ew%zkhwi zz$HimD}}`yxPcW-I&)hL+%icwtA@)A+=VaRm72^laAipaxB84VaOUmrf7#aGz)8%z z;?Ka<9o68_2AP4gGB*vAOA*h%%I`DE`PaJ&V<=N@^l+N}+VZe49`KfzhlT!S>#zF_ zp?<~vE&T7t2j$xnXdEY$j}OYn1?A&`@^L`<{eSs=fBF4>`F(!*{eAg;efj--`F(u( z{d@U+d-?r(`F(o%{dxI)dHMZ#`F(i#{df6&clrHx`F(cz{dM_$b@}~t`F(Wx{d4(! z^PlgRjb-w0??-sGLe%5_%2ULM+?BBK_ z|7-Z~_04$y%KT#nl)vhaupMLie|Ntt^zWB`tuLhiTm36puPx8tcN&QN%PaHO`a=2f z0|!3U>w&bzL%){axo`~ElU2XMd;1qyI`$mmeLcd`&%f;_s_Fh7%zWJHAW=s5`Jl$H!eOF{?)O3E z?1x8)I=b%%<=s+_5@mG%4@z@Fj}uii4ghsEx}PK}X*>WTAE${j8W(^P+X-4?5RDH& z)pGA_qK?K1w0wG#GeiT87btw+>@1NVH-MREp7CKNjUOmo$74M8G>$-6=emg}Kb}BX zer?hPir3S)g3_1BFA^CVUjQ1dZ4OaM;|x&e{D!BR#v2GrJ09mLp>YSohW8OX>NaGxYWwN6@Gikhouy%&sZGJr(_aLlnGKZ&v#y<#a`rYNJqHz$y(m&er)Y5ne zVO0e+Pc@B;5SDFwFT~UM2w`os4^Ih=lMv2q9xIT>O9yog2q({Gfz+Pl+pN#mKWE`CF*FL1*$hpnPp0N>7MEG`<7Xi-+@+(l`%cS%*}f42}0_`SLQJ5*qg*teN45d z5MizK9Zv;~2NBME)tRS(#)SyWdWG{;(fANyX}^0s85$>2`ft_v>wt#FiwNuYxbjrf zxRI9E9L-Zf<41&5BbM`&(>Rir&rac~r|~4h+Hp5|s%Tt^u*B&LPc@A%DV$}`Q%&Pc zgr(AMJashQL>TReXC{q15oV^X5yCY7L@;yUKAsX9hazl{+~cXB@hHObm~T9FG%ls( zl^gKX(fAZ$W$*4hwKPsem>D~Pr-a6<6jns=G|;#eL4$1uPd$xaY5BohJY_VFMNr-R zvk<27EG?f`ho_9jwFqWLyYp1j_!eP(>}VmL#<>U^R7-g3XuONCtWz9MIgNX1`NfMo zl{EfESl#X|PZf=W5th%Zl1J3jcoZ?9GMD>2B{Y7f@VpY9S{g?qtc|V1Q%d7$gqgH1Je4%AMo_V7I8Pmo zuMsv3S->-s#@PrM};5qxHYY$+70PzkH)=@eW+>r)1&XBqPYuHr#)K4%}r?0;jPEry|I(d z4gTq|wf`TZdz|{=QBts-2{2S)M{ZvJbH8_W_OR)I)APR9VC&ztOS-h70ektjUBRp? zR&22D+r5h$99Z9*&hP7Od+xFNqGN2W!?x^();V4Fryefs z?au`+&NW-H)&ae`c~7@v*^oK?AX}amoFr5abnl3lyrMErYYMxwZ)2cM_REDtmlSY z+h2#ZxqJJ{t3fTreC+e6^C~(y<`{wjQe4md&ah+2buIV=p>?kNcU^hCRRanp^Pt>mC{jgB;$v$rF=W^CQm^$S0F2<79>w))}o^VR@c_C%Z5 zDf_cJu~SFeKYs7hj9rslchG_pUD)0o>dulM>d5}FYO-0^+ih8!R$c1u-_egfxwo|L z%3Y4E{Bu~#CzDvVE7vA@%ZTpmZ&yDr@!IXguJ3GV?>oesUAyP{(;kVv+1~?CWbEBu zi#0Q;_sVO7FI#E3=as@e-fT$q=N=b7R%Rn(Kh^C}+=^B7a_iY;h8MeWsOy+6XS~?$ zGe1>an%IrKzB)DOo6L*#OW1h-%#X(G{S)>Hwc|RlF%Rt7$CjPgp0eU~d+T;#gF8-c zs;lV9J{X=~z`lvL9&FdTtJ{@rugcoji`rZxv#)p_&DpQ><|nydZ_EzKt-9H!fit^cdHBd? z9s96NKJ|JZmD-SP($wx^_aUC_qIPNN8_qehZC1^yEB~Vl+pKxT*-x)GWDm9|@mznb zA6u7oUp#ucC)<0%r4^$syxF?bCbvlH+M9Kry|Gm-4^MXA*BbKL0iNvGUH2wcv~Xkn zl5K1!?vt|#BTF129=NlK>uu&MB6_gLwg-Q5>E^=*n{HcS+o2l!apSR6t3}n=W~NVS zcGovy>rQ>OO}4lvyP~>!(CfkN*n687dyP3#g-zW(EOA0cXLhf;?j}ilFZPq4Qo8bd zBev15ReopP8nIU#Zyhd~--ms)u+I7V+r8O38@n8TJ+~Koo(=fW-M0(tTD+|0uzEe% zE>-^c(y?!Ewx0F1Nv5N#u@*P3cR19MV=o+HPcJTQE!IDlJ$w99({~-(utUCmzV~}& zZ`SoduN?P0Uv|UEv&r{Od|9PrtL57BUhFilu3t|$cVutb_ek9D=gaPhEL~FFvK_n2 zpqi9o-Hok3zq4;{i6`r`XQrwsK7tD?$?xsE2+|_6N`s+Q}yw&@x=6`czCyYMe@w%Wd+s~tC=kzmG zSxxH{*_#YGd&fE~s$_H{Hf~1U((c=OurmgQ&c3i~G`r;MmDoN5hO=26Y+WOKhp{a) zcBxKv_G4?b%(7p-Rly$OW_F#FK8k%5tBPs1Ya}~r?88dQo8|0ftL*7%vxc(UQXaK# z>+Q`p?6&*c#*RbT2Nz3SwhtS@&T}<=-mXDkc8$4j!Kr>dShrD^YkX3Bvb*Q{_Q{Cv zB(|d=to@qBUEA#&!q&v|i1xf2&3>8L=6K`1UD*Lyr{}N!?8O!|o7*$=#VFQqx1@sp z`Uv(w?tD#{egvCU+b*(t)gi3TtiyxD&6~0H51+li@QNS%Ftl=|@89~e>zvO`n5iDc zhFFDe{iDrDR*`nA%(}ZH`!##`{qR2R*)sD{7B=sPvuQ`wwqur$WSh11TQ^u*o=NBt2F*a z7pDR2?GEZ@HT(y%jqkg}pY1%H?Z4Mt{v>`lTQRuy{rV1GY^?!{zZYhXU~4q$drh&g z3+w4xr)d875$s8a(OxA#e`7C=R#zVXaWv~|rh3dEwsB#zagO!7 zvQwN|U(4|s&i+^LpGfr$@b4#A{yu&6Zzjq1QO5V{4?WVPO2!%Aw?9%2-XzWMR%ef)PO$L6Whjqm5@DYnJc4K}{7KdbKJxf{9|-`}sVH^HCreg4Z1n#U%W zjqmrbUR>X?aHH{k|NS#VRH-A3@Bh~wUKZ1`sc|0Q!fDSD(+bOe%?JE`#boQ$Q^t9L zZmUa&HHk3J53J~EeRH#)ah||l)2j2KCdT=Kh_x#|F8x&cYu;c!8}9%8oN@lZ@uyvj z`df_i2o@hty?o(soKLu3A-9)LC*!x|XxVj$asEMLe|}~2md1IAx*8Mw8RsKh0w>qrd*3)O@lZ18ty8*j ze&VNgoptNw#(9eLIWH?8A7PxYc-?a6;tTDK^A>m2Ce`~{8|N>$P6hJ@yf)5bbon_} zb^Ww)K4aG^lh4}i#(9mS8?`5z1{>!$YE}q6!}K%Gb7*3hnDlCHobRY$r&O6#FwT28 z^c&W{F+RUgKL4@6q}9d)r;PI;5uFcDPTpyp53#v z-q7ASPf~HguBo9ljPoV6m4&sGg`a=Tn~cc`YZ8CcIDg_%H>Oh0ea3kdtuDNa_Xgv9 zO5F)29rw;K&a1@qa$DYSfN_51sdjbN;C9A&md)vH0uR(Q&bO4gG;2Tp^QT|)E^#m3 zpPi6voPXKy&HKWMW5#)y^FWpvaoyH`G|mTI2ua@X;I46AXizi92g;Mi`JqNR^IVy`!&>G|nIOsG(1t+rv1I^vz+?hY_uf^GS!_z44o4XPj43 znx1Q(V`7|N>V5M^RNr@hnrBkDm{huJoNubuY1q22r;PJX7THA`?URi2PjTj1!)IaN{smZ+s2Lg@rQv2s@ShV{$|XrdIFEI*SE~u1a*gv@L-qD$jm{hAwO$@8oUmY@aeix0 zHGLhEJ;r&iM=jJ#GglkuyO!OnS7-GcOeHZnRcdLRCyQ=;;M<2!#`&^p$|Zv;4-_pN5O&M?lWDVJu%KiX}aS9{?fav)`maenQ^+pK1L=NsqQ zN~_P^b!d`tzHLRwOwC7pj;(y&?fm#!S9|s}&cBs;FFA6-)i@9LrrDgb@y^EixK8u> zcXYBd&daG7dSb4D-Y3AToI>cI{-@7U;Y-B~eZC1&QHWmU@$?x%*+0bNtG$d5miZYf zFvBqSk|(UdxR04N%YW+FDRZTvQ>RRu<i>cnv~r_Y)m5bQE; z`qVCCW=(B1yG4Bi6n3uWkQzjh09IL@T2GqLtBs(LvFn(W>b1XmxaCv?e++Ix{*eS{t1c zt&7f$*5gUdhG5fOk1@oQ#4xc|v65K3*wA>@UrI!ZQjjIM z@+F{32v1NaME$0>CAlW4lOvNg$%)CC$yv$T{6sD&MC4K*A#h* zPl_rfJVl)nnW9NaOvy~iO3|j|r07y|Q}ikMDTb7i6eiUwRg!9#Dou4xm8H6-%2Rz( z6{&uyk~F(CX_|AIEX_4dp5~LLNb^fmrUj-2rG=)c(!$f!X_0A~w8XT`w5&93T27iS zEjLY{mY=3b_e)o%2c`$5ho-C2!_(F2k?ETB#PrPctaNR9PP#5VH(j5epKeGmNoO*w zG9(#x8PW{r4Ep5edmOhk#yLh7;~FE6@rhBy_{At=0%L+=LSs}h>X^uwe|L_gvCgrw zSl3u}Y-Fq^HZe9cHY-*en-i;x&5hN^=EoXhnK-LBNt|7rG|o9r7Uvo#kMoIB#QDW3 z;{xM?;zHw8ap7_5xX3t7Tw~CZ=YlW~KiB zIS>DJc?V^LW~eg4Gt?Q88Jdj5jLeLz3~feEhAxAVSuoDXRtevd{@jMBWdw>f9SP+4 zokz6Qd`*eQD#|X(Im#8S(+@2(6s)+QTvZ=s{DM(V|`-( zU5)&&N=2KRld4P2P1UF7ry5dAQkgWXwEx}OYe*|eW74hCCFyqQ(sbu^S-NYwJl!Y# zf2~$C3Vin+#iKwA2tw^wqvmI#*6UE?4XEuB)O1;fJVTM8%m~6cP^0`aQTDnFeTD%o zfKduFU&+gsMF{*1`QJ>iIPUiqU7iimFNvs=m|AZnNiv( z9r{88&YlE)pbY(=0)1Z)`aL!JyiD|WI`nmhXeLGyBSkBdqlGEax~OewaE7%pp|PsJ zwUEEI4`bWNjLVA4iPOd9#_8ko;|y^nadz?2c;|RoylcEX-X~rW@AsG9((t$I2P3J# zB;t_6aa_4LB4-?n3c0sJzWtEnMC7$3T8dsE2zkqoagJ4>O>1K%Xsar;OWr|p3Y&iNyMWSD# zGBGewlPdJ$Iq1LhQTwe>^PN%aebA2wq7M&8|DA}wI|u!CKKg7c^w-WAt{FZVei?!2 zNy4d&WEGfjK!=(o!+tgYa`kk@Ue!2%)ccm#7be~+UK0PWwMH2q7#|cLimPon&TwSB kCO#48IqSczO)^}Ek%9lb{!;?~DS`i#z<)~Mf4c62~ih!*o1d>1y0^UHZMzQt86%A@b@WOuIpL1q6n*=RBzvuh? z>&J`i%$zyb&-t9s{hTSeq0VBnSS)t@#p4#s23+Z%PyBxW&px~!GIG-p%L@ZvJ#&Lq zfA!31i*8=(UcBVag-hn$?yi`3#~pWu+&9j5FA3k_zWEM!VDeP=?RVZZe{^PMhR3Y> zpUs~(Klxvk4=4UMdCz$GSv+s@p8xP0xIXjl$qzpz-d8>RsYuUzxK3P0JiHdyYj3Vt zL}mN-v@~e3-1OrCmaB%hl_uVGTF$ZzO3ScV9do>O4@>L}YnPts_Jr^n~tED>>^`u#H>1J)3SjtB)xoKW#p2gCAFY=&H%L-hd zz%}Kc&tmb7mf0+>33!o@hTXX4^zH!i)sJ2*Gm0@{td>)dudrV}-{>VvmsH@DfXQ0C zr!13>mv8=UcOv6c1MnWOvN&-q?U!#HQvd&dKL7%%;qd>~YEh%sjo~FyweTHni+I&Ypb@=E zf<^!Zqt8`Ymdeu93e~PMJxf(r_Up=4NDFIMQIMPcg-N^13 zl76S0RKD`JR$fCoH@!z$zqp^Vk3dyIY5;hT-|c!H_^llZ4rA)l628~8{ z5B*wz_m06d<;b7|E!{kZk_23}2giHbn~`i!q4u)Wh7YhlcJPzFueog5+$z~zd{D3E zR`hQ!6&{+4r`<|OG11MB-Xd;t1w=Yxc#_~tp@$ml)|ua>vmGEKx_8UcOx4J|5OC+N zVr19zw&>nwO&Qlsl<7K9jT{gM*AkG+fhx1bY_wu+d#*7CJN3L~)%KQvqt0f!^t|`f z$h|o(Jv!61sVkn4dRm{%M60V@bi%hYJdQyx^-bCn(DtHBX;)(+f> zj(6eDjlbMr^&!<5QHI$GJ2VhP7him(#ezJM?QxZT{rR8OBYVJJ_zmFODr-|l3bO7} z)(@)9P~*GR_!c$Z7>iMr{Bo2;WzVADn@I=&7aApt5x|dE#?8}YnblOrANJ6fSs=w?8;U470?KrWDvSoM^Gzv))oVB#W}Sg4bkG<+Twg%3Tumf=xewL zbks*v7R5?SF1%bmv|P;QBfli_Fw2t*n&vmBG?(9dBh4mzOJ%M6*)eFxL@Mf}Pj)cb zzut4JqI3L1uP8FHP!bLehyzX>y|)lcCRe6ihBTyrrA^F{DWhZxDBMJFaw0fcG6f5v zmB6GDgywb>19F7F#IMxoZ61)t5C}- zk8z;EDrz5~MhY#Vv+z`on^nW(=;%a@KT9>nSs^Y2YSaA6`uZ4FmJK)Hb*=Hz9Ec}j zM-x_qRgE_RRl|Q&a?ylt`=_3VcC)SsEeKY3g=SF0`PAH1)ZBk;>JqfjlxXVBo~AO; zRKAs($fwG+_*O0cYDXMPQDxPhS^|$T%TubcnfV&K)u*wgs*;sY`Eh^HKi5CU#7Q0a z15g9XV&>uPm^~gu+ zo{v@Uj=N7$o0|n)04vLD(7pBd*sn3hSXE=PwV;d8V6qxH3hKlIb7`77i8_r@n`PJ0 zd#vn-#W09l-4NJ5p%e~&8aVyDt)1gMgfpGM~_xO0)rr_6u%2< zI|@0ss#wW$QUpqsZ6&l^oP?JeEdwYQ0j7(AU>}2;6?8>kke$M~?^KH}4&P#ckX(bd zNKT4Yn=k7_V^tPJS}BlnZ`iIHrTKFB&0ojkuUKhV&Bab!2o|>rSlIYQm*Fv5?24ro z98jYpeO*n~P=+j)|4lr;!H(>N3yXmZn6O*jz=d4kfJu*(_5Pe5?z?j*o`w63c5mhR9h?f z*f2K5HRvrhvRt)TG+9xK)-rO^LIe0>489J% zEKC{Scfqri;8`ZQQc@5j!S8Mob7njOG3QmH*pO>nYMxbX=uT5113{Z>$l=ZZrVe(} zQdM=*;MmC!hqOrhH!5r4j{|(*JcD#r0nz7ut>K$A$rw6s09+0GD}>J18210d6|A6u zkVZ^VDNRvX??kAUsl2@Ne?>L&1r={WxT-2d<%+{!FZ_>c9R;70;nsC}GEV;zDt z5~^w5dLjK?L|{3Iz@p{VlW?ec8&qW+S0gQPq^RC)kjIQnH)VUMke~-3gBu)Dsvp4g zYsMG|oS?9j>FbA)=@yZRL`QG)(ow)+u73s40x#yOkyW5tHkDm2>6Zr(jh#iJ!9UgL zav->rHLbqd!q_D$YiP1sV&@afwM_(Yhn}OeZCsfNfD`otwQ~_|lwxUn+WI@^(Or2kBnKy%&qwzU;T7L9ek;7prXSI=V+s$I49Q z{1jwTi^e{R^zbm|xbC7fOT0enn2f9Xm>PAgq5`O{oS#lP+1T4Dft6r+FV9(evOq$1 zIwb7_O3kkUNg{@o%RQFR$$Y{kDBxX|g9&0jmnw6GSEw~zAq@E4*b-h-s7(Gfc@OtU0LQ40Hj5OcuV$X3yyMj`Wv zZqFC5T6qVqb(FM-Un`OXSeF4;eRJoKiV1!qA=-zdBsj@pAw;brF0$O1l+W!@vrI0s z76T`7NB<5kQiWAnC)f!1Q5|JYa+A!OpAlyr1o5Ydgki{sCxgh;9fzqLKrMo)sO$;P zV^m9I>tfk=B0#?W*icH-UZ%0bQUfO8=WG&iiI3waUpMHEq|#md&{51myp>3F4B`$% z7^2Oz=1uuDv7USM*G)5o1`)MMasin+l@3d(1{j*cGa$-ZFSl5#>%-8*2J!8f_d1ZM z@3z6@_b~h(dN2c(f|+v3ZHSNEpffE;iyRe8$x32I0|@4lVv&TWfwcBWI=VQK$y5XL zI!ho)vDb74=nJ8^P%b@;WKPF&aDPxyjH?$FdAkk-Lc6pV6{*I2^r9j!QYMecM3x3n zk<(52-_|7w38Xy|e|pHrXG9DVUOyfi*lTMyXbkajC z2z2ruEJ$QNW(|XO(LjM|7}N#M*m$Bq@AspX<4sx#`MeSN2F1>h<&xCVr@ZopYHPt< z@pYgCKM(>mexcuh=7u~uv|8LM`w`fQiAb8aTkw*D4`N(RAYR}l-MV)-ED1+}JK>Qa z-su{^#_#%!@B-=%E!Ugy&_Qy45-^*ng7c^XkVi4~=Mt0&IO!xn05~&$2oo7?0_GgT z0%PcCoi+1a)4m4)x53_t#p^S9M?S4fQ|e5SSCaTZ@Ni51s?Qyi7L-frePwJ2@!z)Eq?TK0S%@4&jdbN zCd4~|MT25z6OMe6#F0Ue)vzEsYD7lCQdbf*D1Q<^#8Neeo(KC_uAa9|we8|hPbDw` z2Zu{s0@<+zYCX9zAY&tL@?sukfxJ)+3{7LoM-=nE(YJW{VL#pWzvxS;IIMQl2#~X7%Rge9O#D+C!Jvmq! z4zof>)YzNxQdRSA4W*0z#b*7lcgKP5$bS6J>{X&0&4G(^(JM;~?g0o+;J1}{(jk-q z;&NJSyTDCzNJn_Bw~lZr2UFvxUL@I^#$M$ge1Q~=6ig@=*D(D&F=GS7jDaeE^UQ+E z3`T3(28ddNpZ_QMZsxgCzH5a;laOICq_bwaPnGX3M<&R3>yS?J-92YZ`R-0!g?#sO zDj)=yt)YYylJ6D*L?ZA^67t=G5>#MhPd$=Im_Aw1Oex=eIjTp# zTYL`n4I*8C`L0I1YUKk3G=zM&L?rc-@5p|dvL1z)S13)WPYAnKK=8nVc=T_c=P=#0 zfJ-q+dWFxfG2Pi`oe5PjvSPTo&W4-otmZJr$_A+wetTp^j>{s~Sy^?xQvD%STA`7C zNp{10KK!3T{%PU)Se!ZoFTTl>Pd1ZlNw9suvjGKo=xnlr!t6;B<`4qoQYXA|8U#3} z%8qD7D?Cn334EC>uoMMI$cETG!1T%OBA=X12m0x@Rt@VHMl2r&2rUsS)L*9L;(39x z8owoMx+OrQ1=PV!0-}%lS`Iw+$FD6E@@M4=Pq{@2cAIdDfy_K141&xA97|E7#S-bG zMrYBu`H{SYB-qL~cM3?zazZx;O-rs2Or@M5ERw~&7D*2~lk$xaZFI8p!%aBFsk1F0 zvL$&98nk5H+j#c`%{cvW)%&(my`LrnuLaWrC&}9)G-+cDu^24%W_~NsQ#Vc@DHJk2 zItS1;9f@-qmB{0we5ERYk4gi>O)(mPWo&OnBH0E3zVMW(rpb|-X68#EA(T_ z-4Ob=sC&4c*QR^-F0~izh0JPe)4XjE#UXFB@pmCrsB98OfWbGYc>}0-dR~2Kkk-(i z25a@7N~!;o(5b{*H4m;o`{QbKd}>dV5HjVTkm*!ST5C-C88$_enf8*O!C!5?<>yq6 ztL8ym*xgHh&L@fqi4PJ-M^vnPQwI1Qf#c-!1URHX>=q1L8pS{TScn3!(g|6ym}CGy zQL-MPc({-sA=v;TWXwvogJ+VG`=_>I8cAM$aw1^$U3{E+o2W8XPLldy(g1EOdQ7eQ zBpO_Y<52^(a?_H%j0=k+p=ff`w%Os!Z6oT?D|zXq1y!JNr{} zMUs)-MWO=6y^joUq(KJTYJ>G49ZKcVyij`O(F;RqkQ^(H@idTdmBB@0MUm14FbqwZ z57)rfP4j6$ER3P=;y;;A_ua}X)5vfr%)zTf(mr(DzY#etB}$+J{X|!$gDeIugq*Mj z@2D(VtyELAi)sY9nYblT=u{p&4T++%H&L#hze;o%_><*-E?*-<1JX#|gh{66KsX1f zDg#{;>F*<5>c!=1-ZB64`S4|svPNazAh`)QRO+NeDZUORf(3_S`D)&~sxj4yGCFAV zBK}{4@`mPkW=z0HAZI}Z6Vv^$cIVzemX3GKUO7ZM^f0Mlcm_J)Ikr;Qd zo;Cs#ej+4bS`1|ngO%#( zfb3WcenMfzWmC>0mWVYsi*l#{$PaTUVbNfbuEiPoq>u z`UJ7~L9Vm!8)-)ts_wP^rvPP1`2=ySfvogbz+1~Zf{rD7WQ>n1>k-#et4s5hyM*mF z1(fL^E1%zv#ZpPb9MVEMLA*H? zaBPfa;`g;)c#|@NCdwp6Nl17oQ1Fr#99z>Qd}>P5NIi5)1){T?SaCbaE8O{fHF zVg*122lm5ct4DA0q+-7bt}7uY91r^?%_2z66)0i)=l&PrMI@2vCCCbJbQ7^OCN5;T@3rc#kxA zo98gZ!8W1@&B5IXEz(8Bnd9bfffhm>j6)p!4T*z3h=VU73h!xUefUnD-F^?4>_eb- ztHDRtk&u`9mMP@fp$h5b!V7Np{|)t_!jG2JDDAjH-T}2}ZbHOc!*8LC zm}MBxY<~7SkuDdSgFAK?U-coH1Av2;F?k{Pm>t`02|FOmtCRSR`vKJ>k>T;TTb36qi8?isT> zG`YHd)kFv_E%5znc}=RfW$73-@2CbjEAK7D7ePK53n4`xYeis}&KmgvU4#T^@n#Kb zC zfks_MYq1b)_QW{QcaK{(oRnNB9)?C9bEHBVy#d;6mc}kgyjBqLdX-prKF|@bN}lGc z4kT!o7PWhbb(;nbOmeeN7~{;Tqf!{fbz&6%`-1>Vyd-JLXOd>T#rGomO45ut(Tq{o z(Bl&xvDmA&5)J4}D}?uU432bTrd1D2Co+*ZlM8vvO}t( zLi_GLerPu*IDRs65=-crf6Tp~O+jQ(%KT%>#r$_|=|O;i=i06BpxL5b z-sDTWv^O67<<8i!1Rnh5rr4nR4{Tj~D?e~o?kLj}m{4l#g9S}KyMkL}HNQcw1xjwi=gRetABC;BAKmby0ZR26k$&9))OYr^BK@NM zcRQc6E>N!98B4pQ9_fxtkdABiS@M0$l3{AqvD%X@mZd{JSLWIEEp)3#HLz22MEgM7%0cb| zo_U1sr3rMO2YMIq+}O3nl|c8Z4`^^e`O)WBeI-C+e||yiOxzNM&R$h3V2FS{4F+xs##EK{ z4|_@hZ!(ucXrqlRu!iSLN;u7f345KtAB)E|vRggiX-Azz35Nu5t=dWxE#f=p%>T`oBK^AbI)^!~sF>j)Mardee*lzu~|?I{M*2#<=gp0h1oCC4^y)@rRRBUq?8D zH38!j5o#_nfG2#e8;;G8UkQav1@)Gwz+u&7TwB5e#2!E#Nv!RR(NOIY<2 z@rZVcM_eG{5ubjQ#JYCsP8pPB`Xw1q#7UxUjAJ5fi}fm->ZE|6$KDW15EK^pxbhB5 z&-GM_BWw7`uGzkk0W$yU)NEg>(WOp3I*Oz~jGnrQxVc5biz=h!eA7ipwHd!|NQJEh zZ9-vtT8xl6>OV7QAs8=VM@yh}QH1UEN72>W?-9h<2P%Vj021MYkLoN-SwDR>$V6_S z_BL{l1s{cxmmFgq=h#p)gf$qOw{zciQ<=Ns0|I>~R45tN2f=lMsm47q5Cx8_#%b)G zUV7Y9loQfA#l4#jJ<{ZwgUt$oFgky8pmzQ`((E9nRjyoZ2`%HR-lT@%5DKnSy^W#y z(1Dw!JiCskArK?AbW?C0bYz6+EX^j5j26dykWqLYUj)#DjKf?R4XzW=ri~H7%~0KQ zaBWV+;!?ae#VOs*|AiU+u6FMS$v&}mJJG`*&@Kj-6xPp1BN|(s3psu>^aQf*eofZh zugSXmjh{_ODuVwe7pcxII))VbP zMP4Un1UfSAFt>AsJImAvyT~*+K55w<-xFjpN$nqGL8ier3QJ(SQno_~jSvBSrbZ}w z&^#d*2t&lCXV^E0VT=7fC#Dac(8$H8NxPP+R1)>pj3%Tf^NUcW_F*dwxF)U29u;b) z-0&yj)yya@a^||s5Sc{yH$s&`lxl4tE%|29j320n_(>RnMH{`hefF)-eWLX>iZzTr ze}8?sCtBaq|4<*acZ8kv_^*$aAcDK0+fM%L*=oZ<5W=^G3!8ir*kq=95uMiw5H1k^ zl#$gm$rc7@PlJoX?W4t=%>QMy_)F&Rjuv+_|2NU%Zz!Ve9W@Gbk+94~uoDPg?LVO) zV5n_0z1oWKYQw{v^lHDc00dtjBnbX>(yP4}UTyR)#jD+-dw2BqYEu+l8-GlwP?OQ- zYq~KR+HV7*>eak-IJfO;UK=q8UoZZ^&jCHr*<7avq)Fu4CxL)X7rlC)^mLo@mMLzD z?Mop4`yq$7)9{*u!xHv7vh%^3d)KX2OFWz12xdUu)!?1NW__V}nLP$C5j-Eyt`P6L z5wVgbl3L^0rLxFIVdA6M3;qrw7wX6k z59&zIJLPi;3}*EBck}Tj$)x{`$AbC}Iy3++07lzxCa|G}#c)MTpfq}Lsukd|6@ zTqtwoa~w!coHxNS@YF?5yLgLyIz&&e@=fyT!w^>IPX0$cZJ_>=ZA>hzs;%vK>>=#4 zv9sfoT>jUqK%7)3*_rsAxCa5hl>BEM9Re$!#D+Z=e*h5*b(FP?Zx@wP zZ;1WE&m>^OrogAMHUaRINtN5z0bA zr5CvQH4X`hH&h@yZxW@I|c0p}XTeFPd%cP@w!pWcI>uVDlkl#%ohN8l#P3uK;Li2Q;=Cl_1C zNR$HGCLLCsdD45fOy$#XxTp@CfRZbxEV1{w$V20<$VHbN7tWz9HeXk>I3T5!52crq zEI^_rF~>ZMB&7{DV$xTlcS_A(EEqZIDnE+-AHa+e7bjjmi@e(s3hUef(a$(%FaykXw(A_!BOKWh$=_MTkm?-83d` zFdS2^SLfjpl9JOMlqMn}*-7{8!jh{lUlXE>7$DsqO?1J#;= z-fNuUL4l&Q@Bs0yv7jEZv)>7@(|!mST?5fHHF^!iz%8KnhGY8ytnD5=i`?WXgm>TufuM-RKxXpi}61cE)sGeBSuZ6yy+I+`7ZV1UH zuDJnd{uC0hL-z{^je75MW3cX3!hQH>5dB5d=H^>L%8>gXBw`xb>vzcMbj$NQ@-SDu z>K-raUUy=3FOqdXF6-XXyYAiKs=ey|SmwU4JuwWGG7uC=TAANdCO;h^UgtCU?@H_wo=OtN@8T+S!qf=b0rDMr( zs$A4f1}u4q5O%hJej&m(G+Z^7yKt}t?Le7;thF{AZGqj&;n?v+h?IK&b=nEn`!cfr zhkE5Y#NvXCi9Ndf8!<>zm;h|<$J$8nabN~AQm!|9+7DOB>)Gib7el55I8R5wI~#8#<-E~sjtLhNX>KL#qGmxWi#QHO0Y$U%Jn|p`&FRP~ z*2(KAP?&<5d*b`?K=1kQ@e0AwP;$VO(9m38iXyJF%-_SY21DspZ<9#8M~^z5Ktc)F z;mHu+X%F_KXdnZIen&NV;8=DIUBF%wn+_42kwvfP%hz|9uMyoP&V>@M5!Y?XrKWte zd=X%H5Q4I#pCt0QhzdOO0*zxgUps=JjQt8d94?EH2O7Dv<6MD8@_v-ON3145-_W=P zc@lFD#=JjXnjhH__ihOdgZx!yMH>?_{KR<}%KDQ)mrZgF+6wO?$#sh`Yl~r?TW0R* zDuAy7?Dk`dga=aGs)_%@QG;LmzQj-U1H zM}zlt1!5P`XR(+0MEiW3&L*i^q)7I;yjPz!Hc<5WjYOaSO2B91%aeUpd-WLyb`5|P z7gZZXI@mK#%AW7$8;YjIBh=*^Btv<)0S}Okxuh3_dx)(D>M)P7fW(BJff}kPJ z6Y}8-GY{ewjbe1DxD3~6xR#Tkh~LX24Ao^`kfbd|c5BKYD>MV#AMK_|XeEf};NNk|<%X zs*6i`KwKbvP@+#>$|w<(6jhBwl~X{Rr>H3E#vcYb4ikv#P?Z|}$zy1lIWB{{MUO@+ z@er+|a~DV!{Ez$Gmdd?Bmwo*y;fmbnwwdlz_qk0%ChT*28tJsp?IB$09D_BK?=dL4 z57G^=tej7yoNVk$N=WQ;gNp-D!3hNd&uCNPuh6@~>ph`iypm>_jxj(cd5nRX>9|K3 z{22zPuwFbu=_mW!6Y*oV^BB0DdI^?IC){zq;OHsi|Lv(;s>X5xr$RaegB_n?RfGp`pam z)Cw1lVuK31)PXG{BFq}pDqdR1y1^5@-NNTZ<~gLfDfTF7o&oRza3sP$33No)0x*$8 ztsCHl16GN5NH!mLmgKeQMIbS{G7i6faN49?ZaBW4OqkH42OZ3&p(X{f6d4QsmjV`Z z6>KNezYt?^t3|G*o=7|%!mgKbac^^2FzYz#DE6swC*CN*-K-(DLS;Xbv#>XGJLO-b z7O_h7H+&L83U~8|u%(;WsGT*(Ad#T3@v~aI){0!Au{zGi^VMESG7ZFmlRzgHOZYi8 z8&S$-S`}&^gBba9k_99ASpc?%d z^(!`0jlN7bpiC}Fy)fDnp9BYFyY4Jqb0%m;kx`993&sp~NDQCmOWlFJu^nVP%>E3`jjNyumboLE?Nhy+mj zQs6{*5QMDofS}>GQ|N3x&&P5@3Rsz#{eXeZ9$?7RMKoO_8n2fk+<&Blw<7AKPGy&U z-9g=!bnpn>r_#ak$OJn09@2>puECY);A+Z8bg+qT1U1nqCmZ`BC7cKyT=vNip@Zj> z=_0Y^_bystXf&5EMWg`eVEN05gc}iFgy}cwAo`M^gDYu6;PJQo{#3kb<);XM2s*f0 zB=w_%Df6_0|B||d^#Myc$;=%`K&pxVkW8Oq3j)~#=7v0_{`)(n#fu-tZZBva;K&P0 z(fw)ALm=eO{BJQIE&NJzTONAiIo8DV>kvI=EP662o&}X2=?o%1bHsN6kRsCjYO##L z&*=<3&?PJ|aNi22YJ-Dt&(hN(yW`fvzWPI22h<%fgO>$;4?969I^aS;XQsMUBSO?i zg&_xG{={Wiy^B?nb&Iixoq5}FQw9MbA}E9%Z@&$}L(*pO`%Po7u`1qFrPNQTPyyX{BM0v?Dc&~=x zCI~7ks3*Lox~&9T!-DA)W>ScoZo0|G%@Gj?gBwgAL|YJ4k~$Et0qt0+{fO?p+g4>o zx@}6$emv<#x1YN-h|`*=D79J?Ob=J!Nw$xeceKrdacsneUHq1338Cbu|1i;Fp&>tl zS~|u+gr;sLIvpGar;r@c5uOh+udJVaM7&&bBi7!q*ma1ir;0C1Y;$Bm92rs)_vba( z8q|tzsBPzJdEbsxUKtDro}eSVoVG3?z+nJojJ;2#sTQi5YM}$ucJN^UIctm!jREce zpx6X4j9V!c%(Ucc3SFBDeJMqT&c!ixaJn7z&z>tB@d-Q>hZ8TxG`94?!FzWmaIl+j zFtV`{Ft8}qcVIRvHxduR18ftRaVxZ0Ge#l(lKPHG5-+%k7wy1{HjDr7B!K%Xzm=zP z-LAn9`VMH%PI15_W~Gfk52Atn6YZE7tmT!QHX{VvntV=}07}|)h8?WN^fnE95HzKD z4`!mR1iKY_@C6K_T{i}&p?rNG#K;E&84zy?FOQ`+MMU4Jkwmp-m8fbZ+}7P#XXuWZ zxLud1fIY{;sm5nwqOr9wJdQdD-3|RyPS-D6knzWwZ3a7)tdqx_~BjkKCAe;vpQ!Z#shFX-d z^i(1+Q{hog(+m{{c4qi%^I20zjAq#+F-kQ|z5f&Paw-o0*_A+jza|!2@K^`Vm9hA0 z!yf@HN19kP3|N$I@&8R?(Kix{&Yb`(`Z<^cuxKl>n38mUD|iutr-osI(*fX-0*lg3 zEK0|ujRY3qz@H=*rT4)i+jJ@`XiOo+j^@)wA80;7>{$o30omffy^ngRPA*ZM9l&nd zl>5-{aYJnbHAHI(?1M0)79Qph-IXH2s9vGs~#iWG!CXdK5miHeW498C=q86C6%&~{g2cZGau1e{f_8nqK<;#R$ zB>0$whSbYe?h}%k}JD00c{8AQkF(naB4i7l5;%4_E_VsTKQ48_MEa;hbTMoxGZ^Jmz4Ruv_d{ zt0x)7nJnH=KV2 zyl1TqN1<|WS6&*bVw*rE6xLx_dkx)4Y@q_m=AmlC$4cY_D^&I2tg&{MXE`<={K$eV zh%#FyG`!dlBrikqav8k>6Q~cJuwb$vNWu08g}iz0^iSnY59r3MCubor54XkeUaq2_*5X=X@p;&fB=eMFC+}-@7Sp=GKq&f@d7J&VZN{+iMJGklYARl zNXM4;pnKAXfmID3ju;XD<2zVYIQNpe3f#$Lw8*Fr4{WKMnkH&KN%gkE;)D2O8gZoC zx#S-r3ss^ZZOdkeD=Brlz#V;tKmB%o}W^Kv!>NfdPX%9x9#6O4AkTCi$xCU5zYyi6? zz-}~g`Zp4%V}#S~80z0?I{PBFisL=7#{sX|!gXP~2MRBoz9 zP|*x*LIxXe;lG9u-!#F6_6WVf(M(R3upd?~M<>{R*vy#b@!!9TRhHz4KLHZ-;fM+NQCIGlT{(dAed8i}Y|w_|wbc4=0dgkm zzxa6d3tkBSs4)fpQZ))*3xIwcpee+j8E&UQF(dQI3n*d4 z7QE*FmL7%mA}hs<1cnhT;aI+@u}#UQmjI<~MzI?p%PwD9D=#Z3f-R z+oRm~Ypfv{Fq})~bv2}iF7Ilvh6del&(Pv6wb*3e(AAI@QWh{IKv7-*LJ(<4>uRuv zGG&roq}XAQ)fze+;VJUQM&h77gXjkQd|G^axD#ihr<0`?GI(Pn?RP^Pq5(7%{(=%^ zIDN8zc&x*J4SQ_e5K_ibg*uy-0Y=pTVEfzk=oAiRA)}L0yU_>|JCWEOejSaC?6?bP zj!F{aT~MdW?TSlY+<*!#N{&54$q8im)T)L`Jfj<*s8t;lP%frMXM<&Xy3#lhe{{N? zp5da#a`33ZI@UM^o2m2+H>KsGTRuu}r*zmKbhBge7E0};)b8*fv?@+5XjR>GQ6kjI z&dR|aOUFjw6M-PvUon2bpiZ;3^qn9zzCAHVVuI92M_S4xi3viQJvm8<3BvhbG*dJ| zkq%HXnrHyd+A~4va)Ka+CYw0H`3Yon6C^rOGffaYqEmp^feZj{sBFfMEo52YH^lTT zmDAHRUNJv#wcR7<2XjHwlAI%kwe zX@NCUmGyhIjaKuYrmWk8WZ#lA)Fb;N@27_b=y^E39bUl}!i-4!)xeGc;Uf8vE>ICzH9Dzr;cgWz=SYJkaXx66={Vnd=pr$5IH8S#VH;Spo^LG;jq)o6;`O4GT9q zr4xVM;eD}(fdT=H4)DhxO3>_xBDmQ{Pofa6oE|#qr#svxSIsm52;x2bujQzOWj@@1 zNhqqV2kwMUW|b|t);K0ZE(<&OGjk}5UBbb|-6xbx}rW7=t&f#-#>~r*W-fwo9|&3`^U) z0dIwUm4;vE>SB!z5g~`?#?4!?t( zk=zpi+!3%^GnW8SnJeTc!JcLMoibf)Cy;ErM`q;L(#z;$W}#m4B)?p~GYir?*gaF? zqug?9KI|~Mq2de%fbj!fgC&Li-74ar80RzRq56uIgIOmS-lZ5BfO)zACf`T%RzaG2 zooF2{Y};$zCH7Sg?-D1r97bXQNtlAip#l%^W8X;39$?qlb+!%l)82{oXVQSh=QVDV zexEV{82P;_bN)?Jfe%MSh~qjBlN?vdZxC369kWrw+Yz7*T{B3qCi%twD`kOQUO>hSu=0QmmLz5MUzCw{!>5DOsd@iojD9}nVzxT zvr#JgSRu_D8_DbGkxhkw2^|>(&9fL<2E9Zk(Kl>nK$ZFU%Oz#ntujR%z5fRHqkUc} z%1fo2kj3nbY``cMQg~^zRa7W@2>ii5p{VNCqrMz9ioH5f)k)G^ImDYt`93tPf;0O# z_n>Z_Rkl-S02P%D^2ggqGeUwThNG{^%D1&#=#qWMEsk*@D`mwESbI0oL2eMAc4IDs z_}<1Lo)2sTnjFBAJ~WS-nTC@dQDeCuu@b@PsqD32Gza?T`D(>vc+%~^1U6NPLw%um zS-9~F_u<3+<8s2nyzEGh>w6^N=tOR<621&qL7HWO7H2!GBeAcIut zP~+T2o-XoE1g^{89>lnM?;ro8MXQu)D(a|t9yFU=BsFV=Ddbmfk^c95Ty*wg zp}2euLQUVQ5e5t+dwCJb+2kd~BI1K6s;r_W7dbU{ipCULl#7)0H9jh9jJU^57jk4q zgS8qP@1selH47Vm8-;L_T}y=f=bSt7O+N0?x-e=feq z)1e#1I4FA$NDPiL;ET~tfXxKmn~qOQHTok5)1>O-A64(K{9r)+v5_zt+HQ_qXi*V4QQPU5hvR@0XkEp@|)E4)w%978-<)MT>92er$Xl z3j!*)K{k2qgK^bE?V}^8@~Pv4=;I zCN)}jhTU8gY$piXab9HR)po}Zz#C|8<&e5$L>Zzl)d0U ztWd3RTzHRAcRxm&YHO$}q)&*44{jWU;V&psbHG>+L^hDtIOOhuMUQpkMtP(WRr>fE z2yRg7&WenE9WKl?kb=i=hBXxepoL$w5O;C93s>Ohwz5Rl|2DJUL0Mgt!Jf=|4P`xV zDdIx;KbE}y& zkdh`LX(Lt$&ay=dnBsI;7K)IFQ1lOWlOe=$5ElfjZ^eaWZo)4=uQ48H-L%R<#Hc{q zd{l4{Kd^jvM^<=zmar4a+mN!8AHZ*&^h1y{ih@IF?s)4GF$=jJcSGFSxl=bX-~S^_ z9`%?_xQ?9Oj8I;@UbD4Dx-VSvx#2$)>CU=mz=llpwXv!pK+{ba8aX<|uZ+jmp2z$w z&|o*~_QJ3!y$6Qli$7r(ja`k@LwF|t0JSa7HO2%Dgp%@B;yqM9yq7!x=Aa|wH#hVA zHJJx1;$6JZjqG0{jTVJnH%eWwvUt%YuryR67C~QMTk_0CGsc-Yc06m64f)YS$RrS_ zqZ6MYti-8lu>kuzz`h8ugYelsS9JYQrMeilBGtIrZY;GH&9;O`1(cWk`I<5DnS#Ck zjdq|6$w+SQ2+>q3H6H>|#b@RCUkgEH|5k~K7DjMCgGfaNozsd?hP{brk~A|@p1OIQ z%79~3Ap;JA`dSwwM~Q;h1hjNY5Ahn!VR=SHRGWwT<{F$2cHYMG)vG4D@@bC ze%?7o7Qgqqia*$^xLH)%w?zf(%nGhV1)A}vV`ywabhgD`J9o9eb`Ji2Q;8&h?XT%l zgrKZdMFJEB2~eCbKrs}cK&{ARdvifBS{L58MXtS=+1f`;{1aox?I71v$-x45!*X>0i7bk`W0rm`w(lNPV76UQtYjw1!| zfFcP^J}zEGtEeA%b31x~MA?rjF*?Js4+2#5d1_pZR#9(AG<`sPYLu99w3}?&$=v{O zN5~Jbl3ODA;Z;G0hLLH538h^1W*&wt7dj=On-k3mJNaO;aIhBU2MHsMRFr$KG_~Bu zUghqUj08l=%2U3eG0>(sp4mQLS9MlHk{;|q#y_$&p^dRmf2c{(ly#19Z{ zK2-$G8Z+Fg_su05y?Yh!Xky2x4RP$}z$XFe%#3@e^{Aus4QzhFew*xt;u-HJI^)?l z$d|3+<-1fPj|(|m1W@?Q_<AdTwMDfB|@kTG4$SXcTZG~_n z)3F0LdE6FASTNy;t=ZFN2)D*}%Ix`&4gDMHFgTbefIc^Y#$d0wi0+z=B~|Gi9ri<% zxB7A7Zs>|lKn1MF2P<(27}vx(mpyUT{`esz#UL8?>A-7{0d^vy!N`t|CfsY~C0}Cr zMDD*r3ElAI`0pR%sW}ubhL6{$L_S2SuQEPbseT)G#&n;~T7>&;7r$3DCO^{Iuc2G0 zAw>LyeX<^!eAF{nsYWUEd6LhVj`%35i(eut!a*c`dwaI5BrQA-mAID9^i{`015nEa z;Zk$dvIYSM9{Y)yhLMy(sonuBM1G~Zfec4*aaiNqI_wZf`n9qdqRUP*3?GPXntV!W zHyqi0@}bo9tZ~8zL}2a{Mux-uH1K~+{ai%h`)Eo=Kw`V%LeeC`RRG_&pl}J*k3T8g zM3G7)Co*OnH{&wOD0O;@(GkL7zdjG5KAU=WiVY|!Pf^q~JGyZ?WFvV{HQ#oT1(=AM zJBdahmW7Tsz+hqfqi}T^XAc=R7TI5(LS@A!jX>>6e0InUcP0!cnPquIHUgyTI;O$t zMMo6ic*X!&|KAa{ts56!jvTsi&mwI7ya*Y@AqKDVHlQT%tcExQOq-f92Q{9|8?GP; z>I>OYO})x&qGi!SP@xDt$ET=Ii)_mgEtQavBwCv+S{spzw%-77!)dMzyD1px6tZasko(;)uMDG41>fjh0yLVx;TFhDEs0A$Oo?KBVG2 zP^3OX!R6GT5aJ5qH&m)oGuQ-V*GF-Kb7hq32XRw*b#Zu7pm3oztYSj%JBzr5Fb9kT zw-l%ZLxD389kn13t+3PS8n!_690K08D4jYw&?0^A8Z#ZnP+ zil|e5(2^t=XtHndUz>?U(@4g{(tHc3%-19GjxdS~I8clyEHa=B$iY8-L||L>PVtUp zRn+Da8I+fN_D$4k6vrpL%H2StK;hM)AzIZpqA!jT_Du}d^07^)6Y`=HB{-XdLWk)! zuQtT6%XQEPMC2R@W%)M@zzcCwQ7hj=Cx`(Ea57^rf?pmZ1Pt1UbVNy;usc{FDsgCR zTWoZ{)qNo(8OSt!X!kHml|ofJ+9RRrQrco>o@|;}y@K&TVt1#akMT#3h^?vm#0A0x z^URIEHql6kn6!bt;sZjRUrZqE&_Q@e5fKp>0Z+icfrbutK~XBO4%1?vMRW@De*tw# z=oHkWrx3eGk)?~MnX0YST9U5d5EVK;5w$M{h;dMu12FQc{1pj|bZAjtHTD;gfuENy z@xxFZd|)2kb6U7R6`4wmd&IufMbwqDam@aboC7>fn`i`9LjbT(fDMLTm-DOesR}m1 zu3<#jeM#rZ8YOnTDdn>+5m-`#kU25a<(d)1hG1BrQPQiDx~0mR$3^Wg@qgnZ4nelt zABm?aHKYqc;0_4h9-V+AyB>T3qh-}^lImA9!A@UMkPm9~0g;G9!u@oBBc!6Q94p?4 zjfA`rI#sJ^>&f_IDxAoV?V=8)22zfNO&A(9%72uw*rW6L7!lUvI2bwb1Y#V7Vmi%p zUy$uA5T-~s|LILMy3m#MZetunTyUlnDt{V2B}1nvWq17wIAD~3$_r)XB`leO?O0F` zH$)$-L^opTk^iDATDN1^QPDT{V@isyaiTsE{F_cdKy=PT!@e4F#l>gSqS+lEx&=+2 zD->g_G67D0;k#r&oaLEDipu!t=`R6n9eh*$Jo!MvDKy$tu2g>xmrxmfQwG+d>}pVY;&WxAv5+(8yhw&6DHHmY;Ld2Kj+EE{nIgON zia{OUh(l)Q^$AnGtholgoc$C)&tdNpM#pACotJ}BXj&yu!9aqW}I|%CGIsA z$;CLKV@`uafhV7QS_Qh}XAu$vqmIXsQ8qgYW1^3`?&dF!kqu6v0Y`pIErMR*=qvL) z;j+Ye!uh7=*;_t$!@YKa1G16{gf z+u4$&LHbYeXTaldWWV~~;F%UgVAb94LZEK--q74<(h-ldA%0B!Tcy2hk)a(hU%C*r z0cPOTu?O+UUaeq{HtMwyS6OK{%1y!U#|enfcHzDV_ZOM>XX1XCOwOq83U9>e>{cu+ z{2mj(--_RF#P8SQ_bc&xRQz^JJxZuh5H+5?ir_5>6DjODZUQ!W-A$EeS)nDIBiGN5 z2^=uG0GDts17Nfp?!v;F__BfgGH89?!WHTK0^G1dejzSu#qLd1L}&IEny>Krf~tXp zH^!dmM|n1n7Z61k{wa(e){g7nOwfZw{A4TMi0uRjf8tl-buzwjia6VQ9G0Y) z!Dn{d^Iq6p@S_Ni90}?W6Nji3UHlR7Mcp`kK+LAI0~D)Q{R(=Iw~AVEkDcG0`r2PO z(7!fT6*+e2qn6_!lK_0+1uilw)=hx51#i$Ai~ zLe+2=sOwIC3}hi9Li;VN1_?WJyq4Z$WnpEQa1Zp2H<0`3#7VQziV+hazN7vXS?5N8 zRO*MT@BLPJkgOm`K?unz`w9pDy&t$$@e%(AXtP=| z$$5=oZ)anyLVeqwk``HEwOc}`^A}Un)QVYFJO3@E*yIj#p!1vn+l^QYs1S6L=by1K zIMMw?-HCc`Pf3&Y%rO>iC1N91fMhQOJYpXYHzXz`**|kWZ13rS8#0Tg>&g0uh+;oUP&S|7m0r=}c z1pX|3b{52gmwZ@s<*H>o=YUv6aPH@;73$y;ws>K5%3MSwd_r{9OoBkW2>0Uxih}UUO)5bIj&{!5VM& zUbUjuWO9l48vp(HOlYs5Ec#YK(#})$3C$)*3BF+(AU{{&%s&drOFt8KEc)^BX5qoA z?52#c8tlWB|5c`SnrTuZz6Yj{7d*smVJ0CBh?ebKpUzf823m-4q?@2jEvJ&v=cpvV zUEB*NFP|rBtP?e|YoU)o%i}Asvc&h&An)r%cb0`tZSx3_BYV&;BuVngC23w_K8xG%Qg8$)nYMi* zhh#jYso*{< zHILUh%B1x{~uVO{STS8YN9(P_4`!0*7=7bb} zF1n|~W>QfMh_tB&f*5J)Xn!k)X>N^xUIHqemnb!uAASR+DyH@-5|l+qi4i1y3aE3P zl#tN(LLz9`Z<>^m#71LN+bY$;sn&|Q>Hyjbo92FmfkIr}rm{B(()Yej{d~al9CFc` ze@0xpDO6{O2v`I!Y2}BoUicVv4g?r+fxE#+?w3kw32Uk)Qz)v~Kt(||3vo^bE?6!* z=>sA}r%9LM5AR1)@Eu^u&%{geSRPFM#yzb*8Nlxl24fMZpW4PY<9seY_or+CD} zO7&;JXMW|SKwe43RE)HqIuO~=j*BHUou7`kAbR|FuuG{$5lRkSU3Ha^jIOAW>}8|l(E5t#$She0H(F*hZj~=B_iz^2y!;={4(gBZ&4mdLlXh-!fxdu zqFQ{%YXorxp^;GpLBf_c^e2%Ou1-s66kpf>6@(a9C;vIU=n;m-^P2uS6P4W3|COv{ zN>3$~%UqW50)BP>tX%=uC%*7R?!haHZmkwb6yJ_5rarPG5pLe($%g{59ncknFb3C= za7_@lwTOTR;nE=}Ux{U8LWcscn}oFy%A-<^ny{Xdkw z34GMW`S_n?v#=zD4a!0U(Wp_;NR38o;zG^BMm~{^V!fh8jiQJ}+Z1*=lp}Ey$>!rK zZMC(n2W@SOJ-pgNz=MP<;Xb^g)QVVj(p3o_9D?ls{mkrc4)OczpOcn7=(Io!|RuVwc(X-7D_{f(gs>Ux$6Iy~?4fTqdDEl8u>0^Un~ zt7ip5r+T{W^D^BHKV`=27MgQzC;@%Wny~O2wOT%q7#i$D< z$~H&dSvq`0Nx)tr3kahR*Qp)!Ltp%a8PFaU49N;UH`SWZXlY64%w#K$OSWP_q7@;Y z_h|u{zPgb5))na{2+NWt?BK6@fhek!DD_9eB3d5SpR{b7T7O8pJL1s2k(y!4!c z1y>jES23_WGV}#pw$>-9@;7_L8LE68G06oT^*q@q7u$B#AIC!oG9K@PJr`a-q}+Q> z!MtbGg*0%pkOSM)MxJ7kE&cIe;G@?}V8-?aA~#M@*S6{S2|C`BH&c>z(q6TSG7(d{ z{kr5dod+dkrs+&V$pJdXG`R%u-hpZE`^Pf|=!MOOO}s7fBBYT*c^m5lRA|_@toQqN zvfeA(>}$4pH@Y?ZCJ?R8^&x`O<$maWM#|?67JT7;s6z(0bsOZxlyeHo!T3i44pk11 z@WJOKWfdt}n|aEW17RKHW+FrTrh2+ZtUWL{Qn?AErx&T8>F7#)K1_fN9{?7Y<_KrM z{3G>9mnNi#+vS_AaQT~=@%BjhriPY<8+`Gtkk&(DO- z{%tDoxn3f?Q&m!DVq>eOE%A6$rS3?hJYs?8OQeNGVJ>(HrZ-I7#1q9o zuXqG4zmW3;T%>3x1mt3VdZHs)2eUTS&i`>*=p2S+Aw!fnSxQ(JjES5vw2u>m14f3% zAw%;kHHUIxcl6NvxM7`MtG|IUek^^_-gmKUxf*h$qA9amWGj#_93k!Ie5|E~;bu+N zuar^sHJ`RWM!{MG#NP)VIvFjKI|U+M_u;v&l(%ChS2t=9DS2!CldQbX?3Hr5>y zv@|2jUq8FR-B8O~x5sBLo$9Xtxm2+*6NE6GNG=UfVxWfxk+TTz&JSKf5uuAKIU@uw zaVp9LT_;S`4s54^A}0un{59wcw~ulr*sqJO#@xN|mC@D_sxDueHo~4+VsPqg$#AE} zcN0gkw_@yE7)KPmReiOYYyBP$>j63S?)tGqJO~?)xh3B}^g2$?8m#VGNIGU4&ki3v8a!RxN!lB&)51%X#0%DO$)wtGhDTnhc zdyUK*lb4)1xrS|I`WA-H)UYEotfxZGy*08xG^}1EEp)y%c#?Vs44dfJnbNOb7CePh z_6e&nJv5NNwZZrJn-SWD4?l=fZd998oOcfTZ+XY(h6aZ3b)|(~Hp{|_X9G;P9KoEj zk`jEwi?t0$?1{3*lK6NRPAm3?C+4LyYHhJ-^3D%~Ewwxj$v#Oe4v*^J-OO4j9GWRM zTsJCt(_AbgzD@lV8r6*WG9r^BZU!5ExCeBZl>K%dd^tVs#UZRNHer8l<0R%paVfBd z4WT6{BhJ?WQUA)OdlBdJj6a;dP(c*OQs9n}&62pvBtD!-?B6_`0%Nmyv5tEFw2&N; zAt(~9E<#!sU6)N_y1{t|z_AZ`W(Vd;V9KMGqXNmXsOR**$g@&QVlTodSuL}Q25~xu zv*N*Q{vHiFD@D(*EkpPx;y#hmMaleS%A=#kH;@{^f~y^S!4gJ+Str_2AkZSLfaL4TP<<{8zmp|)&XQM7}9r!QK*JOEFt(})d z1A}u_*{z%~E2rnV6IDki(o-6N-JiF<03ODv>(}B)D4eEThnx2MRi)cVK#Ywd}?v*q0+|=Fq zGf9k6Vf^&*iMo<_$*eismxqTP`>g`Un2gXR_(v9cBlumrv~X@N)(=OkkLSujUWM9f z8wBvw`@}R^$PEjwzGbdEy!&f(Q4lhin3o^kD#uf<*z1zhL6`&)JS50_Sz_y_#spO;ofBRsOx?xuR`-AcV8kE+j1Gv}#(bo0w(7Zv$Y z8QfotZ-jL9Fuh+~yvqmL0y^J2@&H#7ceIu&15@5-U2gkxdfH{-F}u@()6mq}4MqwF zZ=%+VSZVAp=d+S-58S=qM?$ergdvYp-=(oWZ(H!R_Mmgp4o1o$-)$KoOcJ8D;oS~cLwOzlt^*&E!0Q)QP z2NfHxPUjL!Yd5LK_%^uU&vLKrc^qxl6?U!%!r)-aL;uwr(!z^o=<;DTmJ73k3o9o! z`MXc-`4F%Pgt@;^W1>#E2_W8*8O#s;{a5nq&!=q|G zY(d>lBOkd*ZLfpynUeSUY8neqcmLYV;Lvb;CQB+1ou2yx4iXgolua->O>s z385o$M4{}s^SWPf=56C~q;6uKixW8J%1LwbBt1SaG7~km=`eN*Bll{Q{V0{25wT>9 zC&}v5q7296yJ-t~U%`(G$phN8GTa_D7YZz?KDLkNHQ0J%M6D0IO0Ukyf!K%MQV+kx zoNNl%tFd4@4Oes`NVX33Nc77}CI6{(D)x%j@#)J>OP&YE)=L*gRDumGLI%U7W{Wi6~OvW2^8Ih^*>HHH|2I}ysc(A~I5X35Kw+#Bw3TcG)>?6>@) zJvT^2US!Bct0w)Vvo6Kqa4g{Xyz^&(`|NitZ*0bG^Z>eHtD0tBQ~biX9wgVOb=HB; z?RN^_j%5G|z#SnW(YZ{$0WR1Bks!3(V7r~eYr-b0uruJ;=Up@N;_&5p`BvfAQbHSD z>->_{?sLBa;r52A=Jp}E-mO6XCB7xRpa4^-^fe84?>{O)X0mfQ+p@xL>s)2_NRqc% z!%mrlFu9fR_mR{V=jrJ^nTSf62uqIbU52JRPEFdFnvV^mnC`P>b1vk*wK#yTxU9lA zgbSKOMIuc?;#VZnXpP=9%GCIihrKdAccPOi_)e1vBE|ldS&<*XJ4o!xYAO=+IZWF0 zp?aLHg~T>W*@>|R}hW1mG5WG z=--CRF7~!$dT^#ptlXfO*mn|bE^|M!K6+_9Hc0bZO#kRwV!NbXqRpuyed&qI1dsW6 zbdt-cCZ-aX4v$*do*Jy;GtFRqN5sU!hj|YVAI=E9qjn+qw3vk^7bj?|*O(wFYMZ(N z(IsO7szi2_o)!4tBKyG(%u_dYm;ELeTxl&-2U~4tkaOlZp?ri2xq>Ao=Y47}L0*iv zlrcmN?=Iu#l#$&QyHuY$7b{Byr|eCMK4Wwxs_fB; z?;b#lx4n3$-F9F<+M8bunB}ZpeO=5^ySm7S0S(c@<}w;|R(LnfcuW2aB5#^{m>N2# z(KFwASc>s>UsJlT$>nQATWvsvR^6`jw$JD^IJQU@L*g++<`qQ~1S+#agBmu>%}PFS z#++KFQTWf+#YEwRvYRnh1Ask1WZRe%H;r;KljlKih{in6~vs?WU=XpeS+xT1W%cK0Ap@pjT z1ZvWLxfi!kh81DUH#X}K8dqLVcQ1s?4carRz}Rp|Nim33(kP+| zST*DSkcDM42oUEhKc>#&bylR@W#3p`jQBhh=0WV}H0)Y%wqGvjt-kn1xhW%z+EPw8 zyL=ci$DTpq>Nv-Hv?_gGhB!#PeU5zqq0dl%%TD*()tLt#uci60?RW|tu@gmKp|-<& z;JCotim#4)&QU_-E4k?+;JH7Y&D;#HS^l|E5xWT@Av$h%#epw5B{ODRF)v#g3v&b?Jz8~xaXmd1aG_R12oHThtg6)vp#!kxtPak^Vx64Jzaodx>z7j z1rp&3T36n1FnEz&StI&*LacfBxgW~p7q0I#$gfeU{FJ4i0OOvel+(Fern_aq9j4>f zf&IGUSj7u`H@AY@CO1#h=`1TXP`d9wW2AN8k-yLq)4!mkHb=7mye}H$5FC{3|C(F~ z859|J1%K4DH5>*E02i^iXDBTaLG|DmyS#=L(QxeP$di@?u@WE3mcGbGek@0GE$N=_ z4nrCDT>2pxunRrMuDVhg^N>^h{8<_jy`nqfJ|g5MTIsTix2Vl8OFLPdHjC3{aYk>? zO)}8MTwa1EN}N|ULO$#e2^iju--0kwcgg*sUOzYcVZ*mD20GbKhqPujiE<+nwyE${ zmlB5BlO~Via|xevg*FCl*HBpPyn<;WgKgtADg0a6j9<$;bq(*-x%db`x%_=$rfPmd zS$OOn@WfaQZ-sK%Rtwp*?&X`$zMPD`gDsJ+n;S{`1a8-hU*HhG!Ir6lYBh$paA{qaz;{*3PW zFZ-7IA@#7is(u5pc&z#}{5T73*YQNdq(OX@h$ad{txx%3tg2X z)SL?fC3K>{FXRPOozt6c)HVyZw!4RNPJM^V0FG^ zJe5Yp)^WGon0$L-)x5Q05Yg(LD>QcxtD|g?OW{m63xHPI?&pmhJ`*{+%0~5S#-#Nsdane?x}TO9dys;`6L|{3Wk7IhgMar zOo}T99NaUzXbEN^i&s}PtO>SZ#}l(R;f}a?t`#eC2q>S4Q1WRB0p$@QpqvQ131=H& zSyP|vOX~cQW{frgEcNTUmz?3@r<@N2M~&gyNFQ+qC6F-1| zHXY$Laqn_3#u3CQf^hHf6cIW@jiDSLfKt*wD9K-PTY(i^EmK~Wz`C)26t}=cxMEGX zWdO#3yt`++m6L25S#;h*=W1WlFMTxjANqkWctPuDd)XNV}F>UIx z{<1Lp6m_mYh`+uEbmi!lS&p|Y2j*lCSA>snsBWr=4jEJ7_P?gKePx7}ekTJOotRM$ z8RS^+D92xDZf!|X@LEJQGkx*)x&3_yI@XU*9}Oenws|9(Pi}}^={ulWzjE5ETh+;q z?P*tKOyBvK-?18iECB8RQD}gS6o6y;&iw%R)5d4+ch3D8?&*U=YDmN_QhW z|H^mm!i>?OezIZWZT5|m*7q-3c{_#MYYub_-%$JB?%LKljaT3u=86pUn_1E$j)?=i z5=k|UE`Q@3hm_tqNeXW!tNg32Xy_MAl~GW;+ot-$>yFIbJQZ9tXF}gh*q@$tz7?GiuPvDxbk>$! z8e)X<@PrZjw1StFAx*B@L#3hqwW}{~%#95=+S=%fIgXae+FEdOxxLocB+J@o|4a2_ zgUiTSj>Gh+JbFV>dC4$P5v=UDhPM>#&a9zL7-4PgyrAwqy*vsli{@mlpO9Y2P-Mii z3E|YFEdwSKw=PDTJD{)<<`rAb+YVgy{vUp`Hs8{ZZrnUkB2||l>*I@N9&g>bGmp*G z*k4>5J2qC}iHeawblbe|Sk2jQ{=Q~x)|86?* zLCC#o1mPuf`7$>7ORhQC==L{WhXL;%yQH`!c^q*B-HT{W$~I3@W)XsMT5rBnna{o+ zB@V7UR~O7N2VqC0{a$>N&sJ*YUUL+lrRM%Uxtl*93`e7cdxN>Z6m{=skp<_;gZ(^F zpl_1nZk>jPQ5M9@c=shEwjI=5lNKEAeJ3;&?@8e?1*5Ggn*<#?+NL5dU&Qkgamjt> z11)BzEzB2w>{fWC>^8U&Z_3H0PWpX3&ZcB8Vy=oQei80Fh*rmJV6EuvboGS5Yf~N? zeDCi>$`Ya?so!Wr&NTM-5tZ4nYi`+rUAk$Z0rndE9j|g%c7VG%QMamVq@XEKyKk5i z6+28))|53X_0|Pq<{$N$)Avec7=|Fyq*_Q)0kTZ9o5Kl5;boa|h-{UQfhP&8T} z?Az2#-dd8U%jt5uw15N*pgCGbrB0RnH1#yJtiHm^fimqGKEi9~nA0S2j^cnSsp8^% z_Qq7l%t;D8`56wWG~O4&Jb99CQneb5?&l=UkdEc4g>J#OT26=#2bCXX4nNB#zF8la zvTWpRjhbLB;H#t@HnW;ThZBoAuCOXzHEw+IE`{V6??{4>Ft>J@5=6SvA=3i95 zAviEeWglqd+@SYzwx&vv%e+}eoT>Tg>Mvk`V;#XTyjbTJ9V$zKkHTQH9Kq9(OHCcY z-~AgOb!%&>L_B+FgF~8!e0%VRaFWY|xAAwX*i^#ifuDoB+7z;tVUJ1LEx`i*`hq9* zh8P9>G+o^sjKpl9jbdA|@USaYm3pH#r+80l&m2+5?YsNk>y%<9$+ z&By=3y|<*Nt;(Wfn01PKw!9|Ckb_$eLLu8iu@pmEcpP1EHgX-By87V6b{To<@_T>g z_5LjC`5E<9850j&lB=lJDxK6NND(&NPBDds@xnO=HQA0JODd`*U#yvPL8z;uu&#J* zH9zt?iuDwk56nt;`O^_!NwqAKZ#bl>rX|0VZA*S98)v?Iwr(YL=+?n*(XA5@Bq5}E zIcVGNRtz$^!EKm*JKg3N>;p8xJ|ud;wA|gpIgnR6r)y~uuKj@F%R4yPpjff z%Nx!m#kK`NJtDx-d#hgQCk02HR|=u4Wmh{%C?iRT4LWUcn=L&{9m2#C% zY0DQ6U_2Xu;^oOC^}f?tOuD`saB)2_Auf$+xQ}S%V%Dp~fB{V8PAW8=XxGV+ z7h6b{1CK|2=cBUMBViq7+w!BHKg{E#$?*&F_Chm;$E)AaL+G`D4S`qa@FIUZmDU}I7syD^y9W}urQVE3KZuzuzHq0 zDh-=xnXg})uPXEPfcctcz809T8uL|SzUG*(TlJS+%dnYp1oVePDLFRhzN+Kr+0-cOSKQ1PG;hR=h-LO+YF|Zn0`!mi7qY@@|_EW2gs3eVD=<(lQQ5D|kTvRp1aJ`MCWtcYBd+pQOkUO}Zj z*HvkshmFt*du~Z(;Q=lsS~{&0>HmgLn1hNbKe4ba+>S}??U?NFUgQqf$-Z&4SkcLC z1MTq#1Kuw8eM&gs%wO?)3^so3M7>jS6E>pL?=1|TCjsyDBf(La?hIk) zpKBuAspoNZ;4iRnb$F!G(T2N+WYclEBpXC7qO6>x+T~2E$7R&$3CR0-Om@HB2%^+z zC^NnzmZpA(nJzhT%o{r__(y&uEck~u3J|ubTRd|EKY*=zv%p21 z)nrPgvt(UKo?bNP6J4I0LFXLlkXLHDQYA7Eu=1(}hgGRvf0t6wv$VKjOiJUl=a~W2 zLM)WMB}oYypAhQu&PSy_!ujHx1Lm^aXBew$jKBPbvT!ubg+j9eBhK5T&S>H@+`^^Y zxlgj&&dYRboG;!jUkOEyi1Q^9+nhfmazJKUOPlinLNdr?xE|_Bs+~sonnV}_)Tc5C z5$6%$461wOhk|J0o(vhBXwF}FLXCSq*}xtc`vyR1p)3RU3*h3O%_OXpst|gJdzSH) zU=^A-lKJQeYCK_NudZR=TG782_1u zE_i`&7R5z!H4#slBuS~&=6oUq-?Cjkn4?OxkZBMv3rtEsa?0(e!6ogDG?Ex<(vb&f!P5zGkztHF;v(rY->i+iqX69#cY^blkv!*zqA>3z*sct!2R+HAttON?L4Baa^3#oq!zg{#NYfMD(9XZqGpS_i1_jMkqEe>UrT7NRbZc=6&X?ny*kcT$&Yk=;?CF@qQi+ zL+D&)-z}yg(`?9+t8xpcvjTC75^>n$3(!j(?}DRr0?Y)m-*gpF2V>WTH{{cT&Px~r z6vTq3g^wPUt2mlu>$!_s2;}t8Rk0icSI4kQnUi}RS~xyeesCD#XIS?@)F+UwN&-9LYfX@_L#yTQ^@(6(!&C= zAk&~_@G7Nof!YCkXEch_dOq5%YM^6p_L-Hx0S1LGF6e#ZvNjMDev&bUq@V! zjKvKtb54m@c3{fDF5ljmhfG5Bn(b`J7o`p;v`mB!2bJE~F|Kj)8FBaH$gdtvay9dL zU}NXp%W3_-Jgc?xrC??6!Px6GE^YnXyh-Z=}`}G7C z)FNUuKK||+6PYPb(K6rKUQaTw;wdFgisusYf+tqE=tSrJ=gNl(-}4;_8+dhSAJYwP zfyO&3=Pp}fxIUCOsUHoI3i+LBeh1y4e|3$WVh?^Z{!?d;)`%#v*T2$N6^Aq#GiaLO z<9&C=Cp~GzZg4s+!hYg~b>=Y8Czo<<13V+XBbk#W+AX=9zHG~6apxy=DC#*XgBZ+oCgW1vrJ`f4k64OMki&&J+p|HK6SS^fuq@jelTfXFZvc`3;S(9 zMxJGU)Q6+FT0v({=U{i&r&Cp%bEcziFYfd??ldvr#P`ck<3rTs%0u@5EUX1FGC zPrlCIp*kcn(m&3tOHREmxt~el`!4;xzlEdO`+3MI1}$I07_@YoJ)(b$QgyQZhu*p&1mD@GJ>F?ua5& z#c$km8ExYzLX=9dAq>m4Wl2FVu%b-X&d#aq{LK<(+R{6#_!8spVB+-7i8^Vsrt68a zS9g|5Oloia3Dy8RqKttizqUIlg{b3AJ|4?=Kv{{um+km^8Kl+Ka>ECQ&e$i?E002r zL;tt9KwHW8)?WG^P*3`e(01N;X@YvHFDR2b52+=rToI4$&{O?sI@a7Tcc|6uP2}<2 z^$11ji06r5f#-zBUP}=qOeUc&euNgg>zfH%(a|ICgg|&{h(oA!XReY}v;|X?Y5;k) z1=)rn0AdOJ3;}Ew0mG@03r}GUaaaAQ2=ZJL@P3UaLgE{L z|C3{vyA~cbPIjCwvWo=>3fxhn`|s20_c~^kiGjJ`d6>!ZHRt9FalJ)7@gor9AM{iv zE&QA2-Yv!U?L$@1NCQ_cQ7gV+V&b0nUXfwsglh?+PQ@{Hg}f4R`D@DdHM*vj76%98 z7*4vrCZq1FFBsAp9|-kWR@V5Bf?}pv9pE`YuFhO5gow4b1?(1GPuw$47yf`=3PMaC zdDsw>vRDi;Ih`YMrj|S5SWMlojJWX=j2>$wILv%Lqz5xJGCcMIKPx0OWQ8mabr*Zi z7}C5|Hl#kO68NhZPXnTX)%eWE^5DNAxf~ymg=H^ZLP2mrOG}ZHir&{EU))9E2hI36 z`R#3=nMpdrwRqNp+b%}N0|>Hldb{h(`KZ8XawsPdsc`wCWk)nwNK|^r1^v-^hOXx? zOQqz<-{mJfHxoNi`w1l;;lfKKF}#}FLm=ZIet=u}p7&8*>4Rx$E%uAg@fC8nh2=NQ z|9Sz`l`bYR8C@sQK&RD}{#2p|8t8grV)xXQ{=7$AgTzg(D}``O!QCfu->)lus7KuW z68F8j(x3H+Ym&IL>q`Hb(VbTa(Adzr(ti+#!E$K?t_35ywf4`Y%^L4<-Hzy<#Ij_d z>x=f zi#x=Z#&Qbv3zLxY9z$E(3ZH%fpG^6goS9p>`|y!Chs(ZktI^sR-CJAT_ZeH5lWgM@ z&h$!C+{=X1I@8$I$JA3uSi0h#kBS{K?HFkDg>RQtya!^&mvEs83EDpp$-vr(tN4>z z@2R1|2>EmvSLZus-6ZhF%(#u=?y??8A6dG z(1-k6S?$q$ai(!}KG(~= z3l$INZA^hQ&lm5PIPZ`ljjxHf)_NxgDdW`8ck4HXMnbaV$98;rZON$=gA8%D9HPi2 zJ=|hcHpOIl1g}6Az%?d6IAP3y;CKe)BKIrpYV+O96&Xg* z!$JDNRd-%8CdMI_O%emLM&k4spf`KHOoyq56GYe3IfOWUA*l!#pwSoS-M9Bm76q6p zS@{Vw!}`qScFK_?L?lapC5#nW`kT(7QPHs%OiP}3g4*6(@00@&N^8gpy)#~3z_N>s zeMKGvcwuK`(-4BR-_;x;&Gpm-1}jrjI!c>HpCY?YE=lqa>(KjVPPV4NH9%e@KJ!X! zRPD38J7!q&c=R5soQX4}BoNK)>MmD5BqvI}1N5u~AZdz&rudlh7Jp>q=hf)hCz--* zH?cCuokb3VW3FRHpe*hY2_Nw;p^q+i{nM-tCV8sAG#?>M&MHEug-{nrggzE4k|kDQ zhx`^5%*!*xq!sy!!KV(Iii6Pyi$(Z{<>#fvcY}21f5;7A$TLym(h=a zJj9O?S)nYlMdIScg`$@E1&3kW^GjHwVwsr)z9M`XEJs(5C05ZB*CEtqWZEFG!1PQt zH^R8*Zpv86-8(J%!*&|E2hGiLA+fg8PIKN&=YR#F&4cK)oN3w6Ia?w*H^`TF`^<7q znE_V#vIFs{ZR6r;S^mfj2cdJ*veu6~PrfGj8)uA%6*g8fgjXZqaM=MaM?ll_qyq9&b1a<)8~p$$v48+d%+#E|rcCO7q--JntrVbdm1C*uAVx-Fg|| zGibYv?ljx;BJubMk=BA!9j`|`PfLuqb*9gXPBIOyz4hGMUX3ow>e1l--)!s#+cQN< z0Tp{+o!1!aXOFy!Z=Zb>3DS8Li0Z_H)8yx9@D|^G4EqOfkRMlQkQi-@d%pUoMrq~N zl)Y9o=Nz)GT;i4vuM2;Wu+i~JmE|}G^H7-uaN5#*P2fH_c8%qDv-*e5?XJI*qb*)O zFy2-XUni)`dw5=bx&2{<{cp=2`zD*$3Xk!k94tRagITo!udB9G&S()HjIZ~5U3CXK znxWd_BQ}IvzvN2c_QT*jo#Mr;zmRQ}GK%a*qAxXV_Tt*qik#y`RS2mxiY$v|g%3JH zP6@kMO5R-E7yN@Inz@=C8uxn##&R?`)<3<{a5hnuFTU2QJFK})4_L(6IRj~S+_RBm zR;0I54%YIGJh5SQ^s*6Y?!VX>bILrAoyO0x@2uSyQN0(E`6ifrW&dNoJd@9M?k$z^ zW!I-!tIq%3yQ6=_-OEB?Z=iJm+C%$~i_MaqRTTFW(;a;g<{n89)sKqeU2)Ikzjuez z1O#YQe@x;>B#K;CNdWbsX2ynp#GHoEV{xLofcM=1u5AO6k+;&AiUY3_s+MMHyst@| z-g;?eqL)?#U#qN#UfLOwqgeFP((KSQq*iS#SatdBuQ;waLelM;%mX*p;LIB_(GjR8 z%|okZk4yB*UVC14G$~%$Hi%OaB z3OGuhPpMSxY%%aN|iol0BnSVt=}ap9WBaP%We?EPwVvo4vP@vJ4z-S8iEY-&geo`L{2C#12|Lm8Y(~hjj>bSIMYsQA--70spJ6RCP*Ms?4yQRT;Xy zq9i56IWHKGYGlPdM|7o(%gV&NhT%!0Whz_&Rn87U?yf2!U_U7#RoLq)UoB@@55G!a z^X*c+?Y!zDnNoQBMq*8Dr4@lek<->TE%t@KN;iHp^eK&)w0K?+eZJyPL{qnxQq+@E zWpOGdcXHfwHnev5;60%^m|)I_UNl+6a_}6$v~FQ=a`c*Xxx@Z>t&Ety`c?4QkOI}r zTW!@@jZc=1V91m&6LjxZRiksZi)<9t%1fI|UcA@M(cD(cHU>}S#+S8-I=BLD&Z=K_6j<|d0-Krtv8HGf0YJ^B$u@5>x*Y2MdESBp19LlJAIsmL?XcWcT) z7SEZq#(rIGSVQHyjX{q#9o~|FoD3ul|Ft;r%cV9cs&gFs7Ar;#7xQ}xoNM>WPjRaT zkIH@np+&91C(3ei$_)cVv$`FfR}q9h%UQuIJjzrbkCG?z6I4bd#e=?=R*T&VeKie; zm5H3YxIMBP%)7{&&uV^W;@m$5-@DAce8xC5-){Abm@CEWE9I$jPKIwk2r}}yd@Igl zRLY}wg3+#l{;C8muvT6-YqU--#?dQs%cGU&>1U*F0NAHfOllsf=l4m??oj2Vl7f@d z%C77V#GmtfmVBm3y&#o3s%Po|lgevvA}`XP-K~nj`=aFUb~i?&Eb820QqI!#feL%~ z=I(Jmz+n`iMy_Or=ihnL3+PU1}9IpVPPG>rLL#8lt*ys@J4`Csp#ao+Xbo zspqCrIUl-P73YbO8j&GNw7I5d>U$>DpGuw6Gj)wgEl;K{l3m{u;CBY#(iA{rAAmXo zAeKG#94=bi2cX&jAfcN85bW2pic0{HQCYNv1Ugb7kUarTG9_G=DnZa_Pk^ss&XaLs z3V^ND3*cV{Admup!0!d{q5-H(0krf1__YDJJO$9+2VkxNxF`j%y$?W@0VqiUbo2qZ z$N-e40Cx8Q7-|46P64Ps0AKQ=qDQie1O~(_kUaOgmKWb zgr6AzjD#g-Q^>ZS06#GRm!tr4`v6Qb0D9}`#^&__IM)C`m?x@$HQB3*K?Y!43Sd+p z0P(9tH_uD~$hp_Ek(&*GJb#--Tha&MF97Jqm64zk9pxeFQrRWi7NGwoQ^Luq5-b2s zn!$^A0w9TSaC5O;BI|onACOB^AOn&h!kDHM@V6!a3QR3Y!I z_%N^s;V09`A`LS~qRG^=({CC8=&wYlYf}Z85|$YN^iUE2jeP(fF#suiy=ZYCfS>_L zq3lJE_5rvG0Nuc8B+y!ozI!(Ed{aXA%q;Cw!e9e{7pO!dpX&qA#Z`uqQz(2Oc*X!|6s{ZF-UlFR08kc8v|)Q6fV&Jp3Y{-n(g$Fo0q9=B9en`KHUKH4 zzG!zJfB^vL#wil?{LyKR<;`#^g;+gN-Kf2*PYG=Xpc}Ox>;v#e1JI4y#Yj=l$!ahF z-Kbp*F!uzw%>ZbOt{a=%2VlGb=tk{%eE?210NtoPzYjpV0q92Uqxt~s<}HG8=tk{D zeE?P&fbZ%CmXN4NQ|2}?l1~_bZqzQXPd&Ri-vD%@cB>D-WCPHR+U1d=X9->d(2d%w z`Tz_!02-s~hF13hIP|&hW;be|)(4=&0Cc1Fnmz!_4L~<)pVJ567Y3jkwb%9m2pNDf zdZydhNP2281#4by0J>58;yxu58-O!XJL}Ot0LK}CZq&Y{55PW{*G%`wR0&G~=!uxO z8vu>Wbt|9i1Mn;WlDh}yZter}03g|j18IN5L6I{Xzcrc<|>@L~y9nXpi5DqzA(BwS{~LVu|X zO!zqok1}DQs?>=l+#=yz6Bb%YWt#AI2_O8S41g^Zkowq!cT0G;2@74LUNhk?3AdZD zP%G*s6HWtJspm}CCE=$`I9I}tnsAE)*n~SITyMg=B|OK3m4xpy;VubRoA5yi zUv0u^EPZ8}uuH;YOgLATJ zCcH$#RVKVt!T}S0PQqm-+$`Y>Bn*2^1dY1+>Y6bao|_K?=8zQ~{ZSzz{1aEUkYNRb znx=mCcj>jrD`0R~VqyXPj?u2FwBdhI&)KA2;>IJt+wVXdt)nem4WtH!<%I_LqAETt z@48_BuDH>GiaaS%y+QjfWof9=N zmz$U>V%C_LF(#&(m=z}GY>Ck$(dSL2mu!z%Y20T;EI2z=+e@!MF*L07`n=F_emmwz zDkeI5Acb`3Q$g9KZfDq=2HWs(g> z(Q`(d5e;Dw19 zMxJ2u)f2qyb0F|Y96&nAz`&>H0LLoTYj%g44?rL~pZ9&8KR1<@4O+SL!w6*hi zm_-%#u;N{@!A%)sislU-Q@SwcoT5h5CSEHU9*V`T9ODgrj=Z0*sVp7yecBRF=(E`S zunHHSiXN676YeixO?(lA?dDu$g1H3Onjiw)&Q=qwE{~ic9$7QXBe#?9{yZ66IAkao zlJ`M9)-1k}q70Llh)`eOCX@M~OrF|DI1qV8@&zKxG<@V4NijOxWeHJvL^x&=PU&s) zW-n3_vLm|JbwwTva`!2EVgE|kF)^Q*H%!bT6EljK%_b&fVvvT-S#4sbn3xh`noZ1= zCZ>#-XH3i`CdMM>DHAi=#7raR1rsyE#MBV8)Wi%hF>{D{!o(byBBP)WVIqHOBKMd` zIgN>|H<4RRKa$V3 z(^f0=zSCF=TWhKl)n)NzFWab@kxuJ8iw&0CWD|9&*<@n5^-LNR>yS;B=dK?M;Ug^D zGc=Iiw>bio0T3%SwnOi>65k3B#ftxuJWXyzulzW**b6%WWeeo{Rt%Az%g$prd?J7V zvF8RPAV&GLnZYDPECG?-p*~Ww>4r&meNU5`WJ}@sn2jx)Zk>k61ELbXw<-@wnbQgsQuNl7`#|){ z?rColpfy2WBsZ%caazmosnvRYtKs^0OXr8MZI6kOF)5@m@>$=8k&(|uU6e8_Ioz(g zMR3La=D0&bWne+-%i>eCc#^4Bu6%>76VO(be1o!+-_`P6?Q82NJ6dqJE9r3_7%;*A ziN7_@9(%L2Mk9B19%1meoq?E>c}qrvU|=m;MK2)4Nf1H;k=(!iLO94Y(`_OEV_tPv@nS^rBR@vmz&>E1`oJ)az^6sJ z7~n^(?eN2-a|jpT#7OYC!xJ{vbGL?gJR7-+OpsK(du<42qe>U*7x!!ucnz~H#9=G1 zn|(ykHshO(e%&LP9SGUbwfN~zNj4*!mKq~%lpdRhugLUS;js%#2!w{If;TZOa`{O0 z(rZb1#f%*?O@}Q9gvsbrD7Yd#?`veWGZ7YWqDfJ=dge#+rZkXv#-(aS zmH_ZRC#tQROxDoo#EZW75&gh5ObFC-Z^SXvjPAVqV>uO($zS7qT@4|L+&O+Kk9*!g zLNFFS)}*9tO`13LDR1$9Uvs*Q1P(^VyeoR`Vzyj}zo>WFGf$y54nAMvZs~So_ z)?8ZA>!FuAUrCphZEu1vfr9e;>?d3WSrBZ#=)`_L_wqIV$c+PWOO`qH^S@C0`K&;! zrZ;7t+$a|2WNsl)qa!I~r4SuWnWc^CSOOFyhMymELJb|9o3v$ojTK!wM@V|?emJEi z&gCaig;oTs;Z%h%MqE)uTv6&ef;=QX&CO%r7=EGZISFeT(-D-7w|dvi_>$+rYq7Uc zlP}M;*T&9rFS{@_1Z> zg>b}`cw$%U|8flQh8nujI|rs)sVR}^r({Cmr6xfAKw5=J2|q3^u~^iB!u7uWj`ZMw z+5xBlWxXF8#3!C5Y6s-|n*6T!8|!4SV*S_a-!z7(LM#WyH-*|`mvF{X1mo}|g0uPI zgD1>(QA~7PeoTzGpD=q6rC{~8PZ_9-GSIKi;1M~h=ZDAU$EOL6|5$-cAarRSG%3jc zUNmps$Bq!4GiJTj=vG;u2b1;+-eA>WSs`h!U;w5Hn%P#dyNo)&=+UrAo|HH#1afrb z`(RMf*Vz>u(G}0e)JfF2gu78njohhLTrYJvR|0577U{gU^RN1g`P9}zD9Y6ETUmEW zTL;#5$+$+=Y>8k*BRe2MZ#LTw8jJ1m@E@7M)8~r^Jv(Fs+PJaq=Dv+_)!iSH9hKK-Hew%YtBF!$#jJ;?I87N6f6RX-> zC>B;_7?Q3>rDb$9GBmMUWbxUbsS2(-m=oYNK9nO8ap@eiokLkK;JSaZd66}=V-@x~ zbBQc1F`{EW$L0#h$D#ojKIELySr=CzM0dS|uGPgmb@%|ibT3DeZaIg(+zmgJd0}7X zi+J6w51GhyI`Z-pMM+aIi~1?3E`sLwO#}U@Fcmp<&)HZ+4@5hCb(T{pW&$C@Y0qQClX26p7ta)`9< z$$XiD)2@j6&(5u|+kGW@!ShP4PY<5sUN+dh%$M%nKKlev!ucb8O%9*qHJ>Y8Vk;e+ zz1wFo_N^HuCxo_Dpx#^Q_*X^Y$DtP)x4OO3HEAjOf_pQA!@_%Wg4r0w8UCW_+KZ-Z zFPg5sC|wH_zQJ&_xpOB*=binEh>U`}4rpP|z3vY?5t6!GA)1`7*Y?l3m>^kl^ukhu! zcYDWY8g-Ku`7A?q&Tj>#LnOvoM7Od=;8&P^+U{%CAoH(d&3LgKB^(YHXh4hE7=aw< zD*=WW`cr;7wm$}7EX71scsIkiS#b@;M|2aq;*S0Iw*#EvZouLQme!|mv>@2?2_LWI z@P|QooM*(vxCo3mU%Z6fG2Rt%F4KX5__UG$Hp84B?>ZV6-PiC}&iOcm+vr~IpO`z< z7pb?FPRU4%ua51p-<0&auVl^7KOT@K{|P6KYphSNxl{wZC%lfjxZsNUL#-*>n(9xG zGgBaHCjikFLnP6e2`+;{AYIBicp7U;cj?2crAs3}B7!c_=1$(XVueT_hR$Fdo;+Cq z|0SY8o775R)z`2+j8le~vzWo#>l0_nTGUxIKB4*0H^0q@`}#DEf&5H(>^m&@G}Jp7 z@VFYlx^Hu~5k;h&vk1*3?2%wR54*)e#^s3SWYWqp^au4OtulVS1|hM8m1BR&U|N?X z3b1U9A;H11?G@Vc@wj7HK6aC?E|SttrSjNw&|UvDI#r^Ix?LRE45EJdvomxOsiUPe z9%)Uk_QTV7LabAaFOO(ELG5K-CtGz#k5&b2toW_2K&(2HS6TSBuPFl~%*R*6S0jmj zwpe@mBz9V$aL4$j%;CO9M}?Tv4Q3IK7YO``DXGWdYaAhpC=GKNGF?(u z`!!VurE1-UaQ7bSX-_Yy8z9|M_E(t9)C9WydBe^*m%uI>TY;2aH1m7EDqM|n(-al3 zKd5y4XQDmiL-m+XlbYEG5Pry2rxK^WbDh!o@t7kTO7|XE(AvP(zALGtEt2i-hu4_% ztxQw=2_ey@!N^7-&lNipN&v>!80Tz!U%VB|ijG@F7#A3a+^=l(HRYB&He4DFY}j7E zGxRz}CN*A=vpsP=sQdUi=88ppz@0de>I;_Wn77mgD`d}e@*GrAM#_Fj-tsY`2$=;J zV-8RwcG~TBSKS^boZhdv zlQQXQ=Pbrj=LubG3XYv`*1*#A&?p4;qB#$}C@bfL;BajTB_sTmtep1rh_2fdV{)uW zqjjgXVl1}aL)rgRpMQ<;y_|C|F#Nq*{mxk3>bu=5^%xH@Rg^wkCH)-eUa4HB0Woi) z?}dr%DjrI8X;>h-NQTmg^KMdCgSr{vdlH4ZN}rXX4E-?C^IJ{vp&JrKe!o|XCYlzF z;A$7JgFql6`C)#zLG@(bEIGoS! zQMrp9k*It=l~Yyb|5(*3sY>(6z{udhYsH=n>RH;Y^nYO(sI~Gc7JXE2{%Yt;z}+y zNk2M5%v?}gnif1)9XCVfQIAt%GMPk5zmN9CJ^pZF;7*W+#*S}e5b0;!GlGQ3`25KD z0^O$iU&=^C@2{1n9r(93B5s{_FnB}lC21%hM?HU_+wv%J`V+d9NM3m!Ohg^0qcB)8 zu^@>RCapvQn&a)E`(#18>%EjMBxMYWeVeMp z#Xl9XTvst>@)@m5-aJ3lVRQ3*o3Orlj!hJ>fe3q|aaI!dOi09{4#I4I5**@QK0bdc z&vpD3OpVOXB%FcTpSvDbo%neZ~Ag-89p$8`6xd(A~PA>uB& zW-8i@tT`3>RtP8>6)Vkw_Hv!x(qrjuCA}?MDRhegihGurL^27(up;_!K`p=bw(_X2 zpNjuH9@p3?>BO%}j`(VJS&^7_724{X|9bh~K*!lN<5Y3MF=XoEru2(5vqI+xe<$8f zTWk!ZjmW#H+?)fHHy?p~PZ&Dd7j&MTpm*2u`0K8{9q z(6FSVk#)LlVdd&}I5G>xudX<5M-uKvTJcQX!)wU@?rWq`ve6r`5VV@}OE#hAU_#zD zWx!9xm0cimB2Gq8rVc^gShrTv5=BgP zpX?`&wjpwGbwQrAEw`erH6QaiF_&3TQx|swbF%PHzJ+Ec5AOwrnQl}%JZk&3$;+~; z_$R!w;{`kU^2DvBi{Mh+%gd>_G=oQr_I&(VcJ8A@ArO5D^HKd34^nVh^^SqZ0v42m z#&1vHHR`%tG}AL7ts)a#f-Qe9nZ&4?Fcd`0Wl{GM!vGeOFb$apJ#Y{ozFI8Hkc)(|A|GePiwmIo&IN zoR?F#Pt_p3fqjIA4*j zw(<11IC`^#xyFO@g>WzC-)>FeA;LNMJgN?l>C%=326ky@nYn5N*HIbve(^RKGzH-5(P0=2?if>-aK2PJ~_K=nhP?J7@GKDjq zVzq$J^*Bj;t`Ry#F8sdhtmt10wgcN-G=hP+>3GUVxzBdK#7Z+yANj+%gQ2X~g$c`3 z<`wvP*uuT4RwMkNAK>skPF5Gf>^Y@G4dN#;_KP5Vgj;DmAERvjKHL4%(ZZcP;`D_- zI$0c4i!Sw~%IWbcUsKRsB2qZjWf}X+wjaehK!&yvkRBS~v)3QkzBE1fYu|pq8|z~^ zSRdnJfc3FW9RH9jPtihI&>NW|Dv40ZWp?In!27#u-SvMbT8()c>V?;&s|zM$wXsF! zGU~h;$kOYFNJ}d0fo`}GanH{gVkvAsxnS-Oz-)oLUS2uko*I%EfvO7o9rpUYSeTNWo|(dk2kaG9KAYv6>R#3eq*BS_2i?Rs&w}Qg0kXb8NV%d4=V?+ zzp{QpC%Kn9Fc5&BkF2Tmr_ASmc&(TN5M9$i%qf=m13kbmb}yGfJ6a-_aKJ-%@ERwk zoKEq*BrIB3z*8k_nc9^Cp-QP(cSXEEuJd`<&pe~^zubN?5E1o!eKyakZFFXjuhH)g zuq^lZ+Wh?|F$QVu%|PcNy|QM_;S)WGASYk?fR6nQFjq>(==^ z3Dd9e+6z1+tIi6 zkb(L}553aE!6fL(su75083Ea&IFan0sB?SwXdtUiKa*h7fbOi$NW7Y07-%U&J_Cch zxADklcC_r~nnLy>KeCUnp$5Bkg?5Yb-~pfasQW$#1ENFM=oUfZuZm-v7y@u%)fa&3ousSWIE1%+AXrYA8Ipj~S;~$I2fSuW@ zPlmE!wy$=6+HiOM`GBaabj>G9&C>{m^V2%V5?{!Qmvb~K$DmNN-pi!C#|j!bFChr! z*i4yedBB@F z9n&lUdv8VS*Umun!e*kNt}LjliFW3v?pFeNuJC*5!E$J_z(q)ZNbG(;mOz7p*|?Y! zBjI?i<(Rw~s_seEq8=hh$HJT90IVb@50fL*TobFV>;~NUXMK1J`5=~95>{yBHYoke zOZHFtmgrTkIxdyH^Kso9cVh#;-mxDbCNgur7@I**r}Zd3I{ibU5-Y`HBXt#bM& zRpm{=oRad4{l4u-53H6;1AgVEAR`PGw%2#&Pzp|DY0~`M{i_}B`VoSRI56D}A23zE z(#biNyZ&v0){ecjD!8%2zB;E&>C5NB_`N`L$HZ>uH78pTot3s|!+eftcg=(esu>L+1)}&bW&T#mB)V}Z(V!M2D-OCB4d6plFk&_dRj8{o z%kaJ;#x{#;OLLG_h>pYZe*4VfwR6+6_>8y4+;U@ZzfwP{>oS_>)k zH#564N4mS;pW~{)jeUSZNP?`@s$F$(oN=j>G%BgbH9 z+RmSV`S-8JGU0L(8J`gRw!_;0yEWxT2KmNZ%W-3(26z4a041k1LoKIn4#CiAQZaII zd8x_~U83^V8C?g;^V0IUbxiCYpE}7UEAO(xuy2h4H=vE5p3UY%s&!WBmxaC#Zy zGkvU|Yy4&j0!Z9l-(oJ2^M0g-eK}O+SjoXs_amVqYd3bH^3K-sGn>>_5VY8v5FR6O zjI(URq>zigMMeDr8i4eb0G^AAp+R+}h&mQdmwWac z`r8AiqkNIT>Ad|jW*xC%>JN}jiF0x#L29-PR{xKT6)5jS^*a-YJPCS}4nVC^T|^SvBSv)s=wjTLjHhe#z@y z6FiA>N+c|(`v-a^1a(KICs8-)cbzM|>34;#(0T~0#xWQ5v>`_Td9hLW3FFrLPjCr^ zFp!n-DQi@gl=z?No}y!%NpFczRGxZ5;F*qFtTVH6yLV(IwamOx#4pioG3V0d zL~%8>xREB#r4oJ8@|>df(9*=YRKiJ{O;j86fjR9#YG|tb@HQ+MtDERGwxCC-k%g~$MH~ws1SGCE6VJ2aeP7p(EFS4g|?xa?JerSHCo1eS*(R<#`xn5%8 zr|+C5##s`0PsEQ?tP>u3?6->J!nG*gZ|?~IH^ba!2?TGIKb7*QD*RtZ@T%~CGlSm= z|F>UoXnbSvLiGWrW)h5&n~*|^l|dA}$7#%SXB}JI$bk?j*x5;tyT%2z^J%kY_gPJC$c0GjM> z$b%XQM09(-@46d=IPphxD?7i|2zj!o7UODx1EVN)7f3LE6MSHp#fJY2$qdVVh+)82 zLjFM`P&aptHkl znP>*01dA6+LNX!I+&Yt@>o#^$u4L<`D>srh~?x8Hj@|BeQ2L*g>K)499JlY6KkJxX3wFzY4qt7AMH- zzQKB)v>aNV?8Q_oESNp72zx2zr=Zo_-G0_&H9z-qXzL)ciH~k`sI|?LXKh=ZzgL+d z*#b8wL{eeP+pFYb;&=cWBHgqoUmQ^KRCzdh-CR6*Ls7$+4fSISTSW@BVigu`&G0;h zJ);|a7#FH2IM1h8i(G2PwgECD11_ZRV}33%FB}AK8q?7PVbqA*Hc|JKZr?Ue zJjbSfM7b-se`~^~$)V6f%&0+Zn61s3fxH~DR~ag?t>{QrsPhJ~54}&i3UuI|u~!)a zdX8>P>4_V(362b8G6vPK(Kv^)8J@YAFrYC*zB94E6+>hs`MuSY^SG%PB1?;i&nXu7 zVDod1aqZov)OL;kXr{9lOrR}|X+{Otm_BsKH|xnkh{}TpoOY5bw$ayafyNc?BL5Mx zinoinmlQK{mfz7QGgo~Ken@p*!D#O^y*n3h1W6>{T0z6iMM26H+JMT#dP6R~sgh0q zESO<9LeK%jes!p8I%vR(qqi7UOuZ6Az_P9~*l?1!H4N zRHdBt3j?MC=C9(myf%m;*Dl`T^6=$Bhp4)(kqZ0UdSdBW8sRGh@n* zD}RQ8((W+YVvI$Ox&I=oyRnC0Ya1z>=LIqQ*@#O?@hPJ`;Q|{xMl6mWVANw&4~&$vA0QH z%o%sk8_ZQQUWu-KhCtJ9<`+&Z|_c({RxqAvN~jT zAaSUApgla+royKaHu!9#t0h4`sQ7P9R9cU%c&$poVPxtOsucenbV0v$!Tj5?u~R#- z9V&UO6;+cy`B0O`E9mJ8zk`}3{2Zr>=)IkuP#GgVAE9`pxE@jzQRl@Xt$x3ODu?Lv3hbk<77n|8g{znA$n6VEez< zBj2_6+V8>=8LQ{~-ae+(unprF>L=M0BYg7{+{SxO?DTGT+=&$Do7_g)5wEb%#Pohz za9w_hf_5owN2-B*Z_UE4%a^+k6<5eJS@UtM*w~J8u?|NmE=QPElv`T*w7}7{pmjJyzt2u3WW_MpvcM4qJxQW66`)_HntOP z!JtZk76f(othb~Qm$qq#t`}ZddhE*#-NnZizSY@C@id}`NPdO6&@cKjhuw`4cJE4a z>uBbFPTHHULfYqECmLWRVDBS5@#dCa^QNIk^rhiP$nJxF39HDVNz4lU4zY$>m}SKr z7MJV#R%YOaav7JqU)FcCsTqfW4AgWR%+Iw)OtjVMLlm@7lQ`HIG4yHM>4#d6ttn>n z;r{piqt}I^bC3F_b$u8`h80BX`onz(L#>oiH{$iB*0`$J3)p9MWKs zPg=9A6bJ%{k@JKiD8GM!2d3O z_6+qET;+lCI{7?vW9TbnNbAY9N9ol;qs@;MKeh`W)quyem2M!4t}SWjsI>j~T*^>f z5Q~Nli!W2(7cF;d2ZV*jByZ6ATO+NGRYTOhXoVc=X-G#U4yOPtRD1E?h99Y}+>Kvp z$k~X}ZJbU^ueh{M*#yDxTpd~r2Qdrn`Vl*BY0F!*kp|+*22t2FuY3!gpX__N@8s+( z%u~~A{{1ZWZJwkGfcd%aT`(KS{0iee?0vek@gkLbLUBb9h*lsL;q(#ovuJV0ac^*; z!`QN)7LM`ygSrbSV}ODp$Xn~Vl0sifp-0*)I_>H%-v{D$?PCjzjE}TasFQ_xUia0Z z%@A$D_PFz~4V{7TM6gZC#TsEXjXL((){mFfFKoy+T9O+CP;(1r5@b+dS!D<9<09J+2)p4Q82zs@GWd5zib#QCk>a&rqUiTiph%q>qLSU3m65uEKbb9-rK8ilTW>ri{zmlFz@&@ zwMZR{EkJeOcI3udnzbP&U}ONp+%g!vAT})mtQd`Xl#>g80Pf-_#X`ETKp#J^7Xup1 zDzG!@z}(^PZ)! z@%R$klm1JQzwovp=cVx7EVG^Fhl~j1>s`9$n6J0S+yc>ut=`;1>j=Ky2L2GcueZ@% z;p=TOw+x~Ns@EeHhkBdMt-C>J+ZDi$l=OAr8htvHggt8vo_g##2kl2sM=Zz)%7BMH zV)+?K>Fc1gOM|el0jw`_4x$OLhwlTOowQ;=nQPt#aiK|JrM#Wol$K9d+?{wU)G12n zLX9ulY!h#d%#+zOEaYcZQX`RqBK1ZpgwQhJ%*XJ;-6+Ij1*rF^UQxgSjO; zDqv1j)H%J(o{nyRg{kON7bV9;R8tsLmr+rJt$iy7v1O=8DmK4P$pznF^6(ATT`FlQ zEAR{QvqM>I|AYg`n_?UVJ<_^=Z4!+n#?l*ssGs|>l!{j$0)@H0-uyKN<6b&BelYS1 zh@i|{i2}M)TMl+)b|W9{N^1l+J;N0=273!~%`GVuN#8-}eAxFIbHm>tn>`t__uHkO z*(4?KmGC+WXN^d$>-$$Yt+{;NzC{$W;z(#KPAH=V-a-S$L0Gm_Pen`=l<-jXFlM^) zeV9nP&42#ZUV-P&lx=wTyA;9E*wa1-l9vljoaSP6FO1nR+Gre(;S?~o_2FRock4d# zf*u}%4(5Y4PA9ABA9vF>Tq2{hz>B6Eh{X6P3|z>67xOQv^|JCQV2ep9HlOihDG>o1s`D zA*==#lOE1Owt!~KsRYrSkARZq8A^MIR-_TPH$8*9_f8?-RAm!$rD|RRFZFf4#B?cy z7lBNef}MORv47Cq&{GJ?)B-3oO5ICa`_@!o%^x$pi=e&|q=E@JHp|^ar_-@*#iL<4!K|nZ4$_Ro~I(&vU<{n;h2?P%@y2oMzQh4 zuw5LRva$7K^EhJIJ(?cf<&=49PoIotG{`VvF=HDgeO1VJq>KgSqi$KL1=Z5OSZfPtb=!K(AG@$+SLG!ENH6s{VOSxe?HNI zdX6a@jXG;3gvw=h;HRAWqsKrTds0zcH7G6#rp6_r* z823()!4h`Du--tv=9$-dhI>~gOvkxND7G-CF!hi#r!n`bQTMOS;mPD)iT;crn%heW zqLHO7tF z`I^#9`M7?(*6*_+0ag6|sH1@@#6P?au^{zVc`Pz-L8-b^kFl0K)UzZ6wy+D9K-x~~ zv;(W`orDwqK0`P|T-n06YGg*syy1k5EJP#h&m#}t;lxv25i<&SAud&6RBWIEYdDJs zw76w(F~L0r|8v~q@qZFtU-EvNi5LGh8 zFuMff*n3_h8N-So8V*^-`B55Xho-L~B?x=|7#~7mYuMaM;}<9#&Byj>U*@&9QW?+| zhq>j)tfFRK1rN5P7@i9Qd8}jwg+0cx^C)O$FE$1+owNIsCWB~SW{?nR53-L8fy`jk ze+2X2r1`(c{k>8Dvjz(9)%j1+{7GNKgIe(MAV*KFAv&BN(eNH4gFmSQNO&Vw zP1;EYtSBcv2LtqVw^C@lKe7a`g1E654c2N|*z{kJkJ^LxMp#tcG_{;b5nbQ+8UdUy zMdL4Trwu!j;5_OK=+3RLK}GI+1t^0~(ps@68O56!v7%w0!eTq(p5B+4OtApxO1!y- zbigzg2w4IF`{jRia0Dne&z_T6DyJ9GOE0)y3(0q)&3{}au zr-VORg{kpf=bY1~bJFg?=#)KUumUh7^!638Ieh5@s9snlA*E_ID1z<_6!~y}%jXE? zNyhrtkjP;=t8LyYRTcUZ6N+%YwhF1h>t6>vVM59vq|gIIbv*JYvKhM!zt`3@ECZa` zwgBN8T3fi7I2%f5sWI#s8NsRQpyv{pLisutpJ|P;s5`ZN37y(DZ#7iS_T$_(sp^5L zYv_baIFd~#x3TUr3a9jqbmJ> zmZY!;C^!e)Zr++f=ZmY5>aXlM?eykkUq^KP#zNhM%Ff2e=Xo`e8iCxwEWf)YmY@sC z*{d}+BD;`r``G#zR6KzSX1hc1(CMNyJ0AVHag<|2KG``&x^>zW*CM|8f*nWEy*e z2WNT|HDtq_QY}lw92|tO_J}K#W%XsuQ8Q270@R8g2ogQIa~x5vS*q>n<6*L1gA01o z#n=7gM|bxA1-ZZ<-LcYqEcWQmDy+oycIc)p&y%#=cCo2s?$CW8KoTF8`f(XhIk28VcFjhovp# z$KlZUam2~_Uf=0o&Lz>ecXF}(3{{`dp2g^XF!JyslUN%EH32AI;^7rKun=;hw?mQI z#Tz?TZOM`rZm3ABso9|g71}3+Di}^8q3W<5t3=rsw3weuLEo<1!=hnPlC7ZKebkoI zj+>aM-o$vM8W^8{= zAQ9->hr&<9iTpQny3X0Y<2Nb@3CHNL40&SJ-Kh~l)_UlA3eTvJb9|KdckvVxW0luO zG=lGwDNN_YX)}P#AHaO*5WSXHm=b<*G87_;)uS}CDnMq_&WtRw7Mb0CZf4WzxD*9# zEOLCm`;aXMbp(5el<9UABLXZk_+$6EX!*_#(SCv5Lque5*}(*Z%HU||u|-q{eV&}q zFCcWHRT+VFXn7J{MP&&KGoeDO(OI+(Q`^3^%OSL)JjOY|fchHg#9-{p36$!KWR5-O zEQ;iMRwTcCpNa&t238~+V1ojEQQslANtDa01D1=XucRv*Ix#>(O9jOC1!m2nWdzT4 zynJ4=MALP{+H=ZBEP*fYVueFmiD-71nf9+C$E6c9FfK6BK2TI!h3N+@#9EcCcI{KT zf!OC?bb=m`9~oMP=%C1w7jm$82ZNLFOPCH#>w{`v+pl3NNV@b4)@XbiVa5QZSnb7J zWAUR{;Da)%@58aTs1KcH8lm@|T)5sg#k3h(lC{94hi9-@e{pCSC?kST#%c)*FNQa^ z$MHCL%MCDYhW7t;L>&@%)V98MN*>;C1|y2$Q@QgyjC~K=+Um-1ozife>P%I4vok@P zeSbNNkz}My*f$=H$OTwFD8!`?q2fDe3xEcXp3$Ku7BP*RLT${5B_}D|orG4+VP}$^ z5W4Sm+U87e!vZ&kdAF!7EJ@g<&b4ZmBIvDdOF__+wpA4+qnJ>N_BYtiM8j?E7TR3TiTGv z_KF2>l-ygc%~r3-iDm zQs{}nTvK8s$y=Fg`lhI24<0vksDCRm?!^*DsJNiV?1Lr|?u<^S?7<_6GogD=LNCwQ z<4$Ln_+dRgdQR{1N{}+f5~U3F5eo=u zQ}_`f5D{s7-9~97ox{x8%v6=X`fYbP()A>?7xam;c_wzdfx<@L(KHrtfJ zSRyNU*ON-#o}|6Tlt>N)hR0+_!G>;nm70x(DWEzJRNZDe{g`Bf5wx&wQe03MGT)zu zY7jv!iRGh}=a(TenDT-p-=_XW$@$Q`x(o{0N0GDi%416h3|MkHY4k$f7|+Q*gTym+ zm5AAQCUNb>;LbIa)}(fspEDNby&#bWwEKklxzs}FLnn|8c`z*Gx`b-pRR|4D(Xa

L;jq4 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 diff --git a/3rdparty/SiftGPU/bin/Speed.exe b/3rdparty/SiftGPU/bin/Speed.exe deleted file mode 100644 index 8c72462fb754c28def2c5424987ed88011e84180..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 154112 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 diff --git a/3rdparty/SiftGPU/bin/TestWin.exe b/3rdparty/SiftGPU/bin/TestWin.exe deleted file mode 100644 index 6b3774c9ee860c6d526a620424c75a67c2193272..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 158720 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 diff --git a/3rdparty/SiftGPU/bin/glew32.dll b/3rdparty/SiftGPU/bin/glew32.dll deleted file mode 100644 index 8857ba675a4c95b444a6fca0ff8e050fb824ac49..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 245760 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 diff --git a/3rdparty/SiftGPU/bin/glew64.dll b/3rdparty/SiftGPU/bin/glew64.dll deleted file mode 100644 index e1dda8680570ef321b08165ccaf56452491ee53b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 279552 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 diff --git a/3rdparty/SiftGPU/license.txt b/3rdparty/SiftGPU/license.txt deleted file mode 100644 index c4c35d9b..00000000 --- a/3rdparty/SiftGPU/license.txt +++ /dev/null @@ -1,17 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/3rdparty/SiftGPU/makefile b/3rdparty/SiftGPU/makefile deleted file mode 100644 index 2059b927..00000000 --- a/3rdparty/SiftGPU/makefile +++ /dev/null @@ -1,207 +0,0 @@ -################################################################# -# SiftGPU congiruation: CUDA, SSE, TIMING -################################################################# -#enable siftgpu server -siftgpu_enable_server = 0 -#enable OpenCL-based SiftGPU? not finished yet; testing purpose -siftgpu_enable_opencl = 0 -#------------------------------------------------------------------------------------------------ -# enable CUDA-based SiftGPU? -simple_find_cuda = $(shell locate libcudart.so) -ifneq ($(simple_find_cuda), ) - siftgpu_enable_cuda = 0 -else - siftgpu_enable_cuda = 0 -endif - -CUDA_INSTALL_PATH = /usr/local/cuda -#change additional settings, like SM version here if it is not 1.0 (eg. -arch sm_13 for GTX280) -#siftgpu_cuda_options = -Xopencc -OPT:unroll_size=200000 -#siftgpu_cuda_options = -arch sm_10 -#-------------------------------------------------------------------------------------------------- -# enable SSE optimization for GL-based implementations -siftgpu_enable_sse = 1 -siftgpu_sse_options = -march=core2 -mfpmath=sse -#-------------------------------------------------------------------------------------------------- -# openGL context creation. 1 for glut, 0 for xlib -siftgpu_prefer_glut = 1 -#whether remove dependency on DevIL (1 to remove, the output libsiftgpu.so still works for VisualSFM) -siftgpu_disable_devil = 0 -#------------------------------------------------------------------------------------------------ -#whether SimpleSIFT uses runtime loading of libsiftgpu.so or static linking of libsiftgpu.a -simplesift_runtime_load = 1 - -################################################################# - - -# cleanup trailing whitespaces for a few settings -siftgpu_enable_cuda := $(strip $(siftgpu_enable_cuda)) -siftgpu_disable_devil := $(strip $(siftgpu_disable_devil)) -siftgpu_enable_server := $(strip $(siftgpu_enable_server)) -siftgpu_enable_opencl := $(strip $(siftgpu_enable_opencl)) -siftgpu_prefer_glut := $(strip $(siftgpu_prefer_glut)) -simplesift_runtime_load := $(strip $(simplesift_runtime_load)) - -# detect OS -OSUPPER = $(shell uname -s 2>/dev/null | tr [:lower:] [:upper:]) -OSLOWER = $(shell uname -s 2>/dev/null | tr [:upper:] [:lower:]) -DARWIN = $(strip $(findstring DARWIN, $(OSUPPER))) - - -SHELL = /bin/sh -INC_DIR = include -BIN_DIR = bin -SRC_SIFTGPU = src/SiftGPU -SRC_DRIVER = src/TestWin -SRC_SERVER = src/ServerSiftGPU -CC = g++ -CFLAGS = -I$(INC_DIR) -fPIC -L/usr/lib -L./bin -L./lib -Wall -Wno-deprecated -pthread - -#simple hack to repalce the native flat on OSX because gcc version is low -ifneq ($(DARWIN),) - siftgpu_sse_options = -march=core2 -mfpmath=sse -endif - -ifneq ($(siftgpu_enable_sse), 0) - CFLAGS += $(siftgpu_sse_options) -endif - -ifneq ($(siftgpu_prefer_glut), 0) - CFLAGS += -DWINDOW_PREFER_GLUT -endif - -ifneq ($(siftgpu_enable_opencl), 0) - CFLAGS += -DCL_SIFTGPU_ENABLED -endif - -ODIR_SIFTGPU = build - - -# external header files -_HEADER_EXTERNAL = GL/glew.h GL/glut.h IL/il.h -# siftgpu header files -_HEADER_SIFTGPU = FrameBufferObject.h GlobalUtil.h GLTexImage.h ProgramGPU.h ShaderMan.h ProgramGLSL.h SiftGPU.h SiftPyramid.h SiftMatch.h PyramidGL.h LiteWindow.h -# siftgpu library header files for drivers -_HEADER_SIFTGPU_LIB = SiftGPU.h - -ifneq ($(DARWIN),) -#librarys for SiftGPU -LIBS_SIFTGPU = -lGLEW -framework GLUT -framework OpenGL -CFLAGS += -L/Users/prb2pal/Development/Resources/lib -else -#librarys for SiftGPU -LIBS_SIFTGPU = -lGLEW -lglut -lGL -lX11 -endif - -ifneq ($(siftgpu_disable_devil), 0) - CFLAGS += -DSIFTGPU_NO_DEVIL -else - LIBS_SIFTGPU += -lIL -endif - -#Obj files for SiftGPU -_OBJ_SIFTGPU = FrameBufferObject.o GlobalUtil.o GLTexImage.o ProgramGLSL.o ProgramGPU.o ShaderMan.o SiftGPU.o SiftPyramid.o PyramidGL.o SiftMatch.o - -#add cuda options -ifneq ($(siftgpu_enable_cuda), 0) - ifdef CUDA_BIN_PATH - NVCC = $(CUDA_BIN_PATH)/nvcc - else - NVCC = $(CUDA_INSTALL_PATH)/bin/nvcc - endif - - ifndef CUDA_INC_PATH - CUDA_INC_PATH = $(CUDA_INSTALL_PATH)/include - endif - - ifndef CUDA_LIB_PATH - CUDA_LIB_PATH = $(CUDA_INSTALL_PATH)/lib64 -L$(CUDA_INSTALL_PATH)/lib - endif - - CFLAGS += -DCUDA_SIFTGPU_ENABLED -I$(CUDA_INC_PATH) -L$(CUDA_LIB_PATH) - LIBS_SIFTGPU += -lcudart - _OBJ_SIFTGPU += CuTexImage.o PyramidCU.o SiftMatchCU.o - _HEADER_SIFTGPU += CuTexImage.h ProgramCU.h PyramidCU.h -endif - -ifneq ($(siftgpu_enable_opencl), 0) - CFLAGS += -lOpenCL -endif - -all: makepath siftgpu server driver - - -#the dependencies of SiftGPU library -DEPS_SIFTGPU = $(patsubst %, $(SRC_SIFTGPU)/%, $(_HEADER_SIFTGPU)) - - -#rules for the rest of the object files -$(ODIR_SIFTGPU)/%.o: $(SRC_SIFTGPU)/%.cpp $(DEPS_SIFTGPU) - $(CC) -o $@ $< $(CFLAGS) -c - - -ifneq ($(siftgpu_enable_cuda), 0) -NVCC_FLAGS = -I$(INC_DIR) -I$(CUDA_INC_PATH) -DCUDA_SIFTGPU_ENABLED -O2 -Xcompiler -fPIC -ifdef siftgpu_cuda_options - NVCC_FLAGS += $(siftgpu_cuda_options) -endif -#build rule for CUDA -$(ODIR_SIFTGPU)/ProgramCU.o: $(SRC_SIFTGPU)/ProgramCU.cu $(DEPS_SIFTGPU) - $(NVCC) $(NVCC_FLAGS) -o $@ $< -c -_OBJ_SIFTGPU += ProgramCU.o -endif - - -ifneq ($(siftgpu_enable_server), 0) -$(ODIR_SIFTGPU)/ServerSiftGPU.o: $(SRC_SERVER)/ServerSiftGPU.cpp $(DEPS_SIFTGPU) - $(CC) -o $@ $< $(CFLAGS) -DSERVER_SIFTGPU_ENABLED -c -_OBJ_SIFTGPU += ServerSiftGPU.o -endif - -OBJ_SIFTGPU = $(patsubst %,$(ODIR_SIFTGPU)/%,$(_OBJ_SIFTGPU)) -LIBS_DRIVER = $(BIN_DIR)/libsiftgpu.a $(LIBS_SIFTGPU) -SRC_TESTWIN = $(SRC_DRIVER)/TestWinGlut.cpp $(SRC_DRIVER)/BasicTestWin.cpp -DEP_TESTWIN = $(SRC_DRIVER)/TestWinGlut.h $(SRC_DRIVER)/BasicTestwin.h $(SRC_DRIVER)/GLTransform.h - - - -ifneq ($(simplesift_runtime_load), 0) -LIBS_SIMPLESIFT = -ldl -DSIFTGPU_DLL_RUNTIME -else -LIBS_SIMPLESIFT = $(LIBS_DRIVER) -DSIFTGPU_STATIC -endif - -siftgpu: makepath $(OBJ_SIFTGPU) - ar rcs $(BIN_DIR)/libsiftgpu.a $(OBJ_SIFTGPU) - $(CC) -o $(BIN_DIR)/libsiftgpu.so $(OBJ_SIFTGPU) $(LIBS_SIFTGPU) $(CFLAGS) -shared -fPIC - -driver: makepath - $(CC) -o $(BIN_DIR)/TestWinGlut $(SRC_TESTWIN) $(LIBS_DRIVER) $(CFLAGS) - $(CC) -o $(BIN_DIR)/SimpleSIFT $(SRC_DRIVER)/SimpleSIFT.cpp $(LIBS_SIMPLESIFT) $(CFLAGS) - $(CC) -o $(BIN_DIR)/speed $(SRC_DRIVER)/speed.cpp $(LIBS_DRIVER) $(CFLAGS) - $(CC) -o $(BIN_DIR)/MultiThreadSIFT $(SRC_DRIVER)/MultiThreadSIFT.cpp $(LIBS_DRIVER) $(CFLAGS) -pthread - -ifneq ($(siftgpu_enable_server), 0) -server: makepath - $(CC) -o $(BIN_DIR)/server_siftgpu $(SRC_SERVER)/server.cpp $(LIBS_DRIVER) $(CFLAGS) -else -server: - -endif - -makepath: - mkdir -p $(ODIR_SIFTGPU) - mkdir -p $(BIN_DIR) - sed -i -e 's/\\/\//g' demos/*.bat - -clean: - rm -f $(ODIR_SIFTGPU)/*.o - rm -f $(BIN_DIR)/libsiftgpu.a - rm -f $(BIN_DIR)/libsiftgpu.so - rm -f $(BIN_DIR)/TestWinGlut - rm -f $(BIN_DIR)/SimpleSIFT - rm -f $(BIN_DIR)/speed - rm -f $(BIN_DIR)/server_siftgpu - rm -f $(BIN_DIR)/MultiThreadSIFT - rm -f ProgramCU.linkinfo - diff --git a/3rdparty/SiftGPU/msvc/ServerSiftGPU/SiftGPU_Server.dsp b/3rdparty/SiftGPU/msvc/ServerSiftGPU/SiftGPU_Server.dsp deleted file mode 100644 index 46978894..00000000 --- a/3rdparty/SiftGPU/msvc/ServerSiftGPU/SiftGPU_Server.dsp +++ /dev/null @@ -1,102 +0,0 @@ -# Microsoft Developer Studio Project File - Name="SiftGPU_Server" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=SiftGPU_Server - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "SiftGPU_Server.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "SiftGPU_Server.mak" CFG="SiftGPU_Server - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "SiftGPU_Server - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "SiftGPU_Server - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "SiftGPU_Server - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "SiftGPU_Server___Win32_Release" -# PROP BASE Intermediate_Dir "SiftGPU_Server___Win32_Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../Include/" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib ws2_32.lib siftgpu.lib opengl32.lib /nologo /subsystem:console /machine:I386 /out:"../../bin/server_siftgpu.exe" /libpath:"../../lib" - -!ELSEIF "$(CFG)" == "SiftGPU_Server - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "SiftGPU_Server___Win32_Debug" -# PROP BASE Intermediate_Dir "SiftGPU_Server___Win32_Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../Include/" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib ws2_32.lib siftgpu_d.lib opengl32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../../bin/server_siftgpu.exe" /pdbtype:sept /libpath:"../../lib" - -!ENDIF - -# Begin Target - -# Name "SiftGPU_Server - Win32 Release" -# Name "SiftGPU_Server - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=..\..\src\ServerSiftGPU\server.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project diff --git a/3rdparty/SiftGPU/msvc/ServerSiftGPU/SiftGPU_Server.vcxproj b/3rdparty/SiftGPU/msvc/ServerSiftGPU/SiftGPU_Server.vcxproj deleted file mode 100644 index 5ab88619..00000000 --- a/3rdparty/SiftGPU/msvc/ServerSiftGPU/SiftGPU_Server.vcxproj +++ /dev/null @@ -1,204 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup Label="ProjectConfigurations"> - <ProjectConfiguration Include="Debug|Win32"> - <Configuration>Debug</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Debug|x64"> - <Configuration>Debug</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|Win32"> - <Configuration>Release</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|x64"> - <Configuration>Release</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - </ItemGroup> - <PropertyGroup Label="Globals"> - <ProjectGuid>{65C987E1-F62C-4461-9E2C-BE1E8A770C51}</ProjectGuid> - <RootNamespace>SiftGPU_Server</RootNamespace> - <Keyword>Win32Proj</Keyword> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <CharacterSet>MultiByte</CharacterSet> - <WholeProgramOptimization>true</WholeProgramOptimization> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <CharacterSet>MultiByte</CharacterSet> - <WholeProgramOptimization>true</WholeProgramOptimization> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> - <ImportGroup Label="ExtensionSettings"> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <PropertyGroup Label="UserMacros" /> - <PropertyGroup> - <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\bin\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(Configuration)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\..\bin\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(Configuration)_$(Platform)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\bin\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(Configuration)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\..\bin\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(Configuration)_$(Platform)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental> - <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">server_siftgpu</TargetName> - <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">server_siftgpu</TargetName> - <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">server_siftgpu</TargetName> - <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">server_siftgpu</TargetName> - </PropertyGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <ClCompile> - <Optimization>Disabled</Optimization> - <AdditionalIncludeDirectories>../../Include/;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <MinimalRebuild>true</MinimalRebuild> - <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> - <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> - <PrecompiledHeader> - </PrecompiledHeader> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <WarningLevel>Level3</WarningLevel> - <DebugInformationFormat>EditAndContinue</DebugInformationFormat> - </ClCompile> - <Link> - <AdditionalDependencies>siftgpu_d.lib;%(AdditionalDependencies)</AdditionalDependencies> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <AdditionalLibraryDirectories>../../lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> - <GenerateDebugInformation>true</GenerateDebugInformation> - <SubSystem>Console</SubSystem> - <TargetMachine>MachineX86</TargetMachine> - </Link> - <ProjectReference> - <LinkLibraryDependencies>false</LinkLibraryDependencies> - </ProjectReference> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - <Midl> - <TargetEnvironment>X64</TargetEnvironment> - </Midl> - <ClCompile> - <Optimization>Disabled</Optimization> - <AdditionalIncludeDirectories>../../Include/;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <MinimalRebuild>true</MinimalRebuild> - <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> - <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> - <PrecompiledHeader> - </PrecompiledHeader> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <WarningLevel>Level3</WarningLevel> - <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> - </ClCompile> - <Link> - <AdditionalDependencies>siftgpu_d.lib;%(AdditionalDependencies)</AdditionalDependencies> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <AdditionalLibraryDirectories>../../lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> - <GenerateDebugInformation>true</GenerateDebugInformation> - <SubSystem>Console</SubSystem> - <TargetMachine>MachineX64</TargetMachine> - </Link> - <ProjectReference> - <LinkLibraryDependencies>false</LinkLibraryDependencies> - </ProjectReference> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <ClCompile> - <AdditionalIncludeDirectories>../../Include/;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <RuntimeLibrary>MultiThreaded</RuntimeLibrary> - <PrecompiledHeader> - </PrecompiledHeader> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <WarningLevel>Level3</WarningLevel> - <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> - </ClCompile> - <Link> - <AdditionalDependencies>siftgpu.lib;%(AdditionalDependencies)</AdditionalDependencies> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <AdditionalLibraryDirectories>../../lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> - <GenerateDebugInformation>true</GenerateDebugInformation> - <SubSystem>Console</SubSystem> - <OptimizeReferences>true</OptimizeReferences> - <EnableCOMDATFolding>true</EnableCOMDATFolding> - <TargetMachine>MachineX86</TargetMachine> - </Link> - <ProjectReference> - <LinkLibraryDependencies>false</LinkLibraryDependencies> - </ProjectReference> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> - <Midl> - <TargetEnvironment>X64</TargetEnvironment> - </Midl> - <ClCompile> - <AdditionalIncludeDirectories>../../Include/;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <RuntimeLibrary>MultiThreaded</RuntimeLibrary> - <PrecompiledHeader> - </PrecompiledHeader> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <WarningLevel>Level3</WarningLevel> - <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> - </ClCompile> - <Link> - <AdditionalDependencies>siftgpu.lib;%(AdditionalDependencies)</AdditionalDependencies> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <AdditionalLibraryDirectories>../../lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> - <GenerateDebugInformation>true</GenerateDebugInformation> - <SubSystem>Console</SubSystem> - <OptimizeReferences>true</OptimizeReferences> - <EnableCOMDATFolding>true</EnableCOMDATFolding> - <TargetMachine>MachineX64</TargetMachine> - </Link> - <ProjectReference> - <LinkLibraryDependencies>false</LinkLibraryDependencies> - </ProjectReference> - </ItemDefinitionGroup> - <ItemGroup> - <ClCompile Include="..\..\src\ServerSiftGPU\server.cpp" /> - </ItemGroup> - <ItemGroup> - <ProjectReference Include="..\SiftGPU\SiftGPU.vcxproj"> - <Project>{594562d3-609a-47bc-b7e2-789c4e794cc3}</Project> - <ReferenceOutputAssembly>false</ReferenceOutputAssembly> - </ProjectReference> - <ProjectReference Include="..\SiftGPU\SiftGPU_CUDA_Enabled.vcxproj"> - <Project>{9252e247-4fe2-4929-bd5d-c2fa16efd656}</Project> - <ReferenceOutputAssembly>false</ReferenceOutputAssembly> - </ProjectReference> - </ItemGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> - <ImportGroup Label="ExtensionTargets"> - </ImportGroup> -</Project> \ No newline at end of file diff --git a/3rdparty/SiftGPU/msvc/SiftGPU.dsw b/3rdparty/SiftGPU/msvc/SiftGPU.dsw deleted file mode 100644 index 8af0f428..00000000 --- a/3rdparty/SiftGPU/msvc/SiftGPU.dsw +++ /dev/null @@ -1,119 +0,0 @@ -Microsoft Developer Studio Workspace File, Format Version 6.00 -# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! - -############################################################################### - -Project: "SiftGPU"=.\SiftGPU\SiftGPU.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "SiftGPU_Server"=.\ServerSiftGPU\SiftGPU_Server.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name SiftGPU - End Project Dependency -}}} - -############################################################################### - -Project: "SimpleSIFT"=.\TestWin\SimpleSIFT.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name SiftGPU - End Project Dependency -}}} - -############################################################################### - -Project: "Speed"=.\TestWin\Speed.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name SiftGPU - End Project Dependency -}}} - -############################################################################### - -Project: "TestBase"=.\TestWin\TestBase.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name SiftGPU - End Project Dependency -}}} - -############################################################################### - -Project: "TestWin"=.\TestWin\TestWin.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name TestBase - End Project Dependency -}}} - -############################################################################### - -Project: "TestWinGlut"=.\TestWin\TestWinGlut.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name TestBase - End Project Dependency -}}} - -############################################################################### - -Global: - -Package=<5> -{{{ -}}} - -Package=<3> -{{{ -}}} - -############################################################################### - diff --git a/3rdparty/SiftGPU/msvc/SiftGPU.sln b/3rdparty/SiftGPU/msvc/SiftGPU.sln deleted file mode 100644 index e4f56a40..00000000 --- a/3rdparty/SiftGPU/msvc/SiftGPU.sln +++ /dev/null @@ -1,86 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SiftGPU", "SiftGPU\SiftGPU.vcxproj", "{594562D3-609A-47BC-B7E2-789C4E794CC3}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestBase", "TestWin\TestBase.vcxproj", "{57496D03-A005-4650-B041-8E70DDA4A591}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestWin", "TestWin\TestWin.vcxproj", "{E1C4EAB3-1677-46F7-92BC-1A1F309B2027}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestWinGlut", "TestWin\TestWinGlut.vcxproj", "{B33F9B4E-BF99-442B-BDC5-3DAA4BCCA395}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SimpleSIFT", "TestWin\SimpleSIFT.vcxproj", "{D864A317-5A65-47B1-AA82-C04F99C4280F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Speed", "TestWin\Speed.vcxproj", "{7FF72B39-F50E-4ED5-9A50-DCD60EF03604}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SiftGPU_Server", "ServerSiftGPU\SiftGPU_Server.vcxproj", "{65C987E1-F62C-4461-9E2C-BE1E8A770C51}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Debug|x64 = Debug|x64 - Release|Win32 = Release|Win32 - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {594562D3-609A-47BC-B7E2-789C4E794CC3}.Debug|Win32.ActiveCfg = Debug|Win32 - {594562D3-609A-47BC-B7E2-789C4E794CC3}.Debug|Win32.Build.0 = Debug|Win32 - {594562D3-609A-47BC-B7E2-789C4E794CC3}.Debug|x64.ActiveCfg = Debug|x64 - {594562D3-609A-47BC-B7E2-789C4E794CC3}.Debug|x64.Build.0 = Debug|x64 - {594562D3-609A-47BC-B7E2-789C4E794CC3}.Release|Win32.ActiveCfg = Release|Win32 - {594562D3-609A-47BC-B7E2-789C4E794CC3}.Release|Win32.Build.0 = Release|Win32 - {594562D3-609A-47BC-B7E2-789C4E794CC3}.Release|x64.ActiveCfg = Release|x64 - {594562D3-609A-47BC-B7E2-789C4E794CC3}.Release|x64.Build.0 = Release|x64 - {57496D03-A005-4650-B041-8E70DDA4A591}.Debug|Win32.ActiveCfg = Debug|Win32 - {57496D03-A005-4650-B041-8E70DDA4A591}.Debug|Win32.Build.0 = Debug|Win32 - {57496D03-A005-4650-B041-8E70DDA4A591}.Debug|x64.ActiveCfg = Debug|x64 - {57496D03-A005-4650-B041-8E70DDA4A591}.Debug|x64.Build.0 = Debug|x64 - {57496D03-A005-4650-B041-8E70DDA4A591}.Release|Win32.ActiveCfg = Release|Win32 - {57496D03-A005-4650-B041-8E70DDA4A591}.Release|Win32.Build.0 = Release|Win32 - {57496D03-A005-4650-B041-8E70DDA4A591}.Release|x64.ActiveCfg = Release|x64 - {57496D03-A005-4650-B041-8E70DDA4A591}.Release|x64.Build.0 = Release|x64 - {E1C4EAB3-1677-46F7-92BC-1A1F309B2027}.Debug|Win32.ActiveCfg = Debug|Win32 - {E1C4EAB3-1677-46F7-92BC-1A1F309B2027}.Debug|Win32.Build.0 = Debug|Win32 - {E1C4EAB3-1677-46F7-92BC-1A1F309B2027}.Debug|x64.ActiveCfg = Debug|x64 - {E1C4EAB3-1677-46F7-92BC-1A1F309B2027}.Debug|x64.Build.0 = Debug|x64 - {E1C4EAB3-1677-46F7-92BC-1A1F309B2027}.Release|Win32.ActiveCfg = Release|Win32 - {E1C4EAB3-1677-46F7-92BC-1A1F309B2027}.Release|Win32.Build.0 = Release|Win32 - {E1C4EAB3-1677-46F7-92BC-1A1F309B2027}.Release|x64.ActiveCfg = Release|x64 - {E1C4EAB3-1677-46F7-92BC-1A1F309B2027}.Release|x64.Build.0 = Release|x64 - {B33F9B4E-BF99-442B-BDC5-3DAA4BCCA395}.Debug|Win32.ActiveCfg = Debug|Win32 - {B33F9B4E-BF99-442B-BDC5-3DAA4BCCA395}.Debug|Win32.Build.0 = Debug|Win32 - {B33F9B4E-BF99-442B-BDC5-3DAA4BCCA395}.Debug|x64.ActiveCfg = Debug|x64 - {B33F9B4E-BF99-442B-BDC5-3DAA4BCCA395}.Debug|x64.Build.0 = Debug|x64 - {B33F9B4E-BF99-442B-BDC5-3DAA4BCCA395}.Release|Win32.ActiveCfg = Release|Win32 - {B33F9B4E-BF99-442B-BDC5-3DAA4BCCA395}.Release|Win32.Build.0 = Release|Win32 - {B33F9B4E-BF99-442B-BDC5-3DAA4BCCA395}.Release|x64.ActiveCfg = Release|x64 - {B33F9B4E-BF99-442B-BDC5-3DAA4BCCA395}.Release|x64.Build.0 = Release|x64 - {D864A317-5A65-47B1-AA82-C04F99C4280F}.Debug|Win32.ActiveCfg = Debug|Win32 - {D864A317-5A65-47B1-AA82-C04F99C4280F}.Debug|Win32.Build.0 = Debug|Win32 - {D864A317-5A65-47B1-AA82-C04F99C4280F}.Debug|x64.ActiveCfg = Debug|x64 - {D864A317-5A65-47B1-AA82-C04F99C4280F}.Debug|x64.Build.0 = Debug|x64 - {D864A317-5A65-47B1-AA82-C04F99C4280F}.Release|Win32.ActiveCfg = Release|Win32 - {D864A317-5A65-47B1-AA82-C04F99C4280F}.Release|Win32.Build.0 = Release|Win32 - {D864A317-5A65-47B1-AA82-C04F99C4280F}.Release|x64.ActiveCfg = Release|x64 - {D864A317-5A65-47B1-AA82-C04F99C4280F}.Release|x64.Build.0 = Release|x64 - {7FF72B39-F50E-4ED5-9A50-DCD60EF03604}.Debug|Win32.ActiveCfg = Debug|Win32 - {7FF72B39-F50E-4ED5-9A50-DCD60EF03604}.Debug|Win32.Build.0 = Debug|Win32 - {7FF72B39-F50E-4ED5-9A50-DCD60EF03604}.Debug|x64.ActiveCfg = Debug|x64 - {7FF72B39-F50E-4ED5-9A50-DCD60EF03604}.Debug|x64.Build.0 = Debug|x64 - {7FF72B39-F50E-4ED5-9A50-DCD60EF03604}.Release|Win32.ActiveCfg = Release|Win32 - {7FF72B39-F50E-4ED5-9A50-DCD60EF03604}.Release|Win32.Build.0 = Release|Win32 - {7FF72B39-F50E-4ED5-9A50-DCD60EF03604}.Release|x64.ActiveCfg = Release|x64 - {7FF72B39-F50E-4ED5-9A50-DCD60EF03604}.Release|x64.Build.0 = Release|x64 - {65C987E1-F62C-4461-9E2C-BE1E8A770C51}.Debug|Win32.ActiveCfg = Debug|Win32 - {65C987E1-F62C-4461-9E2C-BE1E8A770C51}.Debug|Win32.Build.0 = Debug|Win32 - {65C987E1-F62C-4461-9E2C-BE1E8A770C51}.Debug|x64.ActiveCfg = Debug|x64 - {65C987E1-F62C-4461-9E2C-BE1E8A770C51}.Debug|x64.Build.0 = Debug|x64 - {65C987E1-F62C-4461-9E2C-BE1E8A770C51}.Release|Win32.ActiveCfg = Release|Win32 - {65C987E1-F62C-4461-9E2C-BE1E8A770C51}.Release|Win32.Build.0 = Release|Win32 - {65C987E1-F62C-4461-9E2C-BE1E8A770C51}.Release|x64.ActiveCfg = Release|x64 - {65C987E1-F62C-4461-9E2C-BE1E8A770C51}.Release|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/3rdparty/SiftGPU/msvc/SiftGPU/SiftGPU.def b/3rdparty/SiftGPU/msvc/SiftGPU/SiftGPU.def deleted file mode 100644 index 5512ab90..00000000 --- a/3rdparty/SiftGPU/msvc/SiftGPU/SiftGPU.def +++ /dev/null @@ -1,6 +0,0 @@ -EXPORTS - CreateNewSiftGPU @1 - CreateNewSiftMatchGPU @2 - CreateLiteWindow @3 - CreateComboSiftGPU @4 - CreateRemoteSiftGPU @5 diff --git a/3rdparty/SiftGPU/msvc/SiftGPU/SiftGPU.dsp b/3rdparty/SiftGPU/msvc/SiftGPU/SiftGPU.dsp deleted file mode 100644 index 1bfcd0a4..00000000 --- a/3rdparty/SiftGPU/msvc/SiftGPU/SiftGPU.dsp +++ /dev/null @@ -1,204 +0,0 @@ -# Microsoft Developer Studio Project File - Name="SiftGPU" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 - -CFG=SiftGPU - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "SiftGPU.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "SiftGPU.mak" CFG="SiftGPU - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "SiftGPU - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE "SiftGPU - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -MTL=midl.exe -RSC=rc.exe - -!IF "$(CFG)" == "SiftGPU - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SIFTGPU_EXPORTS" /Yu"stdafx.h" /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../Include/" /D "NDEBUG" /D "SIFTGPU_EXPORTS" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SIFTGPU_DLL" /D "DLL_EXPORT" /D "SERVER_SIFTGPU_ENABLED" /FD /c -# SUBTRACT CPP /YX /Yc /Yu -# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib opengl32.lib glu32.lib winmm.lib glew32.lib glew32s.lib /nologo /dll /machine:I386 /def:".\SiftGPU.def" /out:"../../bin/SIFTGPU.dll" /implib:"../../lib/SIFTGPU.lib" /libpath:"../../lib/" -# SUBTRACT LINK32 /pdb:none - -!ELSEIF "$(CFG)" == "SiftGPU - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SIFTGPU_EXPORTS" /Yu"stdafx.h" /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../Include/" /D "_DEBUG" /D "DLL_EXPORT" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SIFTGPU_DLL" /D "SERVER_SIFTGPU_ENABLED" /FD /GZ /c -# SUBTRACT CPP /YX /Yc /Yu -# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib opengl32.lib glu32.lib winmm.lib glew32.lib glew32s.lib /nologo /dll /debug /machine:I386 /def:".\SiftGPU.def" /out:"../../bin/SIFTGPU_d.dll" /implib:"../../lib/SIFTGPU_d.lib" /pdbtype:sept /libpath:"../../lib/" -# SUBTRACT LINK32 /pdb:none - -!ENDIF - -# Begin Target - -# Name "SiftGPU - Win32 Release" -# Name "SiftGPU - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=..\..\src\SiftGPU\FrameBufferObject.cpp -# End Source File -# Begin Source File - -SOURCE=..\..\src\SiftGPU\GlobalUtil.cpp -# End Source File -# Begin Source File - -SOURCE=..\..\src\SiftGPU\GLTexImage.cpp -# End Source File -# Begin Source File - -SOURCE=..\..\src\SiftGPU\ProgramGLSL.cpp -# End Source File -# Begin Source File - -SOURCE=..\..\src\SiftGPU\ProgramGPU.cpp -# End Source File -# Begin Source File - -SOURCE=..\..\src\SiftGPU\PyramidGL.cpp -# End Source File -# Begin Source File - -SOURCE=..\..\src\ServerSiftGPU\ServerSiftGPU.cpp -# End Source File -# Begin Source File - -SOURCE=..\..\src\SiftGPU\ShaderMan.cpp -# End Source File -# Begin Source File - -SOURCE=..\..\src\SiftGPU\SiftGPU.cpp -# End Source File -# Begin Source File - -SOURCE=.\SiftGPU.def -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\..\src\SiftGPU\SiftMatch.cpp -# End Source File -# Begin Source File - -SOURCE=..\..\src\SiftGPU\SIFTPyramid.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=..\..\src\SiftGPU\FrameBufferObject.h -# End Source File -# Begin Source File - -SOURCE=..\..\src\SiftGPU\GlobalUtil.h -# End Source File -# Begin Source File - -SOURCE=..\..\src\SiftGPU\GLTexImage.h -# End Source File -# Begin Source File - -SOURCE=..\..\src\SiftGPU\LiteWindow.h -# End Source File -# Begin Source File - -SOURCE=..\..\src\SiftGPU\ProgramGLSL.h -# End Source File -# Begin Source File - -SOURCE=..\..\src\SiftGPU\programGPU.h -# End Source File -# Begin Source File - -SOURCE=..\..\src\SiftGPU\PyramidGL.h -# End Source File -# Begin Source File - -SOURCE=..\..\src\ServerSiftGPU\ServerSiftGPU.h -# End Source File -# Begin Source File - -SOURCE=..\..\src\SiftGPU\ShaderMan.h -# End Source File -# Begin Source File - -SOURCE=..\..\src\SiftGPU\SiftGPU.h -# End Source File -# Begin Source File - -SOURCE=..\..\src\SiftGPU\SiftMatch.h -# End Source File -# Begin Source File - -SOURCE=..\..\src\SiftGPU\SIFTPyramid.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project diff --git a/3rdparty/SiftGPU/msvc/SiftGPU/SiftGPU.vcxproj b/3rdparty/SiftGPU/msvc/SiftGPU/SiftGPU.vcxproj deleted file mode 100644 index 6d3542d5..00000000 --- a/3rdparty/SiftGPU/msvc/SiftGPU/SiftGPU.vcxproj +++ /dev/null @@ -1,375 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup Label="ProjectConfigurations"> - <ProjectConfiguration Include="Debug|Win32"> - <Configuration>Debug</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Debug|x64"> - <Configuration>Debug</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|Win32"> - <Configuration>Release</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|x64"> - <Configuration>Release</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - </ItemGroup> - <PropertyGroup Label="Globals"> - <ProjectGuid>{594562D3-609A-47BC-B7E2-789C4E794CC3}</ProjectGuid> - <RootNamespace>SiftGPU</RootNamespace> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> - <ConfigurationType>DynamicLibrary</ConfigurationType> - <UseOfMfc>false</UseOfMfc> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> - <ConfigurationType>DynamicLibrary</ConfigurationType> - <UseOfMfc>false</UseOfMfc> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> - <ConfigurationType>DynamicLibrary</ConfigurationType> - <UseOfMfc>false</UseOfMfc> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> - <ConfigurationType>DynamicLibrary</ConfigurationType> - <UseOfMfc>false</UseOfMfc> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> - <ImportGroup Label="ExtensionSettings"> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> - </ImportGroup> - <PropertyGroup Label="UserMacros" /> - <PropertyGroup> - <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\bin\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(Configuration)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\bin\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(Configuration)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\..\bin\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(Configuration)_$(Platform)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\..\bin\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(Configuration)_$(Platform)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental> - <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(ProjectName)_d</TargetName> - <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectName)_d</TargetName> - </PropertyGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Midl> - <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <MkTypLibCompatible>true</MkTypLibCompatible> - <SuppressStartupBanner>true</SuppressStartupBanner> - <TargetEnvironment>Win32</TargetEnvironment> - <TypeLibraryName>.\Release/SiftGPU.tlb</TypeLibraryName> - <HeaderFileName> - </HeaderFileName> - </Midl> - <ClCompile> - <Optimization>MaxSpeed</Optimization> - <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> - <IntrinsicFunctions>false</IntrinsicFunctions> - <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed> - <AdditionalIncludeDirectories>../../Include/;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>NDEBUG;SIFTGPU_EXPORTS;WIN32;_WINDOWS;_USRDLL;SIFTGPU_DLL;DLL_EXPORT;_CRT_SECURE_NO_DEPRECATE;SERVER_SIFTGPU_ENABLED;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <IgnoreStandardIncludePath>false</IgnoreStandardIncludePath> - <StringPooling>true</StringPooling> - <RuntimeLibrary>MultiThreaded</RuntimeLibrary> - <FunctionLevelLinking>true</FunctionLevelLinking> - <PrecompiledHeaderOutputFile> - </PrecompiledHeaderOutputFile> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <ObjectFileName>$(IntDir)</ObjectFileName> - <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName> - <WarningLevel>Level3</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <Culture>0x0409</Culture> - </ResourceCompile> - <Link> - <AdditionalDependencies>opengl32.lib;glu32.lib;winmm.lib;glew32.lib;glew32s.lib;%(AdditionalDependencies)</AdditionalDependencies> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - <AdditionalLibraryDirectories>../../lib/;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> - <ModuleDefinitionFile>SiftGPU.def</ModuleDefinitionFile> - <ProgramDatabaseFile>$(IntDir)/SIFTGPU.pdb</ProgramDatabaseFile> - <ImportLibrary>../../lib/SIFTGPU.lib</ImportLibrary> - <TargetMachine>MachineX86</TargetMachine> - </Link> - <Bscmake> - <SuppressStartupBanner>true</SuppressStartupBanner> - <OutputFile>.\Release/SiftGPU.bsc</OutputFile> - </Bscmake> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Midl> - <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <MkTypLibCompatible>true</MkTypLibCompatible> - <SuppressStartupBanner>true</SuppressStartupBanner> - <TargetEnvironment>Win32</TargetEnvironment> - <TypeLibraryName>.\Debug/SiftGPU.tlb</TypeLibraryName> - <HeaderFileName> - </HeaderFileName> - </Midl> - <ClCompile> - <Optimization>Disabled</Optimization> - <AdditionalIncludeDirectories>../../Include/;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>_DEBUG;DLL_EXPORT;WIN32;_WINDOWS;_USRDLL;SIFTGPU_DLL;_CRT_SECURE_NO_DEPRECATE;SERVER_SIFTGPU_ENABLED;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <IgnoreStandardIncludePath>false</IgnoreStandardIncludePath> - <MinimalRebuild>true</MinimalRebuild> - <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> - <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> - <PrecompiledHeaderOutputFile> - </PrecompiledHeaderOutputFile> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <ObjectFileName>$(IntDir)</ObjectFileName> - <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName> - <WarningLevel>Level3</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - <DebugInformationFormat>EditAndContinue</DebugInformationFormat> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <Culture>0x0409</Culture> - </ResourceCompile> - <Link> - <AdditionalDependencies>opengl32.lib;glu32.lib;winmm.lib;glew32.lib;glew32s.lib;%(AdditionalDependencies)</AdditionalDependencies> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - <AdditionalLibraryDirectories>../../lib/;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> - <ModuleDefinitionFile>SiftGPU.def</ModuleDefinitionFile> - <GenerateDebugInformation>true</GenerateDebugInformation> - <ProgramDatabaseFile>$(IntDir)/SIFTGPU.pdb</ProgramDatabaseFile> - <ImportLibrary>../../lib/SIFTGPU_d.lib</ImportLibrary> - <TargetMachine>MachineX86</TargetMachine> - </Link> - <Bscmake> - <SuppressStartupBanner>true</SuppressStartupBanner> - <OutputFile>.\Debug/SiftGPU.bsc</OutputFile> - </Bscmake> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> - <Midl> - <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <MkTypLibCompatible>true</MkTypLibCompatible> - <SuppressStartupBanner>true</SuppressStartupBanner> - <TargetEnvironment>X64</TargetEnvironment> - <TypeLibraryName>.\Release/SiftGPU.tlb</TypeLibraryName> - <HeaderFileName> - </HeaderFileName> - </Midl> - <ClCompile> - <Optimization>MaxSpeed</Optimization> - <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> - <IntrinsicFunctions>false</IntrinsicFunctions> - <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed> - <AdditionalIncludeDirectories>../../Include/;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>NDEBUG;SIFTGPU_EXPORTS;WIN32;_WINDOWS;_USRDLL;SIFTGPU_DLL;DLL_EXPORT;_CRT_SECURE_NO_DEPRECATE;SERVER_SIFTGPU_ENABLED;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <IgnoreStandardIncludePath>false</IgnoreStandardIncludePath> - <StringPooling>true</StringPooling> - <RuntimeLibrary>MultiThreaded</RuntimeLibrary> - <FunctionLevelLinking>true</FunctionLevelLinking> - <PrecompiledHeaderOutputFile> - </PrecompiledHeaderOutputFile> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <ObjectFileName>$(IntDir)</ObjectFileName> - <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName> - <WarningLevel>Level3</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <Culture>0x0409</Culture> - </ResourceCompile> - <Link> - <AdditionalDependencies>opengl32.lib;glu32.lib;winmm.lib;glew64.lib;glew64s.lib;%(AdditionalDependencies)</AdditionalDependencies> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - <AdditionalLibraryDirectories>../../lib/;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> - <ModuleDefinitionFile>SiftGPU.def</ModuleDefinitionFile> - <ProgramDatabaseFile>$(IntDir)/SIFTGPU.pdb</ProgramDatabaseFile> - <ImportLibrary>../../lib/SIFTGPU.lib</ImportLibrary> - <TargetMachine>MachineX64</TargetMachine> - </Link> - <Bscmake> - <SuppressStartupBanner>true</SuppressStartupBanner> - <OutputFile>.\Release/SiftGPU.bsc</OutputFile> - </Bscmake> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - <Midl> - <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <MkTypLibCompatible>true</MkTypLibCompatible> - <SuppressStartupBanner>true</SuppressStartupBanner> - <TargetEnvironment>X64</TargetEnvironment> - <TypeLibraryName>.\Debug/SiftGPU.tlb</TypeLibraryName> - <HeaderFileName> - </HeaderFileName> - </Midl> - <ClCompile> - <Optimization>Disabled</Optimization> - <AdditionalIncludeDirectories>../../Include/;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>_DEBUG;DLL_EXPORT;WIN32;_WINDOWS;_USRDLL;SIFTGPU_DLL;_CRT_SECURE_NO_DEPRECATE;SERVER_SIFTGPU_ENABLED;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <IgnoreStandardIncludePath>false</IgnoreStandardIncludePath> - <MinimalRebuild>true</MinimalRebuild> - <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> - <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> - <PrecompiledHeaderOutputFile> - </PrecompiledHeaderOutputFile> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <ObjectFileName>$(IntDir)</ObjectFileName> - <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName> - <WarningLevel>Level3</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <Culture>0x0409</Culture> - </ResourceCompile> - <Link> - <AdditionalDependencies>opengl32.lib;glu32.lib;winmm.lib;glew64.lib;glew64s.lib;%(AdditionalDependencies)</AdditionalDependencies> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - <AdditionalLibraryDirectories>../../lib/;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> - <ModuleDefinitionFile>SiftGPU.def</ModuleDefinitionFile> - <GenerateDebugInformation>true</GenerateDebugInformation> - <ProgramDatabaseFile>$(IntDir)/SIFTGPU.pdb</ProgramDatabaseFile> - <ImportLibrary>../../lib/SIFTGPU_d.lib</ImportLibrary> - <TargetMachine>MachineX64</TargetMachine> - </Link> - <Bscmake> - <SuppressStartupBanner>true</SuppressStartupBanner> - <OutputFile>.\Debug/SiftGPU.bsc</OutputFile> - </Bscmake> - </ItemDefinitionGroup> - <ItemGroup> - <ClCompile Include="..\..\src\SiftGPU\CLTexImage.cpp" /> - <ClCompile Include="..\..\src\SiftGPU\FrameBufferObject.cpp"> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> - <ClCompile Include="..\..\src\SiftGPU\GlobalUtil.cpp"> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> - <ClCompile Include="..\..\src\SiftGPU\GLTexImage.cpp"> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> - <ClCompile Include="..\..\src\SiftGPU\ProgramCL.cpp" /> - <ClCompile Include="..\..\src\SiftGPU\ProgramGLSL.cpp"> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> - <ClCompile Include="..\..\src\SiftGPU\ProgramGPU.cpp"> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> - <ClCompile Include="..\..\src\SiftGPU\PyramidCL.cpp" /> - <ClCompile Include="..\..\src\SiftGPU\PyramidGL.cpp" /> - <ClCompile Include="..\..\src\ServerSiftGPU\ServerSiftGPU.cpp" /> - <ClCompile Include="..\..\src\SiftGPU\ShaderMan.cpp"> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> - <ClCompile Include="..\..\src\SiftGPU\SiftGPU.cpp"> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> - <ClCompile Include="..\..\src\SiftGPU\SiftMatch.cpp" /> - <ClCompile Include="..\..\src\SiftGPU\SiftPyramid.cpp" /> - </ItemGroup> - <ItemGroup> - <None Include="SiftGPU.def" /> - </ItemGroup> - <ItemGroup> - <ClInclude Include="..\..\src\SiftGPU\CLTexImage.h" /> - <ClInclude Include="..\..\src\SiftGPU\FrameBufferObject.h" /> - <ClInclude Include="..\..\src\SiftGPU\GlobalUtil.h" /> - <ClInclude Include="..\..\src\SiftGPU\GLTexImage.h" /> - <ClInclude Include="..\..\src\SiftGPU\LiteWindow.h" /> - <ClInclude Include="..\..\src\SiftGPU\ProgramCL.h" /> - <ClInclude Include="..\..\src\SiftGPU\ProgramGLSL.h" /> - <ClInclude Include="..\..\src\SiftGPU\ProgramGPU.h" /> - <ClInclude Include="..\..\src\SiftGPU\PyramidCL.h" /> - <ClInclude Include="..\..\src\SiftGPU\PyramidGL.h" /> - <ClInclude Include="..\..\src\ServerSiftGPU\ServerSiftGPU.h" /> - <ClInclude Include="..\..\src\SiftGPU\ShaderMan.h" /> - <ClInclude Include="..\..\src\SiftGPU\SiftGPU.h" /> - <ClInclude Include="..\..\src\SiftGPU\SiftMatch.h" /> - <ClInclude Include="..\..\src\SiftGPU\SiftPyramid.h" /> - </ItemGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> - <ImportGroup Label="ExtensionTargets"> - </ImportGroup> -</Project> \ No newline at end of file diff --git a/3rdparty/SiftGPU/msvc/SiftGPU/SiftGPU_CUDA_Enabled.vcxproj b/3rdparty/SiftGPU/msvc/SiftGPU/SiftGPU_CUDA_Enabled.vcxproj deleted file mode 100644 index c9b216a4..00000000 --- a/3rdparty/SiftGPU/msvc/SiftGPU/SiftGPU_CUDA_Enabled.vcxproj +++ /dev/null @@ -1,390 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup Label="ProjectConfigurations"> - <ProjectConfiguration Include="Debug|Win32"> - <Configuration>Debug</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Debug|x64"> - <Configuration>Debug</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|Win32"> - <Configuration>Release</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|x64"> - <Configuration>Release</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - </ItemGroup> - <PropertyGroup Label="Globals"> - <ProjectGuid>{9252E247-4FE2-4929-BD5D-C2FA16EFD656}</ProjectGuid> - <RootNamespace>SiftGPU_CUDA_Enabled</RootNamespace> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> - <ConfigurationType>DynamicLibrary</ConfigurationType> - <UseOfMfc>false</UseOfMfc> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> - <ConfigurationType>DynamicLibrary</ConfigurationType> - <UseOfMfc>false</UseOfMfc> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> - <ConfigurationType>DynamicLibrary</ConfigurationType> - <UseOfMfc>false</UseOfMfc> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> - <ConfigurationType>DynamicLibrary</ConfigurationType> - <UseOfMfc>false</UseOfMfc> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> - <ImportGroup Label="ExtensionSettings"> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> - </ImportGroup> - <PropertyGroup Label="UserMacros" /> - <PropertyGroup> - <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\bin\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release_CUDA\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\bin\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug_CUDA\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\..\bin\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(Configuration)_CUDA_$(Platform)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\..\bin\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(Configuration)_CUDA_$(Platform)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental> - <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">SiftGPU_d</TargetName> - <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">SiftGPU_d</TargetName> - <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">SiftGPU</TargetName> - <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">SiftGPU</TargetName> - </PropertyGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Midl> - <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <MkTypLibCompatible>true</MkTypLibCompatible> - <SuppressStartupBanner>true</SuppressStartupBanner> - <TargetEnvironment>Win32</TargetEnvironment> - <TypeLibraryName>.\Release/SiftGPU.tlb</TypeLibraryName> - <HeaderFileName> - </HeaderFileName> - </Midl> - <ClCompile> - <Optimization>MaxSpeed</Optimization> - <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> - <AdditionalIncludeDirectories>../../Include/;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>NDEBUG;SIFTGPU_EXPORTS;WIN32;_WINDOWS;_USRDLL;SIFTGPU_DLL;DLL_EXPORT;_CRT_SECURE_NO_DEPRECATE;CUDA_SIFTGPU_ENABLED;SERVER_SIFTGPU_ENABLED;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <IgnoreStandardIncludePath>false</IgnoreStandardIncludePath> - <StringPooling>true</StringPooling> - <RuntimeLibrary>MultiThreaded</RuntimeLibrary> - <FunctionLevelLinking>true</FunctionLevelLinking> - <PrecompiledHeaderOutputFile>$(IntDir)/SIFTGPU.pch</PrecompiledHeaderOutputFile> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <ObjectFileName>$(IntDir)</ObjectFileName> - <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName> - <WarningLevel>Level3</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <Culture>0x0409</Culture> - </ResourceCompile> - <Link> - <AdditionalDependencies>opengl32.lib;glu32.lib;winmm.lib;cuda.lib;cudart.lib;glew32.lib;glew32s.lib;%(AdditionalDependencies)</AdditionalDependencies> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - <AdditionalLibraryDirectories>../../lib/;$(CUDA_LIB_PATH)/../Win32;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> - <ModuleDefinitionFile>SiftGPU.def</ModuleDefinitionFile> - <ProgramDatabaseFile>$(IntDir)/SIFTGPU.pdb</ProgramDatabaseFile> - <ImportLibrary>../../lib/SIFTGPU.lib</ImportLibrary> - <TargetMachine>MachineX86</TargetMachine> - </Link> - <Bscmake> - <SuppressStartupBanner>true</SuppressStartupBanner> - <OutputFile>.\Release/SiftGPU.bsc</OutputFile> - </Bscmake> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Midl> - <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <MkTypLibCompatible>true</MkTypLibCompatible> - <SuppressStartupBanner>true</SuppressStartupBanner> - <TargetEnvironment>Win32</TargetEnvironment> - <TypeLibraryName>.\Debug/SiftGPU.tlb</TypeLibraryName> - <HeaderFileName> - </HeaderFileName> - </Midl> - <ClCompile> - <Optimization>Disabled</Optimization> - <AdditionalIncludeDirectories>../../Include/;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>_DEBUG;DLL_EXPORT;WIN32;_WINDOWS;_USRDLL;SIFTGPU_DLL;_CRT_SECURE_NO_DEPRECATE;CUDA_SIFTGPU_ENABLED;SERVER_SIFTGPU_ENABLED;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <IgnoreStandardIncludePath>false</IgnoreStandardIncludePath> - <MinimalRebuild>true</MinimalRebuild> - <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> - <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> - <PrecompiledHeaderOutputFile>$(IntDir)/SIFTGPU.pch</PrecompiledHeaderOutputFile> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <ObjectFileName>$(IntDir)</ObjectFileName> - <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName> - <WarningLevel>Level3</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - <DebugInformationFormat>EditAndContinue</DebugInformationFormat> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <Culture>0x0409</Culture> - </ResourceCompile> - <Link> - <AdditionalDependencies>opengl32.lib;glu32.lib;winmm.lib;cuda.lib;cudart.lib;glew32.lib;glew32s.lib;%(AdditionalDependencies)</AdditionalDependencies> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - <AdditionalLibraryDirectories>../../lib/;$(CUDA_LIB_PATH)/../Win32;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> - <ModuleDefinitionFile>SiftGPU.def</ModuleDefinitionFile> - <GenerateDebugInformation>true</GenerateDebugInformation> - <ProgramDatabaseFile>$(IntDir)/SIFTGPU.pdb</ProgramDatabaseFile> - <ImportLibrary>../../lib/SIFTGPU_d.lib</ImportLibrary> - <TargetMachine>MachineX86</TargetMachine> - </Link> - <Bscmake> - <SuppressStartupBanner>true</SuppressStartupBanner> - <OutputFile>.\Debug/SiftGPU.bsc</OutputFile> - </Bscmake> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> - <Midl> - <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <MkTypLibCompatible>true</MkTypLibCompatible> - <SuppressStartupBanner>true</SuppressStartupBanner> - <TargetEnvironment>X64</TargetEnvironment> - <TypeLibraryName>.\Release/SiftGPU.tlb</TypeLibraryName> - <HeaderFileName> - </HeaderFileName> - </Midl> - <ClCompile> - <Optimization>MaxSpeed</Optimization> - <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> - <AdditionalIncludeDirectories>../../Include/;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>NDEBUG;SIFTGPU_EXPORTS;WIN32;_WINDOWS;_USRDLL;SIFTGPU_DLL;DLL_EXPORT;_CRT_SECURE_NO_DEPRECATE;CUDA_SIFTGPU_ENABLED;SERVER_SIFTGPU_ENABLED;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <IgnoreStandardIncludePath>false</IgnoreStandardIncludePath> - <StringPooling>true</StringPooling> - <RuntimeLibrary>MultiThreaded</RuntimeLibrary> - <FunctionLevelLinking>true</FunctionLevelLinking> - <PrecompiledHeaderOutputFile>$(IntDir)/SIFTGPU.pch</PrecompiledHeaderOutputFile> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <ObjectFileName>$(IntDir)</ObjectFileName> - <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName> - <WarningLevel>Level3</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <Culture>0x0409</Culture> - </ResourceCompile> - <Link> - <AdditionalDependencies>opengl32.lib;glu32.lib;winmm.lib;cuda.lib;cudart.lib;glew64.lib;glew64s.lib;%(AdditionalDependencies)</AdditionalDependencies> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - <AdditionalLibraryDirectories>../../lib/;$(CUDA_LIB_PATH)/../x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> - <ModuleDefinitionFile>SiftGPU.def</ModuleDefinitionFile> - <ProgramDatabaseFile>$(IntDir)/SIFTGPU.pdb</ProgramDatabaseFile> - <ImportLibrary>../../lib/SIFTGPU.lib</ImportLibrary> - <TargetMachine>MachineX64</TargetMachine> - </Link> - <Bscmake> - <SuppressStartupBanner>true</SuppressStartupBanner> - <OutputFile>.\Release/SiftGPU.bsc</OutputFile> - </Bscmake> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - <Midl> - <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <MkTypLibCompatible>true</MkTypLibCompatible> - <SuppressStartupBanner>true</SuppressStartupBanner> - <TargetEnvironment>X64</TargetEnvironment> - <TypeLibraryName>.\Debug/SiftGPU.tlb</TypeLibraryName> - <HeaderFileName> - </HeaderFileName> - </Midl> - <ClCompile> - <Optimization>Disabled</Optimization> - <AdditionalIncludeDirectories>../../Include/;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>_DEBUG;DLL_EXPORT;WIN32;_WINDOWS;_USRDLL;SIFTGPU_DLL;_CRT_SECURE_NO_DEPRECATE;CUDA_SIFTGPU_ENABLED;SERVER_SIFTGPU_ENABLED;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <IgnoreStandardIncludePath>false</IgnoreStandardIncludePath> - <MinimalRebuild>true</MinimalRebuild> - <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> - <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> - <PrecompiledHeaderOutputFile>$(IntDir)/SIFTGPU.pch</PrecompiledHeaderOutputFile> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <ObjectFileName>$(IntDir)</ObjectFileName> - <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName> - <WarningLevel>Level3</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <Culture>0x0409</Culture> - </ResourceCompile> - <Link> - <AdditionalDependencies>opengl32.lib;glu32.lib;winmm.lib;cuda.lib;cudart.lib;glew64.lib;glew64s.lib;%(AdditionalDependencies)</AdditionalDependencies> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - <AdditionalLibraryDirectories>../../lib/;$(CUDA_LIB_PATH)/../x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> - <ModuleDefinitionFile>SiftGPU.def</ModuleDefinitionFile> - <GenerateDebugInformation>true</GenerateDebugInformation> - <ProgramDatabaseFile>$(IntDir)/SIFTGPU.pdb</ProgramDatabaseFile> - <ImportLibrary>../../lib/SIFTGPU_d.lib</ImportLibrary> - <TargetMachine>MachineX64</TargetMachine> - </Link> - <Bscmake> - <SuppressStartupBanner>true</SuppressStartupBanner> - <OutputFile>.\Debug/SiftGPU.bsc</OutputFile> - </Bscmake> - </ItemDefinitionGroup> - <ItemGroup> - <ClCompile Include="..\..\src\SiftGPU\CuTexImage.cpp"> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(CUDA_INC_PATH)\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(CUDA_INC_PATH)\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(CUDA_INC_PATH)\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(CUDA_INC_PATH)\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - </ClCompile> - <ClCompile Include="..\..\src\SiftGPU\FrameBufferObject.cpp"> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> - <ClCompile Include="..\..\src\SiftGPU\GlobalUtil.cpp"> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> - <ClCompile Include="..\..\src\SiftGPU\GLTexImage.cpp"> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> - <ClCompile Include="..\..\src\SiftGPU\ProgramGLSL.cpp"> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> - <ClCompile Include="..\..\src\SiftGPU\ProgramGPU.cpp"> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> - <ClCompile Include="..\..\src\SiftGPU\PyramidCU.cpp"> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(CUDA_INC_PATH)\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(CUDA_INC_PATH)\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(CUDA_INC_PATH)\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(CUDA_INC_PATH)\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - </ClCompile> - <ClCompile Include="..\..\src\SiftGPU\PyramidGL.cpp" /> - <ClCompile Include="..\..\src\ServerSiftGPU\ServerSiftGPU.cpp" /> - <ClCompile Include="..\..\src\SiftGPU\ShaderMan.cpp"> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> - <ClCompile Include="..\..\src\SiftGPU\SiftGPU.cpp"> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> - <ClCompile Include="..\..\src\SiftGPU\SiftMatch.cpp" /> - <ClCompile Include="..\..\src\SiftGPU\SiftMatchCU.cpp" /> - <ClCompile Include="..\..\src\SiftGPU\SiftPyramid.cpp" /> - </ItemGroup> - <ItemGroup> - <CustomBuild Include="..\..\src\SiftGPU\ProgramCU.cu"> - <FileType>Document</FileType> - <Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(CUDA_BIN_PATH)\nvcc.exe" -ccbin "$(VCInstallDir)\bin" -c -DWIN32 -D_CONSOLE -D_MBCS -DCUDA_SIFTGPU_ENABLED -Xcompiler /EHsc,/W3,/nologo,/O2,/Zi,/MTd -m 64 -I "$(CUDA_INC_PATH)" -I./ -I../../Include -o $(IntDir)\$(InputName).obj %(FullPath)</Command> - <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(IntDir)\$(InputName).obj</Outputs> - <Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(CUDA_BIN_PATH)\nvcc.exe" -ccbin "$(VCInstallDir)\bin" -c -DWIN32 -D_CONSOLE -D_MBCS -DCUDA_SIFTGPU_ENABLED -Xcompiler /EHsc,/W3,/nologo,/O2,/MT -m 64 -I "$(CUDA_INC_PATH)" -I./ -I../../Include -o $(IntDir)\$(InputName).obj %(FullPath)</Command> - <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(IntDir)\$(InputName).obj</Outputs> - <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(CUDA_BIN_PATH)\nvcc.exe" -ccbin "$(VCInstallDir)\bin" -c -DWIN32 -D_CONSOLE -D_MBCS -DCUDA_SIFTGPU_ENABLED -Xcompiler /EHsc,/W3,/nologo,/O2,/Zi,/MT -m 32 -I "$(CUDA_INC_PATH)" -I./ -I../../Include -o $(IntDir)\$(InputName).obj %(FullPath)</Command> - <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)\$(InputName).obj</Outputs> - <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(CUDA_BIN_PATH)\nvcc.exe" -ccbin "$(VCInstallDir)\bin" -c -DWIN32 -D_CONSOLE -D_MBCS -DCUDA_SIFTGPU_ENABLED -Xcompiler /EHsc,/W3,/nologo,/O2,/Zi,/MTd -m 32 -I "$(CUDA_INC_PATH)" -I./ -I../../Include -o $(IntDir)\$(InputName).obj %(FullPath)</Command> - <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)\$(InputName).obj</Outputs> - </CustomBuild> - <None Include="SiftGPU.def" /> - </ItemGroup> - <ItemGroup> - <ClInclude Include="..\..\src\SiftGPU\CuTexImage.h" /> - <ClInclude Include="..\..\src\SiftGPU\FrameBufferObject.h" /> - <ClInclude Include="..\..\src\SiftGPU\GlobalUtil.h" /> - <ClInclude Include="..\..\src\SiftGPU\GLTexImage.h" /> - <ClInclude Include="..\..\src\SiftGPU\ProgramCU.h" /> - <ClInclude Include="..\..\src\SiftGPU\ProgramGLSL.h" /> - <ClInclude Include="..\..\src\SiftGPU\ProgramGPU.h" /> - <ClInclude Include="..\..\src\SiftGPU\PyramidCU.h" /> - <ClInclude Include="..\..\src\SiftGPU\PyramidGL.h" /> - <ClInclude Include="..\..\src\ServerSiftGPU\ServerSiftGPU.h" /> - <ClInclude Include="..\..\src\SiftGPU\ShaderMan.h" /> - <ClInclude Include="..\..\src\SiftGPU\SiftGPU.h" /> - <ClInclude Include="..\..\src\SiftGPU\SiftMatch.h" /> - <ClInclude Include="..\..\src\SiftGPU\SiftMatchCU.h" /> - <ClInclude Include="..\..\src\SiftGPU\SiftPyramid.h" /> - </ItemGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> - <ImportGroup Label="ExtensionTargets"> - </ImportGroup> -</Project> \ No newline at end of file diff --git a/3rdparty/SiftGPU/msvc/SiftGPU_CUDA_Enabled.sln b/3rdparty/SiftGPU/msvc/SiftGPU_CUDA_Enabled.sln deleted file mode 100644 index febda092..00000000 --- a/3rdparty/SiftGPU/msvc/SiftGPU_CUDA_Enabled.sln +++ /dev/null @@ -1,105 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestBase", "TestWin\TestBase.vcxproj", "{57496D03-A005-4650-B041-8E70DDA4A591}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestWin", "TestWin\TestWin.vcxproj", "{E1C4EAB3-1677-46F7-92BC-1A1F309B2027}" - ProjectSection(ProjectDependencies) = postProject - {9252E247-4FE2-4929-BD5D-C2FA16EFD656} = {9252E247-4FE2-4929-BD5D-C2FA16EFD656} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestWinGlut", "TestWin\TestWinGlut.vcxproj", "{B33F9B4E-BF99-442B-BDC5-3DAA4BCCA395}" - ProjectSection(ProjectDependencies) = postProject - {9252E247-4FE2-4929-BD5D-C2FA16EFD656} = {9252E247-4FE2-4929-BD5D-C2FA16EFD656} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SimpleSIFT", "TestWin\SimpleSIFT.vcxproj", "{D864A317-5A65-47B1-AA82-C04F99C4280F}" - ProjectSection(ProjectDependencies) = postProject - {9252E247-4FE2-4929-BD5D-C2FA16EFD656} = {9252E247-4FE2-4929-BD5D-C2FA16EFD656} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Speed", "TestWin\Speed.vcxproj", "{7FF72B39-F50E-4ED5-9A50-DCD60EF03604}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SiftGPU_CUDA_Enabled", "SiftGPU\SiftGPU_CUDA_Enabled.vcxproj", "{9252E247-4FE2-4929-BD5D-C2FA16EFD656}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SiftGPU_Server", "ServerSiftGPU\SiftGPU_Server.vcxproj", "{65C987E1-F62C-4461-9E2C-BE1E8A770C51}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MultiThreadSIFT", "TestWin\MultiThreadSIFT.vcxproj", "{8F087E0E-0B77-4CF7-BCD7-F899DB361128}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Debug|x64 = Debug|x64 - Release|Win32 = Release|Win32 - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {57496D03-A005-4650-B041-8E70DDA4A591}.Debug|Win32.ActiveCfg = Debug|Win32 - {57496D03-A005-4650-B041-8E70DDA4A591}.Debug|Win32.Build.0 = Debug|Win32 - {57496D03-A005-4650-B041-8E70DDA4A591}.Debug|x64.ActiveCfg = Debug|x64 - {57496D03-A005-4650-B041-8E70DDA4A591}.Debug|x64.Build.0 = Debug|x64 - {57496D03-A005-4650-B041-8E70DDA4A591}.Release|Win32.ActiveCfg = Release|Win32 - {57496D03-A005-4650-B041-8E70DDA4A591}.Release|Win32.Build.0 = Release|Win32 - {57496D03-A005-4650-B041-8E70DDA4A591}.Release|x64.ActiveCfg = Release|x64 - {57496D03-A005-4650-B041-8E70DDA4A591}.Release|x64.Build.0 = Release|x64 - {E1C4EAB3-1677-46F7-92BC-1A1F309B2027}.Debug|Win32.ActiveCfg = Debug|Win32 - {E1C4EAB3-1677-46F7-92BC-1A1F309B2027}.Debug|Win32.Build.0 = Debug|Win32 - {E1C4EAB3-1677-46F7-92BC-1A1F309B2027}.Debug|x64.ActiveCfg = Debug|x64 - {E1C4EAB3-1677-46F7-92BC-1A1F309B2027}.Debug|x64.Build.0 = Debug|x64 - {E1C4EAB3-1677-46F7-92BC-1A1F309B2027}.Release|Win32.ActiveCfg = Release|Win32 - {E1C4EAB3-1677-46F7-92BC-1A1F309B2027}.Release|Win32.Build.0 = Release|Win32 - {E1C4EAB3-1677-46F7-92BC-1A1F309B2027}.Release|x64.ActiveCfg = Release|x64 - {E1C4EAB3-1677-46F7-92BC-1A1F309B2027}.Release|x64.Build.0 = Release|x64 - {B33F9B4E-BF99-442B-BDC5-3DAA4BCCA395}.Debug|Win32.ActiveCfg = Debug|Win32 - {B33F9B4E-BF99-442B-BDC5-3DAA4BCCA395}.Debug|Win32.Build.0 = Debug|Win32 - {B33F9B4E-BF99-442B-BDC5-3DAA4BCCA395}.Debug|x64.ActiveCfg = Debug|x64 - {B33F9B4E-BF99-442B-BDC5-3DAA4BCCA395}.Debug|x64.Build.0 = Debug|x64 - {B33F9B4E-BF99-442B-BDC5-3DAA4BCCA395}.Release|Win32.ActiveCfg = Release|Win32 - {B33F9B4E-BF99-442B-BDC5-3DAA4BCCA395}.Release|Win32.Build.0 = Release|Win32 - {B33F9B4E-BF99-442B-BDC5-3DAA4BCCA395}.Release|x64.ActiveCfg = Release|x64 - {B33F9B4E-BF99-442B-BDC5-3DAA4BCCA395}.Release|x64.Build.0 = Release|x64 - {D864A317-5A65-47B1-AA82-C04F99C4280F}.Debug|Win32.ActiveCfg = Debug|Win32 - {D864A317-5A65-47B1-AA82-C04F99C4280F}.Debug|Win32.Build.0 = Debug|Win32 - {D864A317-5A65-47B1-AA82-C04F99C4280F}.Debug|x64.ActiveCfg = Debug|x64 - {D864A317-5A65-47B1-AA82-C04F99C4280F}.Debug|x64.Build.0 = Debug|x64 - {D864A317-5A65-47B1-AA82-C04F99C4280F}.Release|Win32.ActiveCfg = Release|Win32 - {D864A317-5A65-47B1-AA82-C04F99C4280F}.Release|Win32.Build.0 = Release|Win32 - {D864A317-5A65-47B1-AA82-C04F99C4280F}.Release|x64.ActiveCfg = Release|x64 - {D864A317-5A65-47B1-AA82-C04F99C4280F}.Release|x64.Build.0 = Release|x64 - {7FF72B39-F50E-4ED5-9A50-DCD60EF03604}.Debug|Win32.ActiveCfg = Debug|Win32 - {7FF72B39-F50E-4ED5-9A50-DCD60EF03604}.Debug|Win32.Build.0 = Debug|Win32 - {7FF72B39-F50E-4ED5-9A50-DCD60EF03604}.Debug|x64.ActiveCfg = Debug|x64 - {7FF72B39-F50E-4ED5-9A50-DCD60EF03604}.Debug|x64.Build.0 = Debug|x64 - {7FF72B39-F50E-4ED5-9A50-DCD60EF03604}.Release|Win32.ActiveCfg = Release|Win32 - {7FF72B39-F50E-4ED5-9A50-DCD60EF03604}.Release|Win32.Build.0 = Release|Win32 - {7FF72B39-F50E-4ED5-9A50-DCD60EF03604}.Release|x64.ActiveCfg = Release|x64 - {7FF72B39-F50E-4ED5-9A50-DCD60EF03604}.Release|x64.Build.0 = Release|x64 - {9252E247-4FE2-4929-BD5D-C2FA16EFD656}.Debug|Win32.ActiveCfg = Debug|Win32 - {9252E247-4FE2-4929-BD5D-C2FA16EFD656}.Debug|Win32.Build.0 = Debug|Win32 - {9252E247-4FE2-4929-BD5D-C2FA16EFD656}.Debug|x64.ActiveCfg = Debug|x64 - {9252E247-4FE2-4929-BD5D-C2FA16EFD656}.Debug|x64.Build.0 = Debug|x64 - {9252E247-4FE2-4929-BD5D-C2FA16EFD656}.Release|Win32.ActiveCfg = Release|Win32 - {9252E247-4FE2-4929-BD5D-C2FA16EFD656}.Release|Win32.Build.0 = Release|Win32 - {9252E247-4FE2-4929-BD5D-C2FA16EFD656}.Release|x64.ActiveCfg = Release|x64 - {9252E247-4FE2-4929-BD5D-C2FA16EFD656}.Release|x64.Build.0 = Release|x64 - {65C987E1-F62C-4461-9E2C-BE1E8A770C51}.Debug|Win32.ActiveCfg = Debug|Win32 - {65C987E1-F62C-4461-9E2C-BE1E8A770C51}.Debug|Win32.Build.0 = Debug|Win32 - {65C987E1-F62C-4461-9E2C-BE1E8A770C51}.Debug|x64.ActiveCfg = Debug|x64 - {65C987E1-F62C-4461-9E2C-BE1E8A770C51}.Debug|x64.Build.0 = Debug|x64 - {65C987E1-F62C-4461-9E2C-BE1E8A770C51}.Release|Win32.ActiveCfg = Release|Win32 - {65C987E1-F62C-4461-9E2C-BE1E8A770C51}.Release|Win32.Build.0 = Release|Win32 - {65C987E1-F62C-4461-9E2C-BE1E8A770C51}.Release|x64.ActiveCfg = Release|x64 - {65C987E1-F62C-4461-9E2C-BE1E8A770C51}.Release|x64.Build.0 = Release|x64 - {8F087E0E-0B77-4CF7-BCD7-F899DB361128}.Debug|Win32.ActiveCfg = Debug|Win32 - {8F087E0E-0B77-4CF7-BCD7-F899DB361128}.Debug|Win32.Build.0 = Debug|Win32 - {8F087E0E-0B77-4CF7-BCD7-F899DB361128}.Debug|x64.ActiveCfg = Debug|x64 - {8F087E0E-0B77-4CF7-BCD7-F899DB361128}.Debug|x64.Build.0 = Debug|x64 - {8F087E0E-0B77-4CF7-BCD7-F899DB361128}.Release|Win32.ActiveCfg = Release|Win32 - {8F087E0E-0B77-4CF7-BCD7-F899DB361128}.Release|Win32.Build.0 = Release|Win32 - {8F087E0E-0B77-4CF7-BCD7-F899DB361128}.Release|x64.ActiveCfg = Release|x64 - {8F087E0E-0B77-4CF7-BCD7-F899DB361128}.Release|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/3rdparty/SiftGPU/msvc/TestWin/MultiThreadSIFT.dsp b/3rdparty/SiftGPU/msvc/TestWin/MultiThreadSIFT.dsp deleted file mode 100644 index 0070f726..00000000 --- a/3rdparty/SiftGPU/msvc/TestWin/MultiThreadSIFT.dsp +++ /dev/null @@ -1,102 +0,0 @@ -# Microsoft Developer Studio Project File - Name="MultiThreadSIFT" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=MultiThreadSIFT - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "MultiThreadSIFT.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "MultiThreadSIFT.mak" CFG="MultiThreadSIFT - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "MultiThreadSIFT - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "MultiThreadSIFT - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "MultiThreadSIFT - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"../../bin/MultiThreadSIFT.exe" - -!ELSEIF "$(CFG)" == "MultiThreadSIFT - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../../bin/MultiThreadSIFT_d.exe" /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "MultiThreadSIFT - Win32 Release" -# Name "MultiThreadSIFT - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=..\..\src\TestWin\MultiThreadSIFT.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project diff --git a/3rdparty/SiftGPU/msvc/TestWin/MultiThreadSIFT.vcxproj b/3rdparty/SiftGPU/msvc/TestWin/MultiThreadSIFT.vcxproj deleted file mode 100644 index fe0c1b20..00000000 --- a/3rdparty/SiftGPU/msvc/TestWin/MultiThreadSIFT.vcxproj +++ /dev/null @@ -1,259 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup Label="ProjectConfigurations"> - <ProjectConfiguration Include="Debug|Win32"> - <Configuration>Debug</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Debug|x64"> - <Configuration>Debug</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|Win32"> - <Configuration>Release</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|x64"> - <Configuration>Release</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - </ItemGroup> - <PropertyGroup Label="Globals"> - <ProjectGuid>{8F087E0E-0B77-4CF7-BCD7-F899DB361128}</ProjectGuid> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseOfMfc>false</UseOfMfc> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseOfMfc>false</UseOfMfc> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseOfMfc>false</UseOfMfc> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseOfMfc>false</UseOfMfc> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> - <ImportGroup Label="ExtensionSettings"> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> - </ImportGroup> - <PropertyGroup Label="UserMacros" /> - <PropertyGroup> - <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\bin\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(ProjectName)_$(Configuration)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\bin\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(ProjectName)_$(Configuration)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\..\bin\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectName)_$(Configuration)_$(Platform)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\..\bin\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ProjectName)_$(Configuration)_$(Platform)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental> - <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(ProjectName)_d</TargetName> - <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectName)_d</TargetName> - </PropertyGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Midl> - <TypeLibraryName>.\Debug/MultiThreadSIFT.tlb</TypeLibraryName> - <HeaderFileName> - </HeaderFileName> - </Midl> - <ClCompile> - <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <MinimalRebuild>true</MinimalRebuild> - <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> - <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> - <PrecompiledHeaderOutputFile>.\Debug/MultiThreadSIFT.pch</PrecompiledHeaderOutputFile> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <ObjectFileName>$(IntDir)</ObjectFileName> - <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName> - <WarningLevel>Level3</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - <DebugInformationFormat>EditAndContinue</DebugInformationFormat> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <Culture>0x0409</Culture> - </ResourceCompile> - <Link> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - <GenerateDebugInformation>true</GenerateDebugInformation> - <ProgramDatabaseFile>.\Debug/MultiThreadSIFT_d.pdb</ProgramDatabaseFile> - <SubSystem>Console</SubSystem> - <TargetMachine>MachineX86</TargetMachine> - </Link> - <Bscmake> - <SuppressStartupBanner>true</SuppressStartupBanner> - <OutputFile>.\Debug/MultiThreadSIFT.bsc</OutputFile> - </Bscmake> - <ProjectReference> - <LinkLibraryDependencies>false</LinkLibraryDependencies> - </ProjectReference> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Midl> - <TypeLibraryName>.\Release/MultiThreadSIFT.tlb</TypeLibraryName> - <HeaderFileName> - </HeaderFileName> - </Midl> - <ClCompile> - <Optimization>MaxSpeed</Optimization> - <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <StringPooling>true</StringPooling> - <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> - <FunctionLevelLinking>true</FunctionLevelLinking> - <PrecompiledHeaderOutputFile>.\Release/MultiThreadSIFT.pch</PrecompiledHeaderOutputFile> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <ObjectFileName>$(IntDir)</ObjectFileName> - <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName> - <WarningLevel>Level3</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <Culture>0x0409</Culture> - </ResourceCompile> - <Link> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - <ProgramDatabaseFile>.\Release/MultiThreadSIFT.pdb</ProgramDatabaseFile> - <SubSystem>Console</SubSystem> - <TargetMachine>MachineX86</TargetMachine> - </Link> - <Bscmake> - <SuppressStartupBanner>true</SuppressStartupBanner> - <OutputFile>.\Release/MultiThreadSIFT.bsc</OutputFile> - </Bscmake> - <ProjectReference> - <LinkLibraryDependencies>false</LinkLibraryDependencies> - </ProjectReference> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - <Midl> - <TargetEnvironment>X64</TargetEnvironment> - <TypeLibraryName>.\Debug/MultiThreadSIFT.tlb</TypeLibraryName> - <HeaderFileName> - </HeaderFileName> - </Midl> - <ClCompile> - <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <MinimalRebuild>true</MinimalRebuild> - <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> - <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> - <PrecompiledHeaderOutputFile>.\Debug/MultiThreadSIFT.pch</PrecompiledHeaderOutputFile> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <ObjectFileName>$(IntDir)</ObjectFileName> - <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName> - <WarningLevel>Level3</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <Culture>0x0409</Culture> - </ResourceCompile> - <Link> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - <GenerateDebugInformation>true</GenerateDebugInformation> - <ProgramDatabaseFile>.\Debug/MultiThreadSIFT_d.pdb</ProgramDatabaseFile> - <SubSystem>Console</SubSystem> - <TargetMachine>MachineX64</TargetMachine> - </Link> - <Bscmake> - <SuppressStartupBanner>true</SuppressStartupBanner> - <OutputFile>.\Debug/MultiThreadSIFT.bsc</OutputFile> - </Bscmake> - <ProjectReference> - <LinkLibraryDependencies>false</LinkLibraryDependencies> - </ProjectReference> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> - <Midl> - <TargetEnvironment>X64</TargetEnvironment> - <TypeLibraryName>.\Release/MultiThreadSIFT.tlb</TypeLibraryName> - <HeaderFileName> - </HeaderFileName> - </Midl> - <ClCompile> - <Optimization>MaxSpeed</Optimization> - <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <StringPooling>true</StringPooling> - <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> - <FunctionLevelLinking>true</FunctionLevelLinking> - <PrecompiledHeaderOutputFile>.\Release/MultiThreadSIFT.pch</PrecompiledHeaderOutputFile> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <ObjectFileName>$(IntDir)</ObjectFileName> - <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName> - <WarningLevel>Level3</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <Culture>0x0409</Culture> - </ResourceCompile> - <Link> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - <ProgramDatabaseFile>.\Release/MultiThreadSIFT.pdb</ProgramDatabaseFile> - <SubSystem>Console</SubSystem> - <TargetMachine>MachineX64</TargetMachine> - </Link> - <Bscmake> - <SuppressStartupBanner>true</SuppressStartupBanner> - <OutputFile>.\Release/MultiThreadSIFT.bsc</OutputFile> - </Bscmake> - <ProjectReference> - <LinkLibraryDependencies>false</LinkLibraryDependencies> - </ProjectReference> - </ItemDefinitionGroup> - <ItemGroup> - <ClCompile Include="..\..\src\TestWin\MultiThreadSIFT.cpp"> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> - </ItemGroup> - <ItemGroup> - <ProjectReference Include="..\SiftGPU\SiftGPU_CUDA_Enabled.vcxproj"> - <Project>{9252e247-4fe2-4929-bd5d-c2fa16efd656}</Project> - <ReferenceOutputAssembly>false</ReferenceOutputAssembly> - </ProjectReference> - </ItemGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> - <ImportGroup Label="ExtensionTargets"> - </ImportGroup> -</Project> \ No newline at end of file diff --git a/3rdparty/SiftGPU/msvc/TestWin/SimpleSIFT.dsp b/3rdparty/SiftGPU/msvc/TestWin/SimpleSIFT.dsp deleted file mode 100644 index 512e9ff3..00000000 --- a/3rdparty/SiftGPU/msvc/TestWin/SimpleSIFT.dsp +++ /dev/null @@ -1,102 +0,0 @@ -# Microsoft Developer Studio Project File - Name="SimpleSIFT" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=SimpleSIFT - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "SimpleSIFT.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "SimpleSIFT.mak" CFG="SimpleSIFT - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "SimpleSIFT - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "SimpleSIFT - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "SimpleSIFT - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 kernel32.lib /nologo /subsystem:console /machine:I386 /out:"../../bin/SimpleSIFT.exe" - -!ELSEIF "$(CFG)" == "SimpleSIFT - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../../bin/SimpleSIFT_d.exe" /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "SimpleSIFT - Win32 Release" -# Name "SimpleSIFT - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=..\..\src\TestWin\SimpleSIFT.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project diff --git a/3rdparty/SiftGPU/msvc/TestWin/SimpleSIFT.vcxproj b/3rdparty/SiftGPU/msvc/TestWin/SimpleSIFT.vcxproj deleted file mode 100644 index 5648fa6f..00000000 --- a/3rdparty/SiftGPU/msvc/TestWin/SimpleSIFT.vcxproj +++ /dev/null @@ -1,259 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup Label="ProjectConfigurations"> - <ProjectConfiguration Include="Debug|Win32"> - <Configuration>Debug</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Debug|x64"> - <Configuration>Debug</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|Win32"> - <Configuration>Release</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|x64"> - <Configuration>Release</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - </ItemGroup> - <PropertyGroup Label="Globals"> - <ProjectGuid>{D864A317-5A65-47B1-AA82-C04F99C4280F}</ProjectGuid> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseOfMfc>false</UseOfMfc> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseOfMfc>false</UseOfMfc> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseOfMfc>false</UseOfMfc> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseOfMfc>false</UseOfMfc> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> - <ImportGroup Label="ExtensionSettings"> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> - </ImportGroup> - <PropertyGroup Label="UserMacros" /> - <PropertyGroup> - <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\bin\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(ProjectName)_$(Configuration)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\..\bin\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectName)_$(Configuration)_$(Platform)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\bin\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(ProjectName)_$(Configuration)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\..\bin\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ProjectName)_$(Configuration)_$(Platform)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental> - <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(ProjectName)_d</TargetName> - <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectName)_d</TargetName> - </PropertyGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Midl> - <TypeLibraryName>.\Debug/SimpleSIFT.tlb</TypeLibraryName> - <HeaderFileName> - </HeaderFileName> - </Midl> - <ClCompile> - <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <MinimalRebuild>true</MinimalRebuild> - <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> - <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> - <PrecompiledHeaderOutputFile>.\Debug/SimpleSIFT.pch</PrecompiledHeaderOutputFile> - <AssemblerListingLocation>.\Debug/</AssemblerListingLocation> - <ObjectFileName>.\Debug/</ObjectFileName> - <ProgramDataBaseFileName>.\Debug/</ProgramDataBaseFileName> - <WarningLevel>Level3</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - <DebugInformationFormat>EditAndContinue</DebugInformationFormat> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <Culture>0x0409</Culture> - </ResourceCompile> - <Link> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - <GenerateDebugInformation>true</GenerateDebugInformation> - <ProgramDatabaseFile>.\Debug/SimpleSIFT_d.pdb</ProgramDatabaseFile> - <SubSystem>Console</SubSystem> - <TargetMachine>MachineX86</TargetMachine> - </Link> - <Bscmake> - <SuppressStartupBanner>true</SuppressStartupBanner> - <OutputFile>.\Debug/SimpleSIFT.bsc</OutputFile> - </Bscmake> - <ProjectReference> - <LinkLibraryDependencies>false</LinkLibraryDependencies> - </ProjectReference> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - <Midl> - <TargetEnvironment>X64</TargetEnvironment> - <TypeLibraryName>.\Debug/SimpleSIFT.tlb</TypeLibraryName> - <HeaderFileName> - </HeaderFileName> - </Midl> - <ClCompile> - <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <MinimalRebuild>true</MinimalRebuild> - <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> - <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> - <PrecompiledHeaderOutputFile>.\Debug/SimpleSIFT.pch</PrecompiledHeaderOutputFile> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <ObjectFileName>$(IntDir)</ObjectFileName> - <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName> - <WarningLevel>Level3</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <Culture>0x0409</Culture> - </ResourceCompile> - <Link> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - <GenerateDebugInformation>true</GenerateDebugInformation> - <ProgramDatabaseFile>.\Debug/SimpleSIFT_d.pdb</ProgramDatabaseFile> - <SubSystem>Console</SubSystem> - <TargetMachine>MachineX64</TargetMachine> - </Link> - <Bscmake> - <SuppressStartupBanner>true</SuppressStartupBanner> - <OutputFile>.\Debug/SimpleSIFT.bsc</OutputFile> - </Bscmake> - <ProjectReference> - <LinkLibraryDependencies>false</LinkLibraryDependencies> - </ProjectReference> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Midl> - <TypeLibraryName>.\Release/SimpleSIFT.tlb</TypeLibraryName> - <HeaderFileName> - </HeaderFileName> - </Midl> - <ClCompile> - <Optimization>MaxSpeed</Optimization> - <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <StringPooling>true</StringPooling> - <RuntimeLibrary>MultiThreaded</RuntimeLibrary> - <FunctionLevelLinking>true</FunctionLevelLinking> - <PrecompiledHeaderOutputFile>.\Release/SimpleSIFT.pch</PrecompiledHeaderOutputFile> - <AssemblerListingLocation>.\Release/</AssemblerListingLocation> - <ObjectFileName>.\Release/</ObjectFileName> - <ProgramDataBaseFileName>.\Release/</ProgramDataBaseFileName> - <WarningLevel>Level3</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <Culture>0x0409</Culture> - </ResourceCompile> - <Link> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - <ProgramDatabaseFile>.\Release/SimpleSIFT.pdb</ProgramDatabaseFile> - <SubSystem>Console</SubSystem> - <TargetMachine>MachineX86</TargetMachine> - </Link> - <Bscmake> - <SuppressStartupBanner>true</SuppressStartupBanner> - <OutputFile>.\Release/SimpleSIFT.bsc</OutputFile> - </Bscmake> - <ProjectReference> - <LinkLibraryDependencies>false</LinkLibraryDependencies> - </ProjectReference> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> - <Midl> - <TargetEnvironment>X64</TargetEnvironment> - <TypeLibraryName>.\Release/SimpleSIFT.tlb</TypeLibraryName> - <HeaderFileName> - </HeaderFileName> - </Midl> - <ClCompile> - <Optimization>MaxSpeed</Optimization> - <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <StringPooling>true</StringPooling> - <RuntimeLibrary>MultiThreaded</RuntimeLibrary> - <FunctionLevelLinking>true</FunctionLevelLinking> - <PrecompiledHeaderOutputFile>.\Release/SimpleSIFT.pch</PrecompiledHeaderOutputFile> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <ObjectFileName>$(IntDir)</ObjectFileName> - <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName> - <WarningLevel>Level3</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <Culture>0x0409</Culture> - </ResourceCompile> - <Link> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - <ProgramDatabaseFile>.\Release/SimpleSIFT.pdb</ProgramDatabaseFile> - <SubSystem>Console</SubSystem> - <TargetMachine>MachineX64</TargetMachine> - </Link> - <Bscmake> - <SuppressStartupBanner>true</SuppressStartupBanner> - <OutputFile>.\Release/SimpleSIFT.bsc</OutputFile> - </Bscmake> - <ProjectReference> - <LinkLibraryDependencies>false</LinkLibraryDependencies> - </ProjectReference> - </ItemDefinitionGroup> - <ItemGroup> - <ClCompile Include="..\..\src\TestWin\SimpleSIFT.cpp"> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> - </ItemGroup> - <ItemGroup> - <ProjectReference Include="..\SiftGPU\SiftGPU.vcxproj"> - <Project>{594562d3-609a-47bc-b7e2-789c4e794cc3}</Project> - <ReferenceOutputAssembly>false</ReferenceOutputAssembly> - </ProjectReference> - </ItemGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> - <ImportGroup Label="ExtensionTargets"> - </ImportGroup> -</Project> \ No newline at end of file diff --git a/3rdparty/SiftGPU/msvc/TestWin/Speed.dsp b/3rdparty/SiftGPU/msvc/TestWin/Speed.dsp deleted file mode 100644 index 001d7f41..00000000 --- a/3rdparty/SiftGPU/msvc/TestWin/Speed.dsp +++ /dev/null @@ -1,102 +0,0 @@ -# Microsoft Developer Studio Project File - Name="Speed" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=Speed - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "Speed.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "Speed.mak" CFG="Speed - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "Speed - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "Speed - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "Speed - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib siftgpu.lib /nologo /subsystem:console /machine:I386 /out:"../../bin/speed.exe" /libpath:"../../lib" - -!ELSEIF "$(CFG)" == "Speed - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib siftgpu_d.lib /nologo /subsystem:console /debug /machine:I386 /out:"../../bin/speed_d.exe" /pdbtype:sept /libpath:"../../lib" - -!ENDIF - -# Begin Target - -# Name "Speed - Win32 Release" -# Name "Speed - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=..\..\src\TestWin\speed.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project diff --git a/3rdparty/SiftGPU/msvc/TestWin/Speed.vcxproj b/3rdparty/SiftGPU/msvc/TestWin/Speed.vcxproj deleted file mode 100644 index 725c3709..00000000 --- a/3rdparty/SiftGPU/msvc/TestWin/Speed.vcxproj +++ /dev/null @@ -1,272 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup Label="ProjectConfigurations"> - <ProjectConfiguration Include="Debug|Win32"> - <Configuration>Debug</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Debug|x64"> - <Configuration>Debug</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|Win32"> - <Configuration>Release</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|x64"> - <Configuration>Release</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - </ItemGroup> - <PropertyGroup Label="Globals"> - <ProjectGuid>{7FF72B39-F50E-4ED5-9A50-DCD60EF03604}</ProjectGuid> - <RootNamespace>Speed</RootNamespace> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseOfMfc>false</UseOfMfc> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseOfMfc>false</UseOfMfc> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseOfMfc>false</UseOfMfc> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseOfMfc>false</UseOfMfc> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> - <ImportGroup Label="ExtensionSettings"> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> - </ImportGroup> - <PropertyGroup Label="UserMacros" /> - <PropertyGroup> - <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\bin\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(ProjectName)_$(Configuration)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\..\bin\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ProjectName)_$(Configuration)_$(Platform)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\bin\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(ProjectName)_$(Configuration)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\..\bin\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectName)_$(Configuration)_$(Platform)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental> - <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(ProjectName)_d</TargetName> - <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectName)_d</TargetName> - </PropertyGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Midl> - <TypeLibraryName>.\Release/Speed.tlb</TypeLibraryName> - <HeaderFileName> - </HeaderFileName> - </Midl> - <ClCompile> - <Optimization>MaxSpeed</Optimization> - <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <StringPooling>true</StringPooling> - <RuntimeLibrary>MultiThreaded</RuntimeLibrary> - <FunctionLevelLinking>true</FunctionLevelLinking> - <PrecompiledHeaderOutputFile>.\Release/Speed.pch</PrecompiledHeaderOutputFile> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <ObjectFileName>$(IntDir)</ObjectFileName> - <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName> - <WarningLevel>Level3</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <Culture>0x0409</Culture> - </ResourceCompile> - <Link> - <AdditionalDependencies>siftgpu.lib;%(AdditionalDependencies)</AdditionalDependencies> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - <AdditionalLibraryDirectories>../../lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> - <ProgramDatabaseFile>.\Release/speed.pdb</ProgramDatabaseFile> - <SubSystem>Console</SubSystem> - <TargetMachine>MachineX86</TargetMachine> - </Link> - <Bscmake> - <SuppressStartupBanner>true</SuppressStartupBanner> - <OutputFile>.\Release/Speed.bsc</OutputFile> - </Bscmake> - <ProjectReference> - <LinkLibraryDependencies>false</LinkLibraryDependencies> - </ProjectReference> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> - <Midl> - <TargetEnvironment>X64</TargetEnvironment> - <TypeLibraryName>.\Release/Speed.tlb</TypeLibraryName> - <HeaderFileName> - </HeaderFileName> - </Midl> - <ClCompile> - <Optimization>MaxSpeed</Optimization> - <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <StringPooling>true</StringPooling> - <RuntimeLibrary>MultiThreaded</RuntimeLibrary> - <FunctionLevelLinking>true</FunctionLevelLinking> - <PrecompiledHeaderOutputFile>.\Release/Speed.pch</PrecompiledHeaderOutputFile> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <ObjectFileName>$(IntDir)</ObjectFileName> - <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName> - <WarningLevel>Level3</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <Culture>0x0409</Culture> - </ResourceCompile> - <Link> - <AdditionalDependencies>siftgpu.lib;%(AdditionalDependencies)</AdditionalDependencies> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - <AdditionalLibraryDirectories>../../lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> - <ProgramDatabaseFile>.\Release/speed.pdb</ProgramDatabaseFile> - <SubSystem>Console</SubSystem> - <TargetMachine>MachineX64</TargetMachine> - </Link> - <Bscmake> - <SuppressStartupBanner>true</SuppressStartupBanner> - <OutputFile>.\Release/Speed.bsc</OutputFile> - </Bscmake> - <ProjectReference> - <LinkLibraryDependencies>false</LinkLibraryDependencies> - </ProjectReference> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Midl> - <TypeLibraryName>.\Debug/Speed.tlb</TypeLibraryName> - <HeaderFileName> - </HeaderFileName> - </Midl> - <ClCompile> - <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <MinimalRebuild>true</MinimalRebuild> - <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> - <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> - <PrecompiledHeaderOutputFile>.\Debug/Speed.pch</PrecompiledHeaderOutputFile> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <ObjectFileName>$(IntDir)</ObjectFileName> - <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName> - <WarningLevel>Level3</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - <DebugInformationFormat>EditAndContinue</DebugInformationFormat> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <Culture>0x0409</Culture> - </ResourceCompile> - <Link> - <AdditionalDependencies>siftgpu_d.lib;%(AdditionalDependencies)</AdditionalDependencies> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - <AdditionalLibraryDirectories>../../lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> - <GenerateDebugInformation>true</GenerateDebugInformation> - <ProgramDatabaseFile>.\Debug/speed_d.pdb</ProgramDatabaseFile> - <SubSystem>Console</SubSystem> - <TargetMachine>MachineX86</TargetMachine> - </Link> - <Bscmake> - <SuppressStartupBanner>true</SuppressStartupBanner> - <OutputFile>.\Debug/Speed.bsc</OutputFile> - </Bscmake> - <ProjectReference> - <LinkLibraryDependencies>false</LinkLibraryDependencies> - </ProjectReference> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - <Midl> - <TargetEnvironment>X64</TargetEnvironment> - <TypeLibraryName>.\Debug/Speed.tlb</TypeLibraryName> - <HeaderFileName> - </HeaderFileName> - </Midl> - <ClCompile> - <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <MinimalRebuild>true</MinimalRebuild> - <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> - <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> - <PrecompiledHeaderOutputFile>.\Debug/Speed.pch</PrecompiledHeaderOutputFile> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <ObjectFileName>$(IntDir)</ObjectFileName> - <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName> - <WarningLevel>Level3</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <Culture>0x0409</Culture> - </ResourceCompile> - <Link> - <AdditionalDependencies>siftgpu_d.lib;%(AdditionalDependencies)</AdditionalDependencies> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - <AdditionalLibraryDirectories>../../lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> - <GenerateDebugInformation>true</GenerateDebugInformation> - <ProgramDatabaseFile>.\Debug/speed_d.pdb</ProgramDatabaseFile> - <SubSystem>Console</SubSystem> - <TargetMachine>MachineX64</TargetMachine> - </Link> - <Bscmake> - <SuppressStartupBanner>true</SuppressStartupBanner> - <OutputFile>.\Debug/Speed.bsc</OutputFile> - </Bscmake> - <ProjectReference> - <LinkLibraryDependencies>false</LinkLibraryDependencies> - </ProjectReference> - </ItemDefinitionGroup> - <ItemGroup> - <ClCompile Include="..\..\src\TestWin\speed.cpp"> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> - </ItemGroup> - <ItemGroup> - <ProjectReference Include="..\SiftGPU\SiftGPU.vcxproj"> - <Project>{594562d3-609a-47bc-b7e2-789c4e794cc3}</Project> - <ReferenceOutputAssembly>false</ReferenceOutputAssembly> - </ProjectReference> - <ProjectReference Include="..\SiftGPU\SiftGPU_CUDA_Enabled.vcxproj"> - <Project>{9252e247-4fe2-4929-bd5d-c2fa16efd656}</Project> - <ReferenceOutputAssembly>false</ReferenceOutputAssembly> - </ProjectReference> - </ItemGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> - <ImportGroup Label="ExtensionTargets"> - </ImportGroup> -</Project> \ No newline at end of file diff --git a/3rdparty/SiftGPU/msvc/TestWin/TestBase.dsp b/3rdparty/SiftGPU/msvc/TestWin/TestBase.dsp deleted file mode 100644 index 80d004e0..00000000 --- a/3rdparty/SiftGPU/msvc/TestWin/TestBase.dsp +++ /dev/null @@ -1,104 +0,0 @@ -# Microsoft Developer Studio Project File - Name="TestBase" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=TestBase - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "TestBase.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "TestBase.mak" CFG="TestBase - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "TestBase - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "TestBase - Win32 Debug" (based on "Win32 (x86) Static Library") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "TestBase - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c -# ADD CPP /nologo /W3 /GX /O2 /I "../../Include/" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LIB32=link.exe -lib -# ADD BASE LIB32 /nologo -# ADD LIB32 /nologo /out:"..\..\lib\TestBase.lib" - -!ELSEIF "$(CFG)" == "TestBase - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c -# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "../../Include/" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LIB32=link.exe -lib -# ADD BASE LIB32 /nologo -# ADD LIB32 /nologo /out:"..\..\lib\TestBase_d.lib" - -!ENDIF - -# Begin Target - -# Name "TestBase - Win32 Release" -# Name "TestBase - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=..\..\src\TestWin\BasicTestWin.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=..\..\src\TestWin\BasicTestWin.h -# End Source File -# Begin Source File - -SOURCE=..\..\src\TestWin\GLTransform.h -# End Source File -# End Group -# End Target -# End Project diff --git a/3rdparty/SiftGPU/msvc/TestWin/TestBase.vcxproj b/3rdparty/SiftGPU/msvc/TestWin/TestBase.vcxproj deleted file mode 100644 index d8ca6db5..00000000 --- a/3rdparty/SiftGPU/msvc/TestWin/TestBase.vcxproj +++ /dev/null @@ -1,230 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup Label="ProjectConfigurations"> - <ProjectConfiguration Include="Debug|Win32"> - <Configuration>Debug</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Debug|x64"> - <Configuration>Debug</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|Win32"> - <Configuration>Release</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|x64"> - <Configuration>Release</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - </ItemGroup> - <PropertyGroup Label="Globals"> - <ProjectGuid>{57496D03-A005-4650-B041-8E70DDA4A591}</ProjectGuid> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> - <ConfigurationType>StaticLibrary</ConfigurationType> - <UseOfMfc>false</UseOfMfc> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> - <ConfigurationType>StaticLibrary</ConfigurationType> - <UseOfMfc>false</UseOfMfc> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> - <ConfigurationType>StaticLibrary</ConfigurationType> - <UseOfMfc>false</UseOfMfc> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> - <ConfigurationType>StaticLibrary</ConfigurationType> - <UseOfMfc>false</UseOfMfc> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> - <ImportGroup Label="ExtensionSettings"> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> - </ImportGroup> - <PropertyGroup Label="UserMacros" /> - <PropertyGroup> - <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\lib\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(ProjectName)_$(Configuration)\</IntDir> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\..\lib\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectName)_$(Configuration)_$(Platform)\</IntDir> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\lib\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(ProjectName)_$(Configuration)\</IntDir> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\..\lib\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ProjectName)_$(Configuration)_$(Platform)\</IntDir> - <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(ProjectName)_d</TargetName> - <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectName)_$(Platform)_d</TargetName> - <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ProjectName)_$(Platform)</TargetName> - </PropertyGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <ClCompile> - <Optimization>Disabled</Optimization> - <AdditionalIncludeDirectories>../../Include/;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <MinimalRebuild>true</MinimalRebuild> - <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> - <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> - <PrecompiledHeaderOutputFile>.\Debug/TestBase.pch</PrecompiledHeaderOutputFile> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <ObjectFileName>$(IntDir)</ObjectFileName> - <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName> - <WarningLevel>Level3</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - <DebugInformationFormat>EditAndContinue</DebugInformationFormat> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <Culture>0x0409</Culture> - </ResourceCompile> - <Lib> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - </Lib> - <Bscmake> - <SuppressStartupBanner>true</SuppressStartupBanner> - <OutputFile>.\Debug/TestBase.bsc</OutputFile> - </Bscmake> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - <Midl> - <TargetEnvironment>X64</TargetEnvironment> - </Midl> - <ClCompile> - <Optimization>Disabled</Optimization> - <AdditionalIncludeDirectories>../../Include/;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <MinimalRebuild>true</MinimalRebuild> - <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> - <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> - <PrecompiledHeaderOutputFile>.\Debug/TestBase.pch</PrecompiledHeaderOutputFile> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <ObjectFileName>$(IntDir)</ObjectFileName> - <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName> - <WarningLevel>Level3</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <Culture>0x0409</Culture> - </ResourceCompile> - <Lib> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - </Lib> - <Bscmake> - <SuppressStartupBanner>true</SuppressStartupBanner> - <OutputFile>.\Debug/TestBase.bsc</OutputFile> - </Bscmake> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <ClCompile> - <Optimization>MaxSpeed</Optimization> - <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> - <AdditionalIncludeDirectories>../../Include/;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <StringPooling>true</StringPooling> - <RuntimeLibrary>MultiThreaded</RuntimeLibrary> - <FunctionLevelLinking>true</FunctionLevelLinking> - <PrecompiledHeaderOutputFile>.\Release/TestBase.pch</PrecompiledHeaderOutputFile> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <ObjectFileName>$(IntDir)</ObjectFileName> - <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName> - <WarningLevel>Level3</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <Culture>0x0409</Culture> - </ResourceCompile> - <Lib> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - </Lib> - <Bscmake> - <SuppressStartupBanner>true</SuppressStartupBanner> - <OutputFile>.\Release/TestBase.bsc</OutputFile> - </Bscmake> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> - <Midl> - <TargetEnvironment>X64</TargetEnvironment> - </Midl> - <ClCompile> - <Optimization>MaxSpeed</Optimization> - <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> - <AdditionalIncludeDirectories>../../Include/;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <StringPooling>true</StringPooling> - <RuntimeLibrary>MultiThreaded</RuntimeLibrary> - <FunctionLevelLinking>true</FunctionLevelLinking> - <PrecompiledHeaderOutputFile>.\Release/TestBase.pch</PrecompiledHeaderOutputFile> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <ObjectFileName>$(IntDir)</ObjectFileName> - <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName> - <WarningLevel>Level3</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <Culture>0x0409</Culture> - </ResourceCompile> - <Lib> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - </Lib> - <Bscmake> - <SuppressStartupBanner>true</SuppressStartupBanner> - <OutputFile>.\Release/TestBase.bsc</OutputFile> - </Bscmake> - </ItemDefinitionGroup> - <ItemGroup> - <ClCompile Include="..\..\src\TestWin\BasicTestWin.cpp"> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> - </ItemGroup> - <ItemGroup> - <ClInclude Include="..\..\src\TestWin\BasicTestWin.h" /> - <ClInclude Include="..\..\src\TestWin\GLTransform.h" /> - </ItemGroup> - <ItemGroup> - <ProjectReference Include="..\SiftGPU\SiftGPU.vcxproj"> - <Project>{594562d3-609a-47bc-b7e2-789c4e794cc3}</Project> - <ReferenceOutputAssembly>false</ReferenceOutputAssembly> - </ProjectReference> - <ProjectReference Include="..\SiftGPU\SiftGPU_CUDA_Enabled.vcxproj"> - <Project>{9252e247-4fe2-4929-bd5d-c2fa16efd656}</Project> - <ReferenceOutputAssembly>false</ReferenceOutputAssembly> - </ProjectReference> - </ItemGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> - <ImportGroup Label="ExtensionTargets"> - </ImportGroup> -</Project> \ No newline at end of file diff --git a/3rdparty/SiftGPU/msvc/TestWin/TestWin.dsp b/3rdparty/SiftGPU/msvc/TestWin/TestWin.dsp deleted file mode 100644 index 0aa9998f..00000000 --- a/3rdparty/SiftGPU/msvc/TestWin/TestWin.dsp +++ /dev/null @@ -1,115 +0,0 @@ -# Microsoft Developer Studio Project File - Name="TestWin" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Application" 0x0101 - -CFG=TestWin - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "TestWin.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "TestWin.mak" CFG="TestWin - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "TestWin - Win32 Release" (based on "Win32 (x86) Application") -!MESSAGE "TestWin - Win32 Debug" (based on "Win32 (x86) Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -MTL=midl.exe -RSC=rc.exe - -!IF "$(CFG)" == "TestWin - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /Yu"stdafx.h" /FD /c -# ADD CPP /nologo /W3 /GX /O2 /I "../../Include/" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c -# SUBTRACT CPP /YX /Yc /Yu -# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib opengl32.lib siftgpu.lib testbase.lib /nologo /subsystem:console /machine:I386 /out:"../../bin/TestWin.exe" /libpath:"../../lib/" -# SUBTRACT LINK32 /pdb:none - -!ELSEIF "$(CFG)" == "TestWin - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c -# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "../../Include/" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c -# SUBTRACT CPP /YX /Yc /Yu -# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib opengl32.lib siftgpu_d.lib testbase_d.lib /nologo /subsystem:console /debug /machine:I386 /out:"../../bin/TestWin_d.exe" /pdbtype:sept /libpath:"../../lib/" -# SUBTRACT LINK32 /pdb:none - -!ENDIF - -# Begin Target - -# Name "TestWin - Win32 Release" -# Name "TestWin - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=..\..\src\TestWin\GLTestWnd.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=..\..\src\TestWin\GLTestWnd.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project diff --git a/3rdparty/SiftGPU/msvc/TestWin/TestWin.vcxproj b/3rdparty/SiftGPU/msvc/TestWin/TestWin.vcxproj deleted file mode 100644 index c901972d..00000000 --- a/3rdparty/SiftGPU/msvc/TestWin/TestWin.vcxproj +++ /dev/null @@ -1,292 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup Label="ProjectConfigurations"> - <ProjectConfiguration Include="Debug|Win32"> - <Configuration>Debug</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Debug|x64"> - <Configuration>Debug</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|Win32"> - <Configuration>Release</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|x64"> - <Configuration>Release</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - </ItemGroup> - <PropertyGroup Label="Globals"> - <ProjectGuid>{E1C4EAB3-1677-46F7-92BC-1A1F309B2027}</ProjectGuid> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseOfMfc>false</UseOfMfc> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseOfMfc>false</UseOfMfc> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseOfMfc>false</UseOfMfc> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseOfMfc>false</UseOfMfc> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> - <ImportGroup Label="ExtensionSettings"> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> - </ImportGroup> - <PropertyGroup Label="UserMacros" /> - <PropertyGroup> - <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\bin\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(ProjectName)_$(Configuration)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\..\bin\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ProjectName)_$(Configuration)_$(Platform)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\bin\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(ProjectName)_$(Configuration)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\..\bin\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectName)_$(Configuration)_$(Platform)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental> - <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(ProjectName)_d</TargetName> - <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectName)_d</TargetName> - </PropertyGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Midl> - <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <MkTypLibCompatible>true</MkTypLibCompatible> - <SuppressStartupBanner>true</SuppressStartupBanner> - <TargetEnvironment>Win32</TargetEnvironment> - <TypeLibraryName>.\Release/TestWin.tlb</TypeLibraryName> - <HeaderFileName> - </HeaderFileName> - </Midl> - <ClCompile> - <Optimization>MaxSpeed</Optimization> - <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> - <AdditionalIncludeDirectories>../../Include/;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <StringPooling>true</StringPooling> - <RuntimeLibrary>MultiThreaded</RuntimeLibrary> - <FunctionLevelLinking>true</FunctionLevelLinking> - <PrecompiledHeaderOutputFile>.\Release/TestWin.pch</PrecompiledHeaderOutputFile> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <ObjectFileName>$(IntDir)</ObjectFileName> - <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName> - <WarningLevel>Level3</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <Culture>0x0409</Culture> - </ResourceCompile> - <Link> - <AdditionalDependencies>opengl32.lib;glu32.lib;testbase.lib;siftgpu.lib;%(AdditionalDependencies)</AdditionalDependencies> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - <AdditionalLibraryDirectories>../../lib/;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> - <ProgramDatabaseFile>.\Release/TestWin.pdb</ProgramDatabaseFile> - <SubSystem>Console</SubSystem> - <TargetMachine>MachineX86</TargetMachine> - </Link> - <Bscmake> - <SuppressStartupBanner>true</SuppressStartupBanner> - <OutputFile>.\Release/TestWin.bsc</OutputFile> - </Bscmake> - <ProjectReference> - <LinkLibraryDependencies>false</LinkLibraryDependencies> - </ProjectReference> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> - <Midl> - <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <MkTypLibCompatible>true</MkTypLibCompatible> - <SuppressStartupBanner>true</SuppressStartupBanner> - <TargetEnvironment>X64</TargetEnvironment> - <TypeLibraryName>.\Release/TestWin.tlb</TypeLibraryName> - <HeaderFileName> - </HeaderFileName> - </Midl> - <ClCompile> - <Optimization>MaxSpeed</Optimization> - <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> - <AdditionalIncludeDirectories>../../Include/;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <StringPooling>true</StringPooling> - <RuntimeLibrary>MultiThreaded</RuntimeLibrary> - <FunctionLevelLinking>true</FunctionLevelLinking> - <PrecompiledHeaderOutputFile>.\Release/TestWin.pch</PrecompiledHeaderOutputFile> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <ObjectFileName>$(IntDir)</ObjectFileName> - <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName> - <WarningLevel>Level3</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <Culture>0x0409</Culture> - </ResourceCompile> - <Link> - <AdditionalDependencies>opengl32.lib;glu32.lib;testbase_x64.lib;siftgpu.lib;%(AdditionalDependencies)</AdditionalDependencies> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - <AdditionalLibraryDirectories>../../lib/;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> - <ProgramDatabaseFile>.\Release/TestWin.pdb</ProgramDatabaseFile> - <SubSystem>Console</SubSystem> - <TargetMachine>MachineX64</TargetMachine> - </Link> - <Bscmake> - <SuppressStartupBanner>true</SuppressStartupBanner> - <OutputFile>.\Release/TestWin.bsc</OutputFile> - </Bscmake> - <ProjectReference> - <LinkLibraryDependencies>false</LinkLibraryDependencies> - </ProjectReference> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Midl> - <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <MkTypLibCompatible>true</MkTypLibCompatible> - <SuppressStartupBanner>true</SuppressStartupBanner> - <TargetEnvironment>Win32</TargetEnvironment> - <TypeLibraryName>.\Debug/TestWin.tlb</TypeLibraryName> - <HeaderFileName> - </HeaderFileName> - </Midl> - <ClCompile> - <Optimization>Disabled</Optimization> - <AdditionalIncludeDirectories>../../Include/;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <MinimalRebuild>true</MinimalRebuild> - <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> - <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> - <PrecompiledHeaderOutputFile>.\Debug/TestWin.pch</PrecompiledHeaderOutputFile> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <ObjectFileName>$(IntDir)</ObjectFileName> - <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName> - <WarningLevel>Level3</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - <DebugInformationFormat>EditAndContinue</DebugInformationFormat> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <Culture>0x0409</Culture> - </ResourceCompile> - <Link> - <AdditionalDependencies>opengl32.lib;glu32.lib;testbase_d.lib;siftgpu_d.lib;%(AdditionalDependencies)</AdditionalDependencies> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - <AdditionalLibraryDirectories>../../lib/;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> - <GenerateDebugInformation>true</GenerateDebugInformation> - <ProgramDatabaseFile>.\Debug/TestWin_d.pdb</ProgramDatabaseFile> - <SubSystem>Console</SubSystem> - <TargetMachine>MachineX86</TargetMachine> - </Link> - <Bscmake> - <SuppressStartupBanner>true</SuppressStartupBanner> - <OutputFile>.\Debug/TestWin.bsc</OutputFile> - </Bscmake> - <ProjectReference> - <LinkLibraryDependencies>false</LinkLibraryDependencies> - </ProjectReference> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - <Midl> - <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <MkTypLibCompatible>true</MkTypLibCompatible> - <SuppressStartupBanner>true</SuppressStartupBanner> - <TargetEnvironment>X64</TargetEnvironment> - <TypeLibraryName>.\Debug/TestWin.tlb</TypeLibraryName> - <HeaderFileName> - </HeaderFileName> - </Midl> - <ClCompile> - <Optimization>Disabled</Optimization> - <AdditionalIncludeDirectories>../../Include/;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <MinimalRebuild>true</MinimalRebuild> - <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> - <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> - <PrecompiledHeaderOutputFile>.\Debug/TestWin.pch</PrecompiledHeaderOutputFile> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <ObjectFileName>$(IntDir)</ObjectFileName> - <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName> - <WarningLevel>Level3</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <Culture>0x0409</Culture> - </ResourceCompile> - <Link> - <AdditionalDependencies>opengl32.lib;glu32.lib;testbase_x64_d.lib;siftgpu_d.lib;%(AdditionalDependencies)</AdditionalDependencies> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - <AdditionalLibraryDirectories>../../lib/;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> - <GenerateDebugInformation>true</GenerateDebugInformation> - <ProgramDatabaseFile>.\Debug/TestWin_d.pdb</ProgramDatabaseFile> - <SubSystem>Console</SubSystem> - <TargetMachine>MachineX64</TargetMachine> - </Link> - <Bscmake> - <SuppressStartupBanner>true</SuppressStartupBanner> - <OutputFile>.\Debug/TestWin.bsc</OutputFile> - </Bscmake> - <ProjectReference> - <LinkLibraryDependencies>false</LinkLibraryDependencies> - </ProjectReference> - </ItemDefinitionGroup> - <ItemGroup> - <ClCompile Include="..\..\src\TestWin\GLTestWnd.cpp"> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> - </ItemGroup> - <ItemGroup> - <ClInclude Include="..\..\src\TestWin\GLTestWnd.h" /> - </ItemGroup> - <ItemGroup> - <ProjectReference Include="TestBase.vcxproj"> - <Project>{57496d03-a005-4650-b041-8e70dda4a591}</Project> - <ReferenceOutputAssembly>false</ReferenceOutputAssembly> - </ProjectReference> - </ItemGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> - <ImportGroup Label="ExtensionTargets"> - </ImportGroup> -</Project> \ No newline at end of file diff --git a/3rdparty/SiftGPU/msvc/TestWin/TestWinGlut.dsp b/3rdparty/SiftGPU/msvc/TestWin/TestWinGlut.dsp deleted file mode 100644 index 0d069123..00000000 --- a/3rdparty/SiftGPU/msvc/TestWin/TestWinGlut.dsp +++ /dev/null @@ -1,106 +0,0 @@ -# Microsoft Developer Studio Project File - Name="TestWinGlut" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=TestWinGlut - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "TestWinGlut.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "TestWinGlut.mak" CFG="TestWinGlut - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "TestWinGlut - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "TestWinGlut - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "TestWinGlut - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /W3 /GX /O2 /I "../../Include/" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 kernel32.lib user32.lib siftgpu.lib testbase.lib /nologo /subsystem:console /machine:I386 /out:"../../bin/TestWinGlut.exe" /libpath:"../../lib/" - -!ELSEIF "$(CFG)" == "TestWinGlut - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "../../Include/" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib siftgpu_d.lib testbase_d.lib /nologo /subsystem:console /debug /machine:I386 /out:"../../bin/TestWinGlut_d.exe" /pdbtype:sept /libpath:"../../lib/" - -!ENDIF - -# Begin Target - -# Name "TestWinGlut - Win32 Release" -# Name "TestWinGlut - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=..\..\src\TestWin\TestWinGlut.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=..\..\src\TestWin\TestWinGlut.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project diff --git a/3rdparty/SiftGPU/msvc/TestWin/TestWinGlut.vcxproj b/3rdparty/SiftGPU/msvc/TestWin/TestWinGlut.vcxproj deleted file mode 100644 index 937f2cc0..00000000 --- a/3rdparty/SiftGPU/msvc/TestWin/TestWinGlut.vcxproj +++ /dev/null @@ -1,280 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup Label="ProjectConfigurations"> - <ProjectConfiguration Include="Debug|Win32"> - <Configuration>Debug</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Debug|x64"> - <Configuration>Debug</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|Win32"> - <Configuration>Release</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|x64"> - <Configuration>Release</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - </ItemGroup> - <PropertyGroup Label="Globals"> - <ProjectGuid>{B33F9B4E-BF99-442B-BDC5-3DAA4BCCA395}</ProjectGuid> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseOfMfc>false</UseOfMfc> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseOfMfc>false</UseOfMfc> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseOfMfc>false</UseOfMfc> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseOfMfc>false</UseOfMfc> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> - <ImportGroup Label="ExtensionSettings"> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> - </ImportGroup> - <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" /> - </ImportGroup> - <PropertyGroup Label="UserMacros" /> - <PropertyGroup> - <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\bin\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(ProjectName)_$(Configuration)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\..\bin\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectName)_$(Configuration)_$(Platform)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\bin\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(ProjectName)_$(Configuration)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\..\bin\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ProjectName)_$(Configuration)_$(Platform)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental> - <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(ProjectName)_d</TargetName> - <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectName)_d</TargetName> - </PropertyGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Midl> - <TypeLibraryName>.\Debug/TestWinGlut.tlb</TypeLibraryName> - <HeaderFileName> - </HeaderFileName> - </Midl> - <ClCompile> - <Optimization>Disabled</Optimization> - <AdditionalIncludeDirectories>../../Include/;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <MinimalRebuild>true</MinimalRebuild> - <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> - <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> - <PrecompiledHeaderOutputFile>.\Debug/TestWinGlut.pch</PrecompiledHeaderOutputFile> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <ObjectFileName>$(IntDir)</ObjectFileName> - <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName> - <WarningLevel>Level3</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - <DebugInformationFormat>EditAndContinue</DebugInformationFormat> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <Culture>0x0409</Culture> - </ResourceCompile> - <Link> - <AdditionalDependencies>siftgpu_d.lib;testbase_d.lib;%(AdditionalDependencies)</AdditionalDependencies> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - <AdditionalLibraryDirectories>../../lib/;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> - <GenerateDebugInformation>true</GenerateDebugInformation> - <ProgramDatabaseFile>.\Debug/TestWinGlut_d.pdb</ProgramDatabaseFile> - <SubSystem>Console</SubSystem> - <TargetMachine>MachineX86</TargetMachine> - </Link> - <Bscmake> - <SuppressStartupBanner>true</SuppressStartupBanner> - <OutputFile>.\Debug/TestWinGlut.bsc</OutputFile> - </Bscmake> - <ProjectReference> - <LinkLibraryDependencies>false</LinkLibraryDependencies> - </ProjectReference> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - <Midl> - <TargetEnvironment>X64</TargetEnvironment> - <TypeLibraryName>.\Debug/TestWinGlut.tlb</TypeLibraryName> - <HeaderFileName> - </HeaderFileName> - </Midl> - <ClCompile> - <Optimization>Disabled</Optimization> - <AdditionalIncludeDirectories>../../Include/;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <MinimalRebuild>true</MinimalRebuild> - <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> - <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> - <PrecompiledHeaderOutputFile>.\Debug/TestWinGlut.pch</PrecompiledHeaderOutputFile> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <ObjectFileName>$(IntDir)</ObjectFileName> - <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName> - <WarningLevel>Level3</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <Culture>0x0409</Culture> - </ResourceCompile> - <Link> - <AdditionalDependencies>siftgpu_d.lib;testbase_x64_d.lib;glut64.lib;%(AdditionalDependencies)</AdditionalDependencies> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - <AdditionalLibraryDirectories>../../lib/;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> - <IgnoreSpecificDefaultLibraries>glut32.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries> - <GenerateDebugInformation>true</GenerateDebugInformation> - <ProgramDatabaseFile>.\Debug/TestWinGlut_d.pdb</ProgramDatabaseFile> - <SubSystem>Console</SubSystem> - <TargetMachine>MachineX64</TargetMachine> - </Link> - <Bscmake> - <SuppressStartupBanner>true</SuppressStartupBanner> - <OutputFile>.\Debug/TestWinGlut.bsc</OutputFile> - </Bscmake> - <ProjectReference> - <LinkLibraryDependencies>false</LinkLibraryDependencies> - </ProjectReference> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Midl> - <TypeLibraryName>.\Release/TestWinGlut.tlb</TypeLibraryName> - <HeaderFileName> - </HeaderFileName> - </Midl> - <ClCompile> - <Optimization>MaxSpeed</Optimization> - <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> - <AdditionalIncludeDirectories>../../Include/;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <StringPooling>true</StringPooling> - <RuntimeLibrary>MultiThreaded</RuntimeLibrary> - <FunctionLevelLinking>true</FunctionLevelLinking> - <PrecompiledHeaderOutputFile>.\Release/TestWinGlut.pch</PrecompiledHeaderOutputFile> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <ObjectFileName>$(IntDir)</ObjectFileName> - <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName> - <WarningLevel>Level3</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <Culture>0x0409</Culture> - </ResourceCompile> - <Link> - <AdditionalDependencies>siftgpu.lib;testbase.lib;%(AdditionalDependencies)</AdditionalDependencies> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - <AdditionalLibraryDirectories>../../lib/;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> - <ProgramDatabaseFile>.\Release/TestWinGlut.pdb</ProgramDatabaseFile> - <SubSystem>Console</SubSystem> - <TargetMachine>MachineX86</TargetMachine> - </Link> - <Bscmake> - <SuppressStartupBanner>true</SuppressStartupBanner> - <OutputFile>.\Release/TestWinGlut.bsc</OutputFile> - </Bscmake> - <ProjectReference> - <LinkLibraryDependencies>false</LinkLibraryDependencies> - </ProjectReference> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> - <Midl> - <TargetEnvironment>X64</TargetEnvironment> - <TypeLibraryName>.\Release/TestWinGlut.tlb</TypeLibraryName> - <HeaderFileName> - </HeaderFileName> - </Midl> - <ClCompile> - <Optimization>MaxSpeed</Optimization> - <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> - <AdditionalIncludeDirectories>../../Include/;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <StringPooling>true</StringPooling> - <RuntimeLibrary>MultiThreaded</RuntimeLibrary> - <FunctionLevelLinking>true</FunctionLevelLinking> - <PrecompiledHeaderOutputFile>.\Release/TestWinGlut.pch</PrecompiledHeaderOutputFile> - <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> - <ObjectFileName>$(IntDir)</ObjectFileName> - <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName> - <WarningLevel>Level3</WarningLevel> - <SuppressStartupBanner>true</SuppressStartupBanner> - </ClCompile> - <ResourceCompile> - <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <Culture>0x0409</Culture> - </ResourceCompile> - <Link> - <AdditionalDependencies>siftgpu.lib;testbase_x64.lib;glut64.lib;%(AdditionalDependencies)</AdditionalDependencies> - <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> - <SuppressStartupBanner>true</SuppressStartupBanner> - <AdditionalLibraryDirectories>../../lib/;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> - <IgnoreSpecificDefaultLibraries>glut32.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries> - <ProgramDatabaseFile>.\Release/TestWinGlut.pdb</ProgramDatabaseFile> - <SubSystem>Console</SubSystem> - <TargetMachine>MachineX64</TargetMachine> - </Link> - <Bscmake> - <SuppressStartupBanner>true</SuppressStartupBanner> - <OutputFile>.\Release/TestWinGlut.bsc</OutputFile> - </Bscmake> - <ProjectReference> - <LinkLibraryDependencies>false</LinkLibraryDependencies> - </ProjectReference> - </ItemDefinitionGroup> - <ItemGroup> - <ClCompile Include="..\..\src\TestWin\TestWinGlut.cpp"> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> - </ItemGroup> - <ItemGroup> - <ClInclude Include="..\..\src\TestWin\TestWinGlut.h" /> - </ItemGroup> - <ItemGroup> - <ProjectReference Include="TestBase.vcxproj"> - <Project>{57496d03-a005-4650-b041-8e70dda4a591}</Project> - <ReferenceOutputAssembly>false</ReferenceOutputAssembly> - </ProjectReference> - </ItemGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> - <ImportGroup Label="ExtensionTargets"> - </ImportGroup> -</Project> \ No newline at end of file diff --git a/3rdparty/SiftGPU/speed_and_accuracy.txt b/3rdparty/SiftGPU/speed_and_accuracy.txt deleted file mode 100644 index ca68166c..00000000 --- a/3rdparty/SiftGPU/speed_and_accuracy.txt +++ /dev/null @@ -1,87 +0,0 @@ ------------------------------------------------------------------------- -SPEED ------------------------------------------------------------------------- -1. The first several rounds of SiftGPU might be slow, don't worry. If it -is always slow, the reason might be either the graphic card is old or the -graphic memory is small. - -2. Texture reallocation happens when a new image does not fit in the already -allocated storage. To get less reallocations, you can pre-allocate storage -to fit the largest image size, or just use images with the same size. - -3. Loading some compressed images (.e.g jpg) may take a lot of time on -decompressing. Using binary pgm files or directly specifying data in memory -can achive better performance. Writing ASCII SIFT files is also slow. - -4. The packd version saves GPU memory but also run faster than the unpacked, -which is default now. - -5. SiftGPU might be faster with older grpahic card drivers than with newer ones. - -6. The descriptor normalization in the OpenGL-based implementation is running -on CPU. New versions are now using SSE, which improves the speed for this part -a lot. - -7. The orientation computation in unpacked implementation is occasionally slow -under single orientation mode (-m 1) or packed orientation mode (-m2p). By -default, siftgpu uses 2 orientations (-m 2), which should be fine. This issue -is still unresolved. - -8. The thread block settings in the CUDA-based SiftGPU are currently tuned -for my GPU nVidia GTX 8800, which may not be optimized for other GPUs. - ----------------------------------------------------------------------------- -ACCURACY ----------------------------------------------------------------------------- -1. The latest version of SiftGPU now has comparable accuracy with CPU -implementatins. Evaluation on box.pgm of Lowe's package now gives around 600 -matches, which is close to SIFT++. - -2. In orientation computation, SiftGPU uses a factor 2.0 * sigma as the sample -window size, which is smaller than the typical value 3.0. Changing it from 2.0 -to 3.0 reduces the speed of this step by %40, but gives only a very small -improvements in matching. You can change it by specifying parameter "-w 3". - -3. In keypoint localization, SiftGPU refines the location only once by default, -and SiftGPU does not move the level of keypoints in the refinement. - -4. The feature locations are having a (0.5,0.5) offset compared with CPU -implementations by default. (0, 0) in texture is at the top-left coorner -(instead of center) of the top-left pixel. You can use the center as (0, 0) -by specifying "-loweo" - -5. By default, SiftGPU does not do Upsampling(-fo -1), To match it with Lowe's -implementation you need to use "-fo -1 -loweo". - -6. SiftGPU may get slightly different results on different GPUs due to different -floating point precision. SiftGPU is tested on limited types of graphic cards/OS, -working on your graphic card is not guaranteed. - -IF it returns different number of features at different run on the same card, -then something is going wrong, and probably some special tricks need to be used. -Please email me if that happens. - -7. When getting wrong matches, please look at the saved SIFT file to make sure -there are no weired descriptors( for example, all of the numbers of a descriptor -are 45, or any number in a descriptor is larger than 255) - ------------------------------------------------------------------------------- -KWOWN ISSUES. ------------------------------------------------------------------------------- -1. SiftGPU may have problem with dual monitor. -2. Slow on 7950. Changing GlobalParam::_iTexFormat to GL_RGBA16F_ARB can make - it work. Unknown reason. -3. Experiments on 8600 show problems. It works fine for the first image, but - gets wrong keypoints after. - - - - - - - - - - - - diff --git a/3rdparty/SiftGPU/src/CMakeLists.txt b/3rdparty/SiftGPU/src/CMakeLists.txt deleted file mode 100644 index d72205fe..00000000 --- a/3rdparty/SiftGPU/src/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -ADD_SUBDIRECTORY(SiftGPU) diff --git a/3rdparty/SiftGPU/src/ServerSiftGPU/ServerSiftGPU.cpp b/3rdparty/SiftGPU/src/ServerSiftGPU/ServerSiftGPU.cpp deleted file mode 100644 index 894aa75f..00000000 --- a/3rdparty/SiftGPU/src/ServerSiftGPU/ServerSiftGPU.cpp +++ /dev/null @@ -1,902 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: ServerSiftGPU.cpp -// Author: Changchang Wu -// Description : implementation for the ServerSiftGPU class. -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - -#include <iostream> -#include <vector> - -using std::cout; -using std::vector; - - -#if defined(SERVER_SIFTGPU_ENABLED) - -#include "GL/glew.h" - -#ifdef _WIN32 - #include <winsock2.h> - #include <process.h> - #define socklen_t int - #pragma comment(lib, "ws2_32.lib") -#else - #include <string.h> - #include <stdio.h> - #include <stdlib.h> - #include <sys/types.h> - #include <sys/socket.h> - #include <unistd.h> - #include <pthread.h> - #include <spawn.h> - #include <netdb.h> - #include <netinet/in.h> - //conversion from Win32 - typedef int SOCKET; - #define INVALID_SOCKET -1 - #define closesocket close -#endif - -#include "../SiftGPU/SiftGPU.h" -#include "ServerSiftGPU.h" - - - - - - -class SocketUtil -{ -public: - static int readline(SOCKET s, char *buf, int nread) - { - char c; - int num,n=0; - for(n=1; n<nread; n++) - { - if((num=recv(s,&c,1,0))==1) - { - if(c== '\n') break; - if(c==0) *buf++=' '; - else *buf++=c; - }else if( num==0) - { - if(n==1) return 0; - else break; - }else - { - return -1; - } - } - *buf=0; - return n; - } - static int readint(SOCKET s, int* data, int count = 1) - { - int size = (sizeof(int) * count); - data[0] = 0; - return recv(s, (char*)data, size, 0) == size; - } - static int writeint(SOCKET s, int data) - { - int size = sizeof(int); - return send(s, (char*) (&data), size, 0) == size; - } - static int writeint(SOCKET s, unsigned int data) - { - int size = sizeof(unsigned int); - return send(s, (char*) (&data), size, 0) == size; - } - static int writedata(SOCKET s, const void* data, int count) - { - return send(s, (const char*)data, count, 0) == count; - } - static int readdata(SOCKET s, void* data, int count) - { - int count_read_sum = 0, count_read; - char * p = (char*) data; - do - { - count_read = recv(s, p, count - count_read_sum, 0); - p+= count_read; - count_read_sum += count_read; - }while(count_read > 0 && count_read_sum < count); - return count_read_sum == count; - } - static int init() - { - -#ifdef _WIN32 - WSADATA WsaData; - if(WSAStartup(0x0202, &WsaData)) - { - std::cout << "server: can't startup socket\n"; - return 0; - }else - { - return 1; - } -#else - return 1; -#endif - - } -}; - - -ServerSiftGPU::ServerSiftGPU(int port, char* remote_server) -{ - _port = port; - _socketfd = INVALID_SOCKET; - _connected = 0; - strcpy(_server_name, remote_server? remote_server : "\0"); -} - -int ServerSiftGPU::InitSocket() -{ - return SocketUtil::init(); -} - - -int ServerSiftGPU::StartServerProcess(int argc, char** argv) -{ - vector<char*> args(argc + 4); char ports[16]; - - args[0] = "server_siftgpu"; - args[1] = "-server"; - sprintf(ports, "%d", _port); - args[2] = ports; - /////////////////////////////////////////////// - for(int i = 0; i < argc; ++i) args[i + 3] = argv[i]; - args[argc + 3] = 0; - /////////////////////////////////////////////////////// - //make a new process -#ifdef _WIN32 - int result = (int) _spawnv(_P_NOWAIT, "server_siftgpu.exe", &args[0]); - if(result == -1) std::cerr<< "spawn returns -1 with error = " << errno << "\n"; - return result != -1; -#else - #ifdef __APPLE__ - #define LIBPATH_NAME "DYLD_LIBRARY_PATH" - #else - #define LIBPATH_NAME "LD_LIBRARY_PATH" - #endif - int result; pid_t pid; - char* oldpath = getenv(LIBPATH_NAME); - if(oldpath == NULL) - { - result = posix_spawn(&pid, "server_siftgpu", NULL, NULL, &args[0], NULL); - }else - { - char newpath[1024]= LIBPATH_NAME "="; - strcat(newpath, oldpath); - char* envp [] = {newpath, 0}; - result = posix_spawn(&pid, "server_siftgpu", NULL, NULL, &args[0], envp); - } - if(result) std::cerr << "failed to use poxis_spawn to create the server.\n"; - return result == 0; -#endif -} - -int ServerSiftGPU::ConnectServer(const char* server_name, int port) -{ - struct hostent* hh; - - if((hh = gethostbyname(server_name)) == NULL)return 0; - - //////////////////////////////////////// - struct sockaddr_in servaddr; - memset(&servaddr, 0, sizeof(servaddr)); - servaddr.sin_family = AF_INET; - servaddr.sin_port = htons(port); - servaddr.sin_addr.s_addr=((struct in_addr*)(hh->h_addr))->s_addr; - - ((SOCKET&)_socketfd) = socket(AF_INET, SOCK_STREAM, 0); - if(_socketfd==INVALID_SOCKET) return 0; - - //if can not connect to server, start again - if(connect(_socketfd, (struct sockaddr *)&servaddr, sizeof(servaddr))) - { - closesocket(_socketfd); - _socketfd = INVALID_SOCKET; - return 0; - }else - { - std::cout<<"Connected to server " << server_name << "\n"; - return 1; - } -} - -void ServerSiftGPU::Disconnect() -{ - SocketUtil::writeint(_socketfd, _server_name[0]? COMMAND_DISCONNECT : COMMAND_EXIT); - closesocket(_socketfd); - _socketfd = INVALID_SOCKET; - _connected = 0; -} - -ServerSiftGPU::~ServerSiftGPU() -{ - if(_connected) Disconnect(); -} - - -inline void ServerSiftGPU::ServerLoop(int port, int argc, char** argv) -{ - SOCKET sockfd, newsockfd; - struct sockaddr_in serv_addr, cli_addr; - socklen_t addr_len = sizeof(sockaddr_in); - ////////////////////////////////////////////////// - memset((char*)&serv_addr, 0, sizeof(serv_addr)); - serv_addr.sin_family = AF_INET; - serv_addr.sin_port = htons(port); - serv_addr.sin_addr.s_addr = INADDR_ANY; - ///////////////////////////////////////////// - - if(ServerSiftGPU::InitSocket() == 0) - { - return; - } - ////////////////////////////////////////////////////////////// - sockfd=socket(AF_INET,SOCK_STREAM,0); - if(sockfd==INVALID_SOCKET) - { - std::cout << "server: can't open stream socket\n"; - return; - }else if(bind(sockfd,(struct sockaddr*)&serv_addr, sizeof(serv_addr))) - { - std::cout << "server: can't bind to port " << port <<"\n"; - return; - }else if(listen(sockfd, 1)) - { - std::cout << "server: failed to listen\n"; - return; - }else - { - std::cout << "server: listent to port "<< port << "\n"; - } - - newsockfd = accept(sockfd, (struct sockaddr*) &cli_addr, &addr_len); - if(newsockfd == INVALID_SOCKET) - { - std::cout << "error: accept failed\n"; - closesocket(sockfd); - return; - } - //////////////////////////////////////////////////////////////// - char buf[1024]; - int command, result; - int sift_feature_count = 0;; - vector<SiftGPU::SiftKeypoint> keys; - vector<float> descriptors; - vector<char> databuf; - - ///////////////////////////////////////////////////////////////// - SiftGPU siftgpu; - SiftMatchGPU matcher; - - if(argc > 0) siftgpu.ParseParam(argc, argv); - - ///////////////////// - siftgpu.SetVerbose(0); - ///////////////////////////////////////////////////////////////// - - do - { - while(SocketUtil::readint(newsockfd, &command) && command != COMMAND_DISCONNECT) - { - switch(command) - { - case COMMAND_INITIALIZE: - { - result = (siftgpu.CreateContextGL() == SiftGPU::SIFTGPU_FULL_SUPPORTED); - SocketUtil::writeint(newsockfd, result); - if(result) break; - } - case COMMAND_EXIT: - closesocket(newsockfd); - closesocket(sockfd); - return; - case COMMAND_ALLOCATE_PYRAMID: - { - int size[2]; - SocketUtil::readint(newsockfd, size, 2); - if(size[0] > 0 && size[1] > 0) siftgpu.AllocatePyramid(size[0], size[1]); - break; - } - case COMMAND_GET_KEY_VECTOR: - { - int size = sift_feature_count * sizeof(SiftGPU::SiftKeypoint); - SocketUtil::writedata(newsockfd, &keys[0], size); - break; - } - case COMMAND_GET_DES_VECTOR: - { - int size = sift_feature_count * sizeof(float) * 128; - SocketUtil::writedata(newsockfd, &descriptors[0], size); - break; - } - case COMMAND_RUNSIFT: - { - result = siftgpu.RunSIFT(); - if((sift_feature_count = siftgpu.GetFeatureNum()) > 0) - { - keys.resize(sift_feature_count); - descriptors.resize(sift_feature_count * 128); - siftgpu.GetFeatureVector(&keys[0], &descriptors[0]); - std::cout << "RunSIFT: [-] [" << sift_feature_count << "]\n"; - } - SocketUtil::writeint(newsockfd, result); - break; - } - case COMMAND_RUNSIFT_FILE: - { - SocketUtil::readline(newsockfd, buf, 1024); - - result = siftgpu.RunSIFT(buf); - if((sift_feature_count = siftgpu.GetFeatureNum()) > 0) - { - keys.resize(sift_feature_count); - descriptors.resize(sift_feature_count * 128); - siftgpu.GetFeatureVector(&keys[0], &descriptors[0]); - } - std::cout << "RunSIFT: "<< buf <<" " << sift_feature_count << "\n" ; - SocketUtil::writeint(newsockfd, result); - break; - } - case COMMAND_SET_KEYPOINT: - { - int keys_have_orientation; - SocketUtil::readint(newsockfd, &sift_feature_count); - SocketUtil::readint(newsockfd, &keys_have_orientation); - if(sift_feature_count > 0) - { - keys.resize(sift_feature_count); - descriptors.resize(sift_feature_count * 128); - SocketUtil::readdata(newsockfd, &keys[0], int(keys.size() * sizeof(SiftGPU::SiftKeypoint))); - siftgpu.SetKeypointList(sift_feature_count, &keys[0], keys_have_orientation); - } - break; - } - case COMMAND_RUNSIFT_KEY: - { - int keys_have_orientation; - SocketUtil::readint(newsockfd, &sift_feature_count); - SocketUtil::readint(newsockfd, &keys_have_orientation); - if(sift_feature_count > 0) - { - std::cout << "RunSIFT: "<< sift_feature_count << " KEYPOINTS\n" ; - int key_data_size = sift_feature_count * sizeof(SiftGPU::SiftKeypoint); - keys.resize(sift_feature_count); - descriptors.resize(sift_feature_count * 128); - SocketUtil::readdata(newsockfd, &keys[0], key_data_size); - result = siftgpu.RunSIFT(sift_feature_count, &keys[0], keys_have_orientation); - siftgpu.GetFeatureVector(NULL, &descriptors[0]); - }else - { - result = 0; - } - SocketUtil::writeint(newsockfd, result); - break; - } - case COMMAND_RUNSIFT_DATA: - { - int data_des[4], size = 0; - SocketUtil::readint(newsockfd, data_des, 4); - SocketUtil::readint(newsockfd, &size, 1); - std::cout << "RunSIFT: [" << data_des[0] << "x" << data_des[1] << "]"; - - databuf.resize(size); - void* data_ptr = &databuf[0]; - SocketUtil::readdata(newsockfd, data_ptr, size); - - - result = siftgpu.RunSIFT(data_des[0], data_des[1], data_ptr, data_des[2], data_des[3]); - if((sift_feature_count = siftgpu.GetFeatureNum()) > 0) - { - keys.resize(sift_feature_count); - descriptors.resize(sift_feature_count * 128); - siftgpu.GetFeatureVector(&keys[0], &descriptors[0]); - } - std::cout << "[" << sift_feature_count << "]\n"; - SocketUtil::writeint(newsockfd, result); - break; - } - case COMMAND_SAVE_SIFT: - { - SocketUtil::readline(newsockfd, buf, 1024); - siftgpu.SaveSIFT(buf); - break; - } - case COMMAND_SET_MAX_DIMENSION: - { - int maxd; - if(SocketUtil::readint(newsockfd, &maxd) && maxd > 0) siftgpu.SetMaxDimension(maxd); - break; - } - case COMMAND_SET_TIGHTPYRAMID: - { - int tight; - if(SocketUtil::readint(newsockfd, &tight)) siftgpu.SetTightPyramid(tight); - break; - } - case COMMAND_GET_FEATURE_COUNT: - { - SocketUtil::writeint(newsockfd, sift_feature_count); - break; - } - case COMMAND_PARSE_PARAM: - { - SocketUtil::readline(newsockfd, buf, 1024); - std::cout << "ParseParam [" << buf << "]\n"; - vector<char*> params; - char* p = buf; - while(*p) - { - while(*p == ' ' || *p == '\t')*p++ = 0; - params.push_back(p); - while(*p && *p != ' ' && *p != '\t') p++; - } - siftgpu.ParseParam(params.size(), &params[0]); - break; - } - case COMMAND_MATCH_INITIALIZE: - { - result = matcher.CreateContextGL(); - SocketUtil::writeint(newsockfd, result); - break; - } - case COMMAND_MATCH_SET_LANGUAGE: - { - int language; - if(SocketUtil::readint(newsockfd, &language)) matcher.SetLanguage(language); - break; - } - case COMMAND_MATCH_SET_DES_FLOAT: - { - int command[3] = {0, 0, 0}; - if(SocketUtil::readdata(newsockfd, command, sizeof(command))) - { - databuf.resize(sizeof(float) * 128 * command[1]); - if(SocketUtil::readdata(newsockfd, &databuf[0], databuf.size())) - { - matcher.SetDescriptors(command[0], command[1], (float*) (&databuf[0]), command[2]); - } - } - break; - } - case COMMAND_MATCH_SET_DES_BYTE: - { - int command[3] = {0, 0, 0}; - if(SocketUtil::readdata(newsockfd, command, sizeof(command))) - { - databuf.resize(sizeof(unsigned char) * 128 * command[1]); - if(SocketUtil::readdata(newsockfd, &databuf[0], databuf.size())) - { - matcher.SetDescriptors(command[0], command[1], (unsigned char*) (&databuf[0]), command[2]); - } - } - break; - } - case COMMAND_MATCH_GET_MATCH: - { - int command[2]; float fcommand[2]; - result = 0; - if( SocketUtil::readdata(newsockfd, command, sizeof(command)) && - SocketUtil::readdata(newsockfd, fcommand, sizeof(fcommand))) - { - int max_match = command[0], mbm = command[1]; - float distmax = fcommand[0], ratiomax = fcommand[1]; - databuf.resize(max_match * 2 * sizeof(int)); - result = matcher.GetSiftMatch(max_match, ( int(*)[2]) (&databuf[0]), distmax, ratiomax, mbm); - - } - SocketUtil::writeint(newsockfd, result); - if(result > 0) SocketUtil::writedata(newsockfd, &databuf[0], sizeof(int) * 2 * result); - std::cout << "SiftMatch: " << result << "\n"; - break; - } - case COMMAND_MATCH_SET_MAXSIFT: - { - int max_sift; - if(SocketUtil::readint(newsockfd, &max_sift)) matcher.SetMaxSift(max_sift); - break; - } - break; - default: - std::cout << "unrecognized command: " << command << "\n"; - break; - } - } - - //client disconneted - closesocket(newsockfd); - //wait for the next client. - std::cout << "wait for new client..."; - newsockfd = accept(sockfd, (struct sockaddr*) &cli_addr, &addr_len); - if(newsockfd == INVALID_SOCKET) - { - std::cout << "error: accept failed"; - closesocket(sockfd); - return; - }else - { - std::cout << "connected\n\n"; - } - }while(1); -} - -void ServerSiftGPU::SetParamSiftGPU(int argc, char** argv) -{ - if(!_connected || argc <= 0) return; - - char buf[1025], *p = buf, cret = '\n'; - for(int i = 0; i < argc; ++i) - { - if(argv[i]) - { - strcpy(p, argv[i]); - p += strlen(argv[i]); - } - *p++= ((i +1 < argc)? ' ' : '\0'); - } - SocketUtil::writeint(_socketfd, COMMAND_PARSE_PARAM); - SocketUtil::writedata(_socketfd, buf, (p - buf)); - SocketUtil::writedata(_socketfd, &cret, 1); - -} - -int ServerSiftGPU:: InitializeConnection(int argc, char** argv) -{ - char server_name[1024]; - if(!InitSocket()) return 0 ; - if(_server_name[0] == 0) - { - if(StartServerProcess(argc, argv) == 0) - { - std::cout<<"Unable to start local siftgpu server\n"; - return 0 ; - } - strcpy(server_name, "127.0.0.1"); - }else - { - strcpy(server_name, _server_name); - } - - /////////////////////////////////////////////// - if(ConnectServer(server_name, _port) == 0) - { - //wait for one second -#ifdef _WIN32 - Sleep(1000); -#else - sleep(1); -#endif - if(ConnectServer(server_name, _port) == 0) - { - std::cout<<"Unable to connect siftgpu sever\n"; - return 0 ; - } - } - return 1; -} - -void ServerSiftGPU::ParseParam(int argc, char **argv) -{ - if(_connected == 1) - { - SetParamSiftGPU(argc, argv); - }else - { - _connected = InitializeConnection(argc, argv); - if(_server_name[0] && argc > 0) SetParamSiftGPU(argc, argv); - } -} - -int ServerSiftGPU::VerifyContextGL() -{ - //////////////////////////////////////////////////// - if(!_connected) return 0; - - int result = 0; - - if(SocketUtil::writeint(_socketfd, COMMAND_INITIALIZE) && - SocketUtil::readint(_socketfd, &result) && result) - { - return SiftGPU::SIFTGPU_FULL_SUPPORTED; - }else - { - std::cout<<"SifGPU failed to initialize\n"; - Disconnect(); - return 0; - } -} - -int ServerSiftGPU::GetFeatureNum() -{ - if(!_connected) return 0; - int result = 0; - SocketUtil::writeint(_socketfd, COMMAND_GET_FEATURE_COUNT); - SocketUtil::readint(_socketfd, &result); - return result; -} - -int ServerSiftGPU::AllocatePyramid(int width, int height) -{ - if(!_connected) return 0; - int command[3] = {COMMAND_ALLOCATE_PYRAMID, width, height}; - return SocketUtil::writedata(_socketfd, command, sizeof(int) * 3); -} - -//////////////////////////////////////////////////////////// -void ServerSiftGPU::SaveSIFT(const char * szFileName) -{ - if(!_connected) return; - - char cret = '\n'; - SocketUtil::writeint(_socketfd, COMMAND_SAVE_SIFT); - SocketUtil::writedata(_socketfd, szFileName, (int)strlen(szFileName)); - SocketUtil::writedata(_socketfd, &cret, 1); - -} - -void ServerSiftGPU::GetFeatureVector(SiftGPU::SiftKeypoint * keys, float * descriptors) -{ - if(!_connected) return; - - int num = GetFeatureNum(), result = 1; - if(keys && num > 0) - { - result&= SocketUtil::writeint(_socketfd, COMMAND_GET_KEY_VECTOR); - result &= SocketUtil::readdata(_socketfd, keys, num * sizeof(SiftGPU::SiftKeypoint)); - } - if(descriptors && num > 0) - { - result&= SocketUtil::writeint(_socketfd, COMMAND_GET_DES_VECTOR); - result&= SocketUtil::readdata(_socketfd, descriptors, num * 128 * sizeof(float)); - } -} - -void ServerSiftGPU::SetKeypointList(int num, const SiftGPU::SiftKeypoint * keys, int keys_have_orientation) -{ - if(!_connected) return; - - SocketUtil::writeint(_socketfd, COMMAND_SET_KEYPOINT); - SocketUtil::writeint(_socketfd, num); - SocketUtil::writeint(_socketfd, keys_have_orientation); - SocketUtil::writedata(_socketfd, keys, sizeof(SiftGPU::SiftKeypoint) * num); - -} - -void ServerSiftGPU::SetTightPyramid(int tight) -{ - if(!_connected) return ; - SocketUtil::writeint(_socketfd, COMMAND_SET_TIGHTPYRAMID); - SocketUtil::writeint(_socketfd, tight); - -} - -void ServerSiftGPU::SetMaxDimension(int sz) -{ - if(!_connected) return ; - SocketUtil::writeint(_socketfd, COMMAND_SET_MAX_DIMENSION); - SocketUtil::writeint(_socketfd, sz); - -} - -int ServerSiftGPU::GetPixelSizeGL(unsigned int gl_format, unsigned int gl_type) -{ - int num_channel_byte = 0; - int num_channels = 0; - switch(gl_type) - { - case GL_BITMAP: - case GL_UNSIGNED_BYTE: - case GL_BYTE: - num_channel_byte = 1; - break; - case GL_UNSIGNED_SHORT: - case GL_SHORT: - num_channel_byte = 2; - break; - case GL_UNSIGNED_INT: - case GL_INT: - case GL_FLOAT: - num_channel_byte = 4; - break; - default: - num_channel_byte = 0; - break; - } - - switch(gl_format) - { - case GL_RED: - case GL_GREEN: - case GL_BLUE: - case GL_ALPHA: - case GL_LUMINANCE: - num_channels = 1; - break; - case GL_RGB: - case GL_BGR_EXT: - num_channels = 3; - break; - case GL_RGBA: - case GL_BGRA_EXT: -#ifdef GL_ARGB_I3D - case GL_ARGB_I3D: -#endif - num_channels = 4; - break; - case GL_LUMINANCE_ALPHA: -#ifdef GL_422_EXT - case GL_422_EXT: - case GL_422_REV_EXT: - case GL_422_AVERAGE_EXT: - case GL_422_REV_AVERAGE_EXT: -#endif - num_channels = 2; - default: - num_channels = 0; - break; - } - return num_channels * num_channel_byte; -} - -int ServerSiftGPU::RunSIFT(int width, int height, const void * data, unsigned int gl_format, unsigned int gl_type) -{ - if(width <=0 || height <= 0 || data == NULL || !_connected) return 0; - int num_bytes = GetPixelSizeGL(gl_format , gl_type) * width * height; - if(num_bytes == 0) return 0; - SocketUtil::writeint(_socketfd, COMMAND_RUNSIFT_DATA); - unsigned int data_des[5] = {width, height, gl_format, gl_type, num_bytes}; - SocketUtil::writedata(_socketfd, data_des, 5 * sizeof(unsigned int)); - SocketUtil::writedata(_socketfd, data, num_bytes); - int result = 0; - return SocketUtil::readint(_socketfd, &result) && result; -} - -int ServerSiftGPU::RunSIFT(int num, const SiftGPU::SiftKeypoint * keys, int keys_have_orientation) -{ - if(num <= 0 || keys == NULL) return 0; - if(!_connected) return 0; - SocketUtil::writeint(_socketfd, COMMAND_RUNSIFT_KEY); - SocketUtil::writeint(_socketfd, num); - SocketUtil::writeint(_socketfd, keys_have_orientation); - SocketUtil::writedata(_socketfd, keys, sizeof(SiftGPU::SiftKeypoint) * num); - int result = 0; - return SocketUtil::readint(_socketfd, &result) && result; -} - -int ServerSiftGPU::RunSIFT() -{ - if(!_connected) return 0; - int result = 0; - SocketUtil::writeint(_socketfd, COMMAND_RUNSIFT); - return SocketUtil::readint(_socketfd, &result) && result; -} - -int ServerSiftGPU::RunSIFT(const char *imgpath) -{ - if(!_connected) return 0; - int result = 0; char cret = '\n'; - SocketUtil::writeint(_socketfd, COMMAND_RUNSIFT_FILE); - SocketUtil::writedata(_socketfd, imgpath, (int)strlen(imgpath)); - SocketUtil::writedata(_socketfd, &cret, 1); - return SocketUtil::readint(_socketfd, &result) && result; -} - - -//////////////////////////////////////////////// -int ServerSiftGPU::_VerifyContextGL() -{ - if(!_connected) return 0; - int result; - if( SocketUtil::writeint(_socketfd, COMMAND_MATCH_INITIALIZE) && - SocketUtil::readint(_socketfd, &result) && result) - { - return 1; - }else - { - Disconnect(); - return 0; - } -} - -void ServerSiftGPU::SetLanguage(int gpu_language) -{ - if(!_connected) return ; - SocketUtil::writeint(_socketfd, COMMAND_MATCH_SET_LANGUAGE); - SocketUtil::writeint(_socketfd, gpu_language); -} - -void ServerSiftGPU::SetDeviceParam(int argc, char**argv) -{ - ServerSiftGPU::ParseParam(argc, argv); -} - - -void ServerSiftGPU::SetMaxSift(int max_sift) -{ - if(!_connected) return; - SocketUtil::writeint(_socketfd, COMMAND_MATCH_SET_MAXSIFT); - SocketUtil::writeint(_socketfd, max_sift); -} - -void ServerSiftGPU::SetDescriptors(int index, int num, const float* descriptors, int id) -{ - if(!_connected) return ; - int command[4] = {COMMAND_MATCH_SET_DES_FLOAT, index, num, id}; - SocketUtil::writedata(_socketfd, command, sizeof(command)); - SocketUtil::writedata(_socketfd, descriptors, sizeof(float) * 128 * num); -} - - -void ServerSiftGPU::SetDescriptors(int index, int num, const unsigned char * descriptors, int id) -{ - if(!_connected) return ; - int command[4] = {COMMAND_MATCH_SET_DES_BYTE, index, num, id}; - SocketUtil::writedata(_socketfd, command, sizeof(command)); - SocketUtil::writedata(_socketfd, descriptors, sizeof(unsigned char) * 128 * num); -} - -int ServerSiftGPU::GetSiftMatch(int max_match, int match_buffer[][2], - float distmax, float ratiomax, int mutual_best_match) -{ - if(!_connected) return 0; - int command[3] = {COMMAND_MATCH_GET_MATCH, max_match, mutual_best_match}; - float fcommand[2] = {distmax, ratiomax}; - SocketUtil::writedata(_socketfd, command, sizeof(command)); - SocketUtil::writedata(_socketfd, fcommand, sizeof(fcommand)); - int nm; SocketUtil::readint(_socketfd, &nm); - if(nm > 0) SocketUtil::readint(_socketfd, match_buffer[0], 2 * nm); - return nm; -} - -void RunServerLoop(int port, int argc, char** argv) -{ - ServerSiftGPU::ServerLoop(port, argc, argv); -} - - -ComboSiftGPU* CreateRemoteSiftGPU(int port, char* remote_server) -{ - return new ServerSiftGPU(port, remote_server); -} - - -#else - -#include "../SiftGPU/LiteWindow.h" -#include "../SiftGPU/SiftGPU.h" - -ComboSiftGPU* CreateRemoteSiftGPU(int port, char* remote_server) -{ - std::cout << "ServerSiftGPU need marcro SERVER_SIFTGPU_EANBLED.\n" - << "Use local SiftGPU/SiftMatchGPU instead. \n"; - return new ComboSiftGPU; -} - -void RunServerLoop(int port, int argc, char** argv) -{ - std::cout << "ServerSiftGPU need marcro SERVER_SIFTGPU_EANBLED.\n" - } - -#endif - diff --git a/3rdparty/SiftGPU/src/ServerSiftGPU/ServerSiftGPU.h b/3rdparty/SiftGPU/src/ServerSiftGPU/ServerSiftGPU.h deleted file mode 100644 index b0a28b22..00000000 --- a/3rdparty/SiftGPU/src/ServerSiftGPU/ServerSiftGPU.h +++ /dev/null @@ -1,159 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: ServerSiftGPu.h -// Author: Changchang Wu -// Description : interface for the ServerSiftGPU class. -// It starts a SiftGPU server in a new process -// or connects to an existing server. -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - -#ifndef GPU_SIFT_SERVER_H -#define GPU_SIFT_SERVER_H - - -class ComboSiftGPU; -class LiteWindow; - -///////////////////////////////////////////////////////////////////////////// -//ServerSiftGPU::ServerSiftGPU(int port, char* remote_server) -//Run SiftGPU/SiftMatchGPU computation on a remote computer/process -//if( remote_server == NULL) -// a local server is created in a different process and connects to it -// multiple-GPU can be used by creating multiple instances -// GPU selection done through ParseParam function -//otherwise, -// Assumes the existenc of a remote server and connects to it -// GPU selection is done on the server-end when create the server by running -// server_siftgpu -server port [siftgpu_param] -///////////////////////////////////////////////////////////////////////////// - - -class ServerSiftGPU: public ComboSiftGPU -{ - enum - { - COMMAND_NONE= 0, - COMMAND_EXIT= 1, - COMMAND_DISCONNECT, - /////////////////////////////// - COMMAND_INITIALIZE, - COMMAND_ALLOCATE_PYRAMID, - COMMAND_RUNSIFT, - COMMAND_RUNSIFT_FILE, - COMMAND_RUNSIFT_KEY, - COMMAND_RUNSIFT_DATA, - COMMAND_SAVE_SIFT, - COMMAND_SET_MAX_DIMENSION, - COMMAND_SET_KEYPOINT, - COMMAND_GET_FEATURE_COUNT, - COMMAND_SET_TIGHTPYRAMID, - COMMAND_GET_KEY_VECTOR, - COMMAND_GET_DES_VECTOR, - COMMAND_PARSE_PARAM, - - ///////////////////////////////// - COMMAND_MATCH_INITIALIZE, - COMMAND_MATCH_SET_LANGUAGE, - COMMAND_MATCH_SET_DES_FLOAT, - COMMAND_MATCH_SET_DES_BYTE, - COMMAND_MATCH_SET_MAXSIFT, - COMMAND_MATCH_GET_MATCH, - /////////////////////////////// - DEFAULT_PORT = 7777 - }; -private: -#ifdef _WIN64 - unsigned __int64 _socketfd; -#else - int _socketfd; -#endif - int _port; - int _connected; - char _server_name[1024]; -private: - void SetParamSiftGPU(int argc, char** argv); - int InitializeConnection(int argc, char** argv); - int StartServerProcess(int argc, char** argv); - int ConnectServer(const char* server_name, int port); - void Disconnect(); - static int InitSocket(); - static int GetPixelSizeGL(unsigned int gl_format, unsigned int gl_type); - static void ServerLoop(int port, int argc, char** argv); -public: - //two options : multi-threading or multi-processing - SIFTGPU_EXPORT ServerSiftGPU(int port = DEFAULT_PORT, char* remote_server = NULL); - virtual ~ServerSiftGPU(); - //////////////////////////////////////// - virtual void ParseParam(int argc, char **argv); - virtual int VerifyContextGL(); - //////////////////////////////////////////////////////////////////////// - //available SiftGPU functions are the following: - virtual int RunSIFT(const char * imgpath); - virtual int RunSIFT(); - //note the difference with class SiftGPU for the function below, - //you have to provide the number of bytes of the data. - virtual int RunSIFT(int width, int height, const void * data, unsigned int gl_format, unsigned int gl_type); - virtual int RunSIFT(int num, const SiftGPU::SiftKeypoint * keys, int keys_have_orientation = 1); - virtual int AllocatePyramid(int width, int height); - ///////////////////////////////////////////////////////////////////// - virtual int GetFeatureNum(); - virtual void SetTightPyramid(int tight = 1); - virtual void SetMaxDimension(int sz); - virtual void GetFeatureVector(SiftGPU::SiftKeypoint * keys, float * descriptors); - virtual void SetKeypointList(int num, const SiftGPU::SiftKeypoint * keys, int keys_have_orientation = 1); - ///////////////////////////////////////////////////////////////////////////// - //the following functions do not block in multi-process mode - //for example, SaveSIFT will return before the file is written - virtual void SaveSIFT(const char * szFileName); - - //simplified functions - int GetImageCount(){return 1;} - int CreateContextGL(){return VerifyContextGL();} - int IsFullSupported(){return VerifyContextGL() == SiftGPU::SIFTGPU_FULL_SUPPORTED;} - int RunSIFT(int index) {return RunSIFT();} - -//////////////////////// -public: - virtual int _CreateContextGL() {return _VerifyContextGL();} - virtual int _VerifyContextGL(); - virtual void SetLanguage(int gpu_language); - virtual void SetDeviceParam(int argc, char**argv); - virtual void SetMaxSift(int max_sift); - virtual void SetDescriptors(int index, int num, const float* descriptors, int id = -1); - virtual void SetDescriptors(int index, int num, const unsigned char * descriptors, int id = -1); - virtual int GetSiftMatch(int max_match, int match_buffer[][2], //buffeture indices - float distmax, float ratiomax, int mutual_best_match); // -public: - ////////////////////////////////////////////////////// - //Some SiftGPU functions are not supported - void SetImageList(int nimage, const char** filelist) {} - void SetVerbose(){} - - ///Guided matching is not supported here, not hard to implement yourself - virtual void SetFeautreLocation(int index, const float* locations, int gap) {return ;} - virtual int GetGuidedSiftMatch(int max_match, int match_buffer[][2], float H[3][3],float F[3][3], - float distmax, float ratiomax, float hdistmax, float fdistmax, int mutual_best_match) {return 0; } - - friend void RunServerLoop(int port, int argc, char** argv); - -}; - - - -#endif - - diff --git a/3rdparty/SiftGPU/src/ServerSiftGPU/server.cpp b/3rdparty/SiftGPU/src/ServerSiftGPU/server.cpp deleted file mode 100644 index c0f0e240..00000000 --- a/3rdparty/SiftGPU/src/ServerSiftGPU/server.cpp +++ /dev/null @@ -1,119 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: server.cpp -// Author: Changchang Wu -// Description : driver of a SiftGPU Server. -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// -#include <string.h> -#include <iostream> -#include <stdio.h> -using std::cout; - -#include "../SiftGPU/SiftGPU.h" - -//if you included ServerSiftGPU.h here. -//CreateRemoteSiftGPU can be replaced by new ServerSiftGPU(); - -int main(int argc, char** argv) -{ - - if(argc >= 2 && strcmp(argv[1], "-server") == 0) - { - //run siftgpu server - std::cout << "Starting server process...\n"; - int port = 7777; - if(argc >= 3) sscanf(argv[2], "%d", &port); - ////////////////////////////////////// - RunServerLoop(port, argc - 3, argv + 3); - }else if(argc >=2 && strcmp(argv[1], "-test") == 0) - { - //start server on same computer and connect... - //note we are using a SiftGPU pointer.. - SiftGPU* siftgpu = CreateRemoteSiftGPU(); - siftgpu->ParseParam(argc - 2, argv + 2); - if(siftgpu->VerifyContextGL()) - { - //files on the matchines where the server runs - siftgpu->RunSIFT("../data/800-1.jpg"); - siftgpu->RunSIFT("../data/800-2.jpg"); - } - delete siftgpu; //this will terminate the server - }else if(argc >=4 && strcmp(argv[1], "-test_remote") == 0) - { - ////test client that connects to remote server... - char server_name[1024]; int port = 7777; - strcpy(server_name, argv[2]); - sscanf(argv[3], "%d", &port); - SiftGPU* siftgpu = CreateRemoteSiftGPU(port, server_name);//new ServerSiftGPU(port, server_name); - siftgpu->ParseParam(argc - 4, argv + 4); - if(siftgpu->VerifyContextGL()) - { - //files on the matchines where the server runs - siftgpu->RunSIFT("../data/800-1.jpg"); - siftgpu->RunSIFT("../data/800-2.jpg"); - } - delete siftgpu; - }else if(argc >=2 && strcmp(argv[1], "-test2") == 0) - { - //test using two siftgpu servers...usage: - //server_siftgpu -test2 [server1_params] [-server2 [server2_params]] - - //////////////////////////////////////////// - //they need to use different ports. - SiftGPU* siftgpu1 = CreateRemoteSiftGPU(7777);//new ServerSiftGPU(7777); - SiftGPU* siftgpu2 = CreateRemoteSiftGPU(8888);//new ServerSiftGPU(8888); - - //split the parameters for the two servers - int argc1 = 0, argc2 = 0; - char** argv1 = argv + 2, **argv2 = 0; - while(argc1 + 2 < argc && strcmp(argv[argc1 + 2], "-server2")) - { - argc1++; - } - if(argc1 + 3 < argc && strcmp(argv[argc1 + 2], "-server2") == 0) - { - argv2 = argv + argc1 + 3; - argc2 = argc - argc1 - 3; - } - - //initialize the two servers with their siftgpu parameters - siftgpu1->ParseParam(argc1, argv1); - siftgpu2->ParseParam(argc2, argv2); - if(siftgpu1->VerifyContextGL() && siftgpu2->VerifyContextGL()) - { - siftgpu1->RunSIFT("../data/800-1.jpg"); - siftgpu2->RunSIFT("../data/800-1.jpg"); - siftgpu1->RunSIFT("../data/800-2.jpg"); - siftgpu2->RunSIFT("../data/800-2.jpg"); - } - delete siftgpu1; - delete siftgpu2; - }else - { - std::cout - <<"try -test [siftgpu_param] to create a local server and a client\n" - <<"try -test2 [siftgpu_param1] [-server2 [siftgpu_param2]]\n" - <<" to create two servers and two clients\n" - <<"try -test_remote remote_sever_name remote_server_port\n" - <<" to create a client and connect to remote server\n" - <<"use -server port [siftgpu_param] to start a server\n" - <<"Note [siftgpu_param] allows you to select GPU from multi-GPUs\n" - <<"\n"; - } - return 1; -} - diff --git a/3rdparty/SiftGPU/src/SiftGPU/CLTexImage.cpp b/3rdparty/SiftGPU/src/SiftGPU/CLTexImage.cpp deleted file mode 100644 index a796721f..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/CLTexImage.cpp +++ /dev/null @@ -1,229 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: CLTexImage.cpp -// Author: Changchang Wu -// Description : implementation of the CLTexImage class. -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - -#if defined(CL_SIFTGPU_ENABLED) - -#include "GL/glew.h" -#include <iostream> -#include <vector> -#include <algorithm> -#include <stdlib.h> -#include <math.h> -using namespace std; - - -#include <CL/OpenCL.h> -#include "CLTexImage.h" -#include "ProgramCL.h" -#include "GlobalUtil.h" - - -CLTexImage::CLTexImage() -{ - _context = NULL; - _queue = NULL; - _clData = NULL; - _numChannel = _bufferLen = _fromGL = 0; - _imgWidth = _imgHeight = _texWidth = _texHeight = 0; -} - -CLTexImage::CLTexImage(cl_context context, cl_command_queue queue) -{ - _context = context; - _queue = queue; - _clData = NULL; - _numChannel = _bufferLen = _fromGL = 0; - _imgWidth = _imgHeight = _texWidth = _texHeight = 0; -} - -void CLTexImage::SetContext(cl_context context, cl_command_queue queue) -{ - _context = context; - _queue = queue; -} - - -CLTexImage::~CLTexImage() -{ - ReleaseTexture(); -} - -void CLTexImage::ReleaseTexture() -{ - if(_fromGL) clEnqueueReleaseGLObjects(_queue, 1, &_clData, 0, NULL, NULL); - if(_clData) clReleaseMemObject(_clData); -} - -void CLTexImage::SetImageSize(int width, int height) -{ - _imgWidth = width; - _imgHeight = height; -} - -void CLTexImage::InitBufferTex(int width, int height, int nchannel) -{ - if(width == 0 || height == 0 || nchannel <= 0 || _fromGL) return; - - _imgWidth = width; _imgHeight = height; - _texWidth = _texHeight = _fromGL = 0; - _numChannel = min(nchannel, 4); - - int size = width * height * _numChannel * sizeof(float); - if (size <= _bufferLen) return; - - //allocate the buffer data - cl_int status; - if(_clData) status = clReleaseMemObject(_clData); - - _clData = clCreateBuffer(_context, CL_MEM_READ_WRITE, - _bufferLen = size, NULL, &status); - - ProgramBagCL::CheckErrorCL(status, "CLTexImage::InitBufferTex"); - -} - -void CLTexImage::InitTexture(int width, int height, int nchannel) -{ - if(width == 0 || height == 0 || nchannel <= 0 || _fromGL) return; - if(_clData && width == _texWidth && height == _texHeight && _numChannel == nchannel) return; - if(_clData) clReleaseMemObject(_clData); - - _texWidth = _imgWidth = width; - _texHeight = _imgHeight = height; - _numChannel = nchannel; - _bufferLen = _fromGL = 0; - - cl_int status; cl_image_format format; - - if(nchannel == 1) format.image_channel_order = CL_R; - else if(nchannel == 2) format.image_channel_order = CL_RG; - else if(nchannel == 3) format.image_channel_order = CL_RGB; - else format.image_channel_order = CL_RGBA; - - format.image_channel_data_type = CL_FLOAT; - _clData = clCreateImage2D(_context, CL_MEM_READ_WRITE, & format, - _texWidth, _texHeight, 0, 0, &status); - ProgramBagCL::CheckErrorCL(status, "CLTexImage::InitTexture"); -} - -void CLTexImage::InitPackedTex(int width, int height, int packed) -{ - if(packed) InitTexture((width + 1) >> 1, (height + 1) >> 1, 4); - else InitTexture(width, height, 1); -} - -void CLTexImage::SetPackedSize(int width, int height, int packed) -{ - if(packed) SetImageSize((width + 1) >> 1, (height + 1) >> 1); - else SetImageSize(width, height); -} - -void CLTexImage::InitTextureGL(GLuint tex, int width, int height, int nchannel) -{ - if(tex == 0) return; - if(_clData) clReleaseMemObject(_clData); - - ////create the memory object - cl_int status; - _clData = clCreateFromGLTexture2D(_context, CL_MEM_WRITE_ONLY, - GlobalUtil::_texTarget, 0 , tex, &status); - ProgramBagCL::CheckErrorCL(status, "CLTexImage::InitTextureGL->clCreateFromGLTexture2D"); - if(status != CL_SUCCESS) return; - - _texWidth = _imgWidth = width; - _texHeight = _imgHeight = height; - _numChannel = nchannel; - _bufferLen = 0; _fromGL = 1; - - ////acquire object - status = clEnqueueAcquireGLObjects(_queue, 1, &_clData, 0, NULL, NULL); - ProgramBagCL::CheckErrorCL(status, "CLTexImage::InitTextureGL->clEnqueueAcquireGLObjects"); - -} - -void CLTexImage::CopyFromHost(const void * buf) -{ - if(_clData == NULL) return; - cl_int status; - if(_bufferLen) - { - status = clEnqueueWriteBuffer(_queue, _clData, false, 0, - _imgWidth * _imgHeight * _numChannel * sizeof(float), buf, 0, NULL, NULL); - }else - { - size_t origin[3] = {0, 0, 0}, region[3] = {_imgWidth, _imgHeight, 1}; - size_t row_pitch = _imgWidth * _numChannel * sizeof(float); - status = clEnqueueWriteImage(_queue, _clData, false, origin, - region, row_pitch, 0, buf, 0, 0, 0); - } - ProgramBagCL::CheckErrorCL(status, "CLTexImage::CopyFromHost"); -} - -int CLTexImage::GetImageDataSize() -{ - return _imgWidth * _imgHeight * _numChannel * sizeof(float); -} - -int CLTexImage::CopyToPBO(GLuint pbo) -{ - glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pbo); - - int esize = GetImageDataSize(), bsize; - glGetBufferParameteriv(GL_PIXEL_UNPACK_BUFFER_ARB, GL_BUFFER_SIZE, &bsize); - if(bsize < esize) - { - glBufferData(GL_PIXEL_UNPACK_BUFFER_ARB, esize, NULL, GL_STATIC_DRAW_ARB); - glGetBufferParameteriv(GL_PIXEL_UNPACK_BUFFER_ARB, GL_BUFFER_SIZE, &bsize); - } - if(bsize >= esize) - { - // map the buffer object into client's memory - void* ptr = glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY_ARB); - CopyToHost(ptr); - clFinish(_queue); - glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB); - } - glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0); - GlobalUtil::CheckErrorsGL("CLTexImage::CopyToPBO"); - return esize >= bsize; -} - -void CLTexImage::CopyToHost(void * buf) -{ - if(_clData == NULL) return; - cl_int status; - if(_bufferLen) - { - status = clEnqueueReadBuffer(_queue, _clData, true, 0, - _imgWidth * _imgHeight * _numChannel * sizeof(float), buf, 0, NULL, NULL); - }else - { - size_t origin[3] = {0, 0, 0}, region[3] = {_imgWidth, _imgHeight, 1}; - size_t row_pitch = _imgWidth * _numChannel * sizeof(float); - status = clEnqueueReadImage(_queue, _clData, true, origin, - region, row_pitch, 0, buf, 0, 0, 0); - } - - ProgramBagCL::CheckErrorCL(status, "CLTexImage::CopyToHost"); -} - -#endif - diff --git a/3rdparty/SiftGPU/src/SiftGPU/CLTexImage.h b/3rdparty/SiftGPU/src/SiftGPU/CLTexImage.h deleted file mode 100644 index 2897cbc8..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/CLTexImage.h +++ /dev/null @@ -1,83 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: CLTexImage.h -// Author: Changchang Wu -// Description : interface for the CLTexImage class. -// class for storing data in CUDA. -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// -#if defined(CL_SIFTGPU_ENABLED) - -#ifndef CL_TEX_IMAGE_H -#define CL_TEX_IMAGE_H - -class GLTexImage; - -class CLTexImage -{ -protected: - cl_context _context; - cl_command_queue _queue; - cl_mem _clData; - int _numChannel; - int _imgWidth; - int _imgHeight; - int _texWidth; - int _texHeight; - int _bufferLen; - int _fromGL; -private: - void ReleaseTexture(); -public: - void SetImageSize(int width, int height); - void SetPackedSize(int width, int height, int packed); - void InitBufferTex(int width, int height, int nchannel); - void InitTexture(int width, int height, int nchannel); - void InitPackedTex(int width, int height, int packed); - void InitTextureGL(GLuint tex, int width, int height, int nchannel); - void CopyToHost(void* buf); - void CopyFromHost(const void* buf); -public: - int CopyToPBO(GLuint pbo); - int GetImageDataSize(); -public: - inline operator cl_mem(){return _clData; } - inline int GetImgWidth(){return _imgWidth;} - inline int GetImgHeight(){return _imgHeight;} - inline int GetTexWidth(){return _texWidth;} - inline int GetTexHeight(){return _texHeight;} - inline int GetDataSize(){return _bufferLen;} - inline bool IsImage2D() {return _bufferLen == 0;} - inline int GetImgPixelCount(){return _imgWidth*_imgHeight;} - inline int GetTexPixelCount(){return _texWidth*_texHeight;} -public: - CLTexImage(); - CLTexImage(cl_context context, cl_command_queue queue); - void SetContext(cl_context context, cl_command_queue queue); - virtual ~CLTexImage(); - friend class ProgramCL; - friend class PyramidCL; - friend class ProgramBagCL; - friend class ProgramBagCLN; -}; - -////////////////////////////////////////////////// -//transfer OpenGL Texture to PBO, then to CUDA vector -//#endif -#endif // !defined(CU_TEX_IMAGE_H) -#endif - - diff --git a/3rdparty/SiftGPU/src/SiftGPU/CMakeLists.txt b/3rdparty/SiftGPU/src/SiftGPU/CMakeLists.txt deleted file mode 100644 index e280fe72..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/CMakeLists.txt +++ /dev/null @@ -1,50 +0,0 @@ -find_package(OpenGL REQUIRED) -find_package(GLUT REQUIRED) -find_package(GLEW) - -OPTION(SIFTGPU_ENABLE_SERVER FALSE) -SET(SIFTGPU_ENABLE_OPENCL FALSE) -SET(SIFTGPU_ENABLE_CUDA TRUE) -SET(SIFTGPU_ENABLE_SSE TRUE) -SET(SIFTGPU_SSE_OPTIONS -march=core2 -mfpmath=sse) -SET(SIFTGPU_PREFER_GLUT TRUE) -SET(SIFTGPU_DISABLE_DEVIL TRUE) - -SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -Wall -Wno-deprecated" ) - -IF(SIFTGPU_ENABLE_SSE) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=core2 -mfpmath=sse" ) -ENDIF(SIFTGPU_ENABLE_SSE) - -IF(SIFTGPU_PREFER_GLUT) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWINDOW_PREFER_GLUT" ) -ENDIF(SIFTGPU_PREFER_GLUT) - -IF(SIFTGPU_ENABLE_OPENCL) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DCL_SIFTGPU_ENABLED" ) -ENDIF(SIFTGPU_ENABLE_OPENCL) - -IF(APPLE) - SET(LIBS_SIFTGPU ) -ELSE(APPLE) - SET(LIBS_SIFTGPU glut GL X11 ) -ENDIF(APPLE) - -IF(SIFTGPU_DISABLE_DEVIL) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSIFTGPU_NO_DEVIL" ) -ELSE(SIFTGPU_DISABLE_DEVIL) - SET(LIBS_SIFTGPU ${LIBS_SIFTGPU} IL ) -ENDIF(SIFTGPU_DISABLE_DEVIL) - -if (GLEW_FOUND) - include_directories( ${GLEW_INCLUDE_DIRS} ) - SET (GLEW_LIBRARIES GLEW) -endif (GLEW_FOUND) - -ADD_LIBRARY(siftgpu STATIC FrameBufferObject.cpp GlobalUtil.cpp GLTexImage.cpp ProgramGLSL.cpp - ProgramGPU.cpp ShaderMan.cpp SiftGPU.cpp SiftPyramid.cpp PyramidGL.cpp SiftMatch.cpp) - -TARGET_LINK_LIBRARIES(siftgpu ${LIBS_SIFTGPU} ${GLEW_LIBRARIES} ${GLUT_LIBRARIES} ${OPENGL_LIBRARIES} ) - -INSTALL(FILES SiftGPU.h DESTINATION include) -INSTALL(TARGETS siftgpu DESTINATION lib ) diff --git a/3rdparty/SiftGPU/src/SiftGPU/CuTexImage.cpp b/3rdparty/SiftGPU/src/SiftGPU/CuTexImage.cpp deleted file mode 100644 index c8feeb74..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/CuTexImage.cpp +++ /dev/null @@ -1,259 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: CuTexImage.cpp -// Author: Changchang Wu -// Description : implementation of the CuTexImage class. -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - -#if defined(CUDA_SIFTGPU_ENABLED) - -#include "GL/glew.h" -#include <iostream> -#include <vector> -#include <algorithm> -#include <stdlib.h> -#include <math.h> -using namespace std; - - -#include <cuda.h> -#include <cuda_runtime_api.h> -#include <cuda_gl_interop.h> - -#include "GlobalUtil.h" -#include "GLTexImage.h" -#include "CuTexImage.h" -#include "ProgramCU.h" - -#if CUDA_VERSION <= 2010 && defined(SIFTGPU_ENABLE_LINEAR_TEX2D) -#error "Require CUDA 2.2 or higher" -#endif - - -CuTexImage::CuTexImage() -{ - _cuData = NULL; - _cuData2D = NULL; - _fromPBO = 0; - _numChannel = _numBytes = 0; - _imgWidth = _imgHeight = _texWidth = _texHeight = 0; -} - -CuTexImage::CuTexImage(int width, int height, int nchannel, GLuint pbo) -{ - _cuData = NULL; - - //check size of pbo - GLint bsize, esize = width * height * nchannel * sizeof(float); - glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, pbo); - glGetBufferParameteriv(GL_PIXEL_PACK_BUFFER_ARB, GL_BUFFER_SIZE, &bsize); - if(bsize < esize) - { - glBufferData(GL_PIXEL_PACK_BUFFER_ARB, esize, NULL, GL_STATIC_DRAW_ARB); - glGetBufferParameteriv(GL_PIXEL_PACK_BUFFER_ARB, GL_BUFFER_SIZE, &bsize); - } - glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0); - if(bsize >=esize) - { - - cudaGLRegisterBufferObject(pbo); - cudaGLMapBufferObject(&_cuData, pbo); - ProgramCU::CheckErrorCUDA("cudaGLMapBufferObject"); - _fromPBO = pbo; - }else - { - _cuData = NULL; - _fromPBO = 0; - } - if(_cuData) - { - _numBytes = bsize; - _imgWidth = width; - _imgHeight = height; - _numChannel = nchannel; - }else - { - _numBytes = 0; - _imgWidth = 0; - _imgHeight = 0; - _numChannel = 0; - } - - _texWidth = _texHeight =0; - - _cuData2D = NULL; -} - -CuTexImage::~CuTexImage() -{ - - - if(_fromPBO) - { - cudaGLUnmapBufferObject(_fromPBO); - cudaGLUnregisterBufferObject(_fromPBO); - }else if(_cuData) - { - cudaFree(_cuData); - } - if(_cuData2D) cudaFreeArray(_cuData2D); -} - -void CuTexImage::SetImageSize(int width, int height) -{ - _imgWidth = width; - _imgHeight = height; -} - -void CuTexImage::InitTexture(int width, int height, int nchannel) -{ - int size; - _imgWidth = width; - _imgHeight = height; - _numChannel = min(max(nchannel, 1), 4); - - size = width * height * _numChannel * sizeof(float); - - if(size <= _numBytes) return; - - if(_cuData) cudaFree(_cuData); - - //allocate the array data - cudaMalloc(&_cuData, _numBytes = size); - -#ifdef _DEBUG - ProgramCU::CheckErrorCUDA("CuTexImage::InitTexture"); -#endif -} - -void CuTexImage::CopyFromHost(const void * buf) -{ - if(_cuData == NULL) return; - cudaMemcpy( _cuData, buf, _imgWidth * _imgHeight * _numChannel * sizeof(float), cudaMemcpyHostToDevice); -} - -void CuTexImage::CopyToHost(void * buf) -{ - if(_cuData == NULL) return; - cudaMemcpy(buf, _cuData, _imgWidth * _imgHeight * _numChannel * sizeof(float), cudaMemcpyDeviceToHost); -} - -void CuTexImage::CopyToHost(void * buf, int stream) -{ - if(_cuData == NULL) return; - cudaMemcpyAsync(buf, _cuData, _imgWidth * _imgHeight * _numChannel * sizeof(float), cudaMemcpyDeviceToHost, (cudaStream_t)stream); -} - -void CuTexImage::InitTexture2D() -{ -#if !defined(SIFTGPU_ENABLE_LINEAR_TEX2D) - if(_cuData2D && (_texWidth < _imgWidth || _texHeight < _imgHeight)) - { - cudaFreeArray(_cuData2D); - _cuData2D = NULL; - } - - if(_cuData2D == NULL) - { - _texWidth = max(_texWidth, _imgWidth); - _texHeight = max(_texHeight, _imgHeight); - cudaChannelFormatDesc desc; - desc.f = cudaChannelFormatKindFloat; - desc.x = sizeof(float) * 8; - desc.y = _numChannel >=2 ? sizeof(float) * 8 : 0; - desc.z = _numChannel >=3 ? sizeof(float) * 8 : 0; - desc.w = _numChannel >=4 ? sizeof(float) * 8 : 0; - cudaMallocArray(&_cuData2D, &desc, _texWidth, _texHeight); - ProgramCU::CheckErrorCUDA("cudaMallocArray"); - } -#endif -} - -void CuTexImage::CopyToTexture2D() -{ -#if !defined(SIFTGPU_ENABLE_LINEAR_TEX2D) - InitTexture2D(); - - if(_cuData2D) - { - cudaMemcpy2DToArray(_cuData2D, 0, 0, _cuData, _imgWidth* _numChannel* sizeof(float) , - _imgWidth * _numChannel*sizeof(float), _imgHeight, cudaMemcpyDeviceToDevice); - ProgramCU::CheckErrorCUDA("cudaMemcpy2DToArray"); - } -#endif - -} - -int CuTexImage::DebugCopyToTexture2D() -{ - -/* CuTexImage tex; - float data1[2][3] = {{1, 2, 5}, {3, 4, 5}}, data2[2][5]; - tex.InitTexture(3, 2, 1); - cudaMemcpy(tex._cuData, data1[0], 6 * sizeof(float), cudaMemcpyHostToDevice); - cudaMemcpy(data1, tex._cuData, 4 * sizeof(float) , cudaMemcpyDeviceToHost); - tex._texWidth =5; tex._texHeight = 2; - tex.CopyToTexture2D(); - cudaMemcpyFromArray(data2[0], tex._cuData2D, 0, 0, 10 * sizeof(float), cudaMemcpyDeviceToHost);*/ - - return 1; -} - - - -void CuTexImage::CopyFromPBO(int width, int height, GLuint pbo) -{ - void* pbuf =NULL; - GLint esize = width * height * sizeof(float); - cudaGLRegisterBufferObject(pbo); - cudaGLMapBufferObject(&pbuf, pbo); - - cudaMemcpy(_cuData, pbuf, esize, cudaMemcpyDeviceToDevice); - - cudaGLUnmapBufferObject(pbo); - cudaGLUnregisterBufferObject(pbo); -} - -int CuTexImage::CopyToPBO(GLuint pbo) -{ - void* pbuf =NULL; - GLint bsize, esize = _imgWidth * _imgHeight * sizeof(float) * _numChannel; - glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, pbo); - glGetBufferParameteriv(GL_PIXEL_PACK_BUFFER_ARB, GL_BUFFER_SIZE, &bsize); - if(bsize < esize) - { - glBufferData(GL_PIXEL_PACK_BUFFER_ARB, esize*3/2, NULL, GL_STATIC_DRAW_ARB); - glGetBufferParameteriv(GL_PIXEL_PACK_BUFFER_ARB, GL_BUFFER_SIZE, &bsize); - } - glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0); - - if(bsize >= esize) - { - cudaGLRegisterBufferObject(pbo); - cudaGLMapBufferObject(&pbuf, pbo); - cudaMemcpy(pbuf, _cuData, esize, cudaMemcpyDeviceToDevice); - cudaGLUnmapBufferObject(pbo); - cudaGLUnregisterBufferObject(pbo); - return 1; - }else - { - return 0; - } -} - -#endif - diff --git a/3rdparty/SiftGPU/src/SiftGPU/CuTexImage.h b/3rdparty/SiftGPU/src/SiftGPU/CuTexImage.h deleted file mode 100644 index 6785c8d7..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/CuTexImage.h +++ /dev/null @@ -1,76 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: CuTexImage.h -// Author: Changchang Wu -// Description : interface for the CuTexImage class. -// class for storing data in CUDA. -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - - -#ifndef CU_TEX_IMAGE_H -#define CU_TEX_IMAGE_H - -class GLTexImage; -struct cudaArray; -struct textureReference; - -//using texture2D from linear memory - -#define SIFTGPU_ENABLE_LINEAR_TEX2D - -class CuTexImage -{ -protected: - void* _cuData; - cudaArray* _cuData2D; - int _numChannel; - int _numBytes; - int _imgWidth; - int _imgHeight; - int _texWidth; - int _texHeight; - GLuint _fromPBO; -public: - virtual void SetImageSize(int width, int height); - virtual void InitTexture(int width, int height, int nchannel = 1); - void InitTexture2D(); - inline void BindTexture(textureReference& texRef); - inline void BindTexture2D(textureReference& texRef); - void CopyToTexture2D(); - void CopyToHost(void* buf); - void CopyToHost(void* buf, int stream); - void CopyFromHost(const void* buf); - int CopyToPBO(GLuint pbo); - void CopyFromPBO(int width, int height, GLuint pbo); - static int DebugCopyToTexture2D(); -public: - inline int GetImgWidth(){return _imgWidth;} - inline int GetImgHeight(){return _imgHeight;} - inline int GetDataSize(){return _numBytes;} -public: - CuTexImage(); - CuTexImage(int width, int height, int nchannel, GLuint pbo); - virtual ~CuTexImage(); - friend class ProgramCU; - friend class PyramidCU; -}; - -////////////////////////////////////////////////// -//transfer OpenGL Texture to PBO, then to CUDA vector -//#endif -#endif // !defined(CU_TEX_IMAGE_H) - diff --git a/3rdparty/SiftGPU/src/SiftGPU/FrameBufferObject.cpp b/3rdparty/SiftGPU/src/SiftGPU/FrameBufferObject.cpp deleted file mode 100644 index b586d9b0..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/FrameBufferObject.cpp +++ /dev/null @@ -1,105 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: FrameBufferObject.cpp -// Author: Changchang Wu -// Description : Implementation of FrameBufferObject Class -// -// -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - - -#include "GL/glew.h" -#include <stdlib.h> -#include "GlobalUtil.h" -#include "FrameBufferObject.h" - -//whether use only one FBO globally -int FrameBufferObject::UseSingleFBO=1; -GLuint FrameBufferObject::GlobalFBO=0; - -////////////////////////////////////////////////////////////////////// -// Construction/Destruction -////////////////////////////////////////////////////////////////////// - -FrameBufferObject::FrameBufferObject(int autobind) -{ - if(UseSingleFBO && GlobalFBO) - { - _fboID = GlobalFBO; - }else - { - glGenFramebuffersEXT(1, &_fboID); - if(UseSingleFBO )GlobalFBO = _fboID; - } - if(autobind ) glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _fboID); -} - -FrameBufferObject::~FrameBufferObject() -{ - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - if(!UseSingleFBO ) - { - glDeleteFramebuffersEXT (1,&_fboID); - } -} - -void FrameBufferObject::DeleteGlobalFBO() -{ - if(UseSingleFBO) - { - glDeleteFramebuffersEXT (1,&GlobalFBO); - GlobalFBO = 0; - } -} - -void FrameBufferObject::AttachDepthTexture(GLenum textureTarget, GLuint texID) -{ - - glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, textureTarget, texID, 0); -} - -void FrameBufferObject::AttachTexture(GLenum textureTarget, GLenum attachment, GLuint texId) -{ - glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, attachment, textureTarget, texId, 0); -} - -void FrameBufferObject::BindFBO() -{ - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _fboID); -} - -void FrameBufferObject::UnbindFBO() -{ - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); -} - -void FrameBufferObject::UnattachTex(GLenum attachment) -{ - glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, attachment, GL_TEXTURE_2D, 0, 0 ); -} - -void FrameBufferObject::AttachRenderBuffer(GLenum attachment, GLuint buffID) -{ - glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, attachment, GL_RENDERBUFFER_EXT, buffID); - -} - -void FrameBufferObject:: UnattachRenderBuffer(GLenum attachment) -{ - glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, attachment, GL_RENDERBUFFER_EXT, 0); -} - diff --git a/3rdparty/SiftGPU/src/SiftGPU/FrameBufferObject.h b/3rdparty/SiftGPU/src/SiftGPU/FrameBufferObject.h deleted file mode 100644 index e4cc5263..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/FrameBufferObject.h +++ /dev/null @@ -1,49 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: FrameBufferObject.h -// Author: Changchang Wu -// Description : interface for the FrameBufferObject class. -// -// -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - - -#if !defined(_FRAME_BUFFER_OBJECT_H) -#define _FRAME_BUFFER_OBJECT_H - -class FrameBufferObject -{ - static GLuint GlobalFBO; //not thread-safe - GLuint _fboID; -public: - static int UseSingleFBO; -public: - static void DeleteGlobalFBO(); - static void UnattachTex(GLenum attachment); - static void UnbindFBO(); - static void AttachDepthTexture(GLenum textureTarget, GLuint texID); - static void AttachTexture( GLenum textureTarget, GLenum attachment, GLuint texID); - static void AttachRenderBuffer(GLenum attachment, GLuint buffID ); - static void UnattachRenderBuffer(GLenum attachment); -public: - void BindFBO(); - FrameBufferObject(int autobind = 1); - ~FrameBufferObject(); - -}; - -#endif diff --git a/3rdparty/SiftGPU/src/SiftGPU/GLTexImage.cpp b/3rdparty/SiftGPU/src/SiftGPU/GLTexImage.cpp deleted file mode 100644 index a6a28098..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/GLTexImage.cpp +++ /dev/null @@ -1,1264 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: GLTexImage.cpp -// Author: Changchang Wu -// Description : implementation of the GLTexImage class. -// -// -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - - -#include "GL/glew.h" -#include <stdio.h> -#include <iostream> -#include <fstream> -#include <vector> -#include <algorithm> -#include <stdlib.h> -#include <math.h> -using namespace std; - - - -#include "GlobalUtil.h" - -#include "GLTexImage.h" -#include "FrameBufferObject.h" -#include "ShaderMan.h" - - -//#define SIFTGPU_NO_DEVIL - -#ifndef SIFTGPU_NO_DEVIL - #include "IL/il.h" - #if defined(_WIN64) - #pragma comment(lib, "../../lib/DevIL64.lib") - #elif defined(_WIN32) - #pragma comment(lib, "../../lib/DevIL.lib") - #endif -#else - #include <string.h> -#endif -////////////////////////////////////////////////////////////////////// -// Construction/Destruction -////////////////////////////////////////////////////////////////////// - - -GLTexImage::GLTexImage() -{ - _imgWidth = _imgHeight = 0; - _texWidth = _texHeight = 0; - _drawWidth = _drawHeight = 0; - _texID = 0; - -} - -GLTexImage::~GLTexImage() -{ - if(_texID) glDeleteTextures(1, &_texID); -} - -int GLTexImage::CheckTexture() -{ - if(_texID) - { - GLint tw, th; - BindTex(); - glGetTexLevelParameteriv(_texTarget, 0, GL_TEXTURE_WIDTH , &tw); - glGetTexLevelParameteriv(_texTarget, 0, GL_TEXTURE_HEIGHT , &th); - UnbindTex(); - return tw == _texWidth && th == _texHeight; - }else - { - return _texWidth == 0 && _texHeight ==0; - - } -} -//set a dimension that is smaller than the actuall size -//for drawQuad -void GLTexImage::SetImageSize( int width, int height) -{ - _drawWidth = _imgWidth = width; - _drawHeight = _imgHeight = height; -} - -void GLTexImage::InitTexture( int width, int height, int clamp_to_edge) -{ - - if(_texID && width == _texWidth && height == _texHeight ) return; - if(_texID==0) glGenTextures(1, &_texID); - - _texWidth = _imgWidth = _drawWidth = width; - _texHeight = _imgHeight = _drawHeight = height; - - BindTex(); - - if(clamp_to_edge) - { - glTexParameteri (_texTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri (_texTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - }else - { - //out of bound tex read returns 0?? - glTexParameteri (_texTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - glTexParameteri (_texTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); - } - glTexParameteri(_texTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(_texTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - - glTexImage2D(_texTarget, 0, _iTexFormat, - _texWidth, _texHeight, 0, GL_RGBA, GL_FLOAT, NULL); - CheckErrorsGL("glTexImage2D"); - - - UnbindTex(); - -} - - -void GLTexImage::InitTexture( int width, int height, int clamp_to_edge, GLuint format) -{ - - if(_texID && width == _texWidth && height == _texHeight ) return; - if(_texID==0) glGenTextures(1, &_texID); - - _texWidth = _imgWidth = _drawWidth = width; - _texHeight = _imgHeight = _drawHeight = height; - - BindTex(); - - if(clamp_to_edge) - { - glTexParameteri (_texTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri (_texTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - }else - { - //out of bound tex read returns 0?? - glTexParameteri (_texTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - glTexParameteri (_texTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); - } - glTexParameteri(_texTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(_texTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - - glTexImage2D(_texTarget, 0, format, _texWidth, _texHeight, 0, GL_RGBA, GL_FLOAT, NULL); - - UnbindTex(); - -} -void GLTexImage::BindTex() -{ - glBindTexture(_texTarget, _texID); -} - -void GLTexImage::UnbindTex() -{ - glBindTexture(_texTarget, 0); -} - - -void GLTexImage::DrawQuad() -{ - glBegin (GL_QUADS); - glTexCoord2i ( 0 , 0 ); glVertex2i ( 0 , 0 ); - glTexCoord2i ( 0 , _drawHeight ); glVertex2i ( 0 , _drawHeight ); - glTexCoord2i ( _drawWidth , _drawHeight ); glVertex2i ( _drawWidth , _drawHeight ); - glTexCoord2i ( _drawWidth , 0 ); glVertex2i ( _drawWidth , 0 ); - glEnd (); - glFlush(); -} - -void GLTexImage::FillMargin(int marginx, int marginy) -{ - // - marginx = min(marginx, _texWidth - _imgWidth); - marginy = min(marginy, _texHeight - _imgHeight); - if(marginx >0 || marginy > 0) - { - GlobalUtil::FitViewPort(_imgWidth + marginx, _imgHeight + marginy); - AttachToFBO(0); - BindTex(); - ShaderMan::UseShaderMarginCopy(_imgWidth, _imgHeight); - DrawMargin(_imgWidth + marginx, _imgHeight + marginy); - } -} - -void GLTexImage::ZeroHistoMargin() -{ - ZeroHistoMargin(_imgWidth, _imgHeight); -} - -void GLTexImage::ZeroHistoMargin(int width, int height) -{ - int marginx = width & 0x01; - int marginy = height & 0x01; - if(marginx >0 || marginy > 0) - { - int right = width + marginx; - int bottom = height + marginy; - GlobalUtil::FitViewPort(right, bottom); - AttachToFBO(0); - ShaderMan::UseShaderZeroPass(); - glBegin(GL_QUADS); - if(right > width && _texWidth > width) - { - glTexCoord2i ( width , 0 ); glVertex2i ( width , 0 ); - glTexCoord2i ( width , bottom ); glVertex2i ( width , bottom ); - glTexCoord2i ( right , bottom ); glVertex2i ( right , bottom ); - glTexCoord2i ( right , 0 ); glVertex2i ( right , 0 ); - } - if(bottom>height && _texHeight > height) - { - glTexCoord2i ( 0 , height ); glVertex2i ( 0 , height ); - glTexCoord2i ( 0 , bottom ); glVertex2i ( 0 , bottom ); - glTexCoord2i ( width , bottom ); glVertex2i ( width , bottom ); - glTexCoord2i ( width , height ); glVertex2i ( width , height ); - } - glEnd(); - glFlush(); - } - -} - -void GLTexImage::DrawMargin(int right, int bottom) -{ - glBegin(GL_QUADS); - if(right > _drawWidth) - { - glTexCoord2i ( _drawWidth , 0 ); glVertex2i ( _drawWidth , 0 ); - glTexCoord2i ( _drawWidth , bottom ); glVertex2i ( _drawWidth , bottom ); - glTexCoord2i ( right , bottom ); glVertex2i ( right , bottom ); - glTexCoord2i ( right , 0 ); glVertex2i ( right , 0 ); - } - if(bottom>_drawHeight) - { - glTexCoord2i ( 0 , _drawHeight ); glVertex2i ( 0 , _drawHeight ); - glTexCoord2i ( 0 , bottom ); glVertex2i ( 0 , bottom ); - glTexCoord2i ( _drawWidth , bottom ); glVertex2i ( _drawWidth , bottom ); - glTexCoord2i ( _drawWidth , _drawHeight ); glVertex2i ( _drawWidth , _drawHeight ); - } - glEnd(); - glFlush(); - - -} - - -void GLTexImage::DrawQuadMT4() -{ - int w = _drawWidth, h = _drawHeight; - glBegin (GL_QUADS); - glMultiTexCoord2i( GL_TEXTURE0, 0 , 0 ); - glMultiTexCoord2i( GL_TEXTURE1, -1 , 0 ); - glMultiTexCoord2i( GL_TEXTURE2, 1 , 0 ); - glMultiTexCoord2i( GL_TEXTURE3, 0 , -1 ); - glMultiTexCoord2i( GL_TEXTURE4, 0 , 1 ); - glVertex2i ( 0 , 0 ); - - glMultiTexCoord2i( GL_TEXTURE0, 0 , h ); - glMultiTexCoord2i( GL_TEXTURE1, -1 , h ); - glMultiTexCoord2i( GL_TEXTURE2, 1 , h ); - glMultiTexCoord2i( GL_TEXTURE3, 0 , h -1 ); - glMultiTexCoord2i( GL_TEXTURE4, 0 , h +1 ); - glVertex2i ( 0 , h ); - - - glMultiTexCoord2i( GL_TEXTURE0, w , h ); - glMultiTexCoord2i( GL_TEXTURE1, w-1 , h ); - glMultiTexCoord2i( GL_TEXTURE2, w+1 , h ); - glMultiTexCoord2i( GL_TEXTURE3, w , h-1 ); - glMultiTexCoord2i( GL_TEXTURE4, w , h+1 ); - glVertex2i ( w , h ); - - glMultiTexCoord2i( GL_TEXTURE0, w , 0 ); - glMultiTexCoord2i( GL_TEXTURE1, w-1 , 0 ); - glMultiTexCoord2i( GL_TEXTURE2, w+1 , 0 ); - glMultiTexCoord2i( GL_TEXTURE3, w , -1 ); - glMultiTexCoord2i( GL_TEXTURE4, w , 1 ); - glVertex2i ( w , 0 ); - glEnd (); - glFlush(); -} - - -void GLTexImage::DrawQuadMT8() -{ - int w = _drawWidth; - int h = _drawHeight; - glBegin (GL_QUADS); - glMultiTexCoord2i( GL_TEXTURE0, 0 , 0 ); - glMultiTexCoord2i( GL_TEXTURE1, -1 , 0 ); - glMultiTexCoord2i( GL_TEXTURE2, 1 , 0 ); - glMultiTexCoord2i( GL_TEXTURE3, 0 , -1 ); - glMultiTexCoord2i( GL_TEXTURE4, 0 , 1 ); - glMultiTexCoord2i( GL_TEXTURE5, -1 , -1 ); - glMultiTexCoord2i( GL_TEXTURE6, -1 , 1 ); - glMultiTexCoord2i( GL_TEXTURE7, 1 , -1 ); - glVertex2i ( 0 , 0 ); - - glMultiTexCoord2i( GL_TEXTURE0, 0 , h ); - glMultiTexCoord2i( GL_TEXTURE1, -1 , h ); - glMultiTexCoord2i( GL_TEXTURE2, 1 , h ); - glMultiTexCoord2i( GL_TEXTURE3, 0 , h -1 ); - glMultiTexCoord2i( GL_TEXTURE4, 0 , h +1 ); - glMultiTexCoord2i( GL_TEXTURE5, -1 , h -1 ); - glMultiTexCoord2i( GL_TEXTURE6, -1 , h +1 ); - glMultiTexCoord2i( GL_TEXTURE7, 1 , h -1 ); - glVertex2i ( 0 , h ); - - - glMultiTexCoord2i( GL_TEXTURE0, w , h ); - glMultiTexCoord2i( GL_TEXTURE1, w-1 , h ); - glMultiTexCoord2i( GL_TEXTURE2, w+1 , h ); - glMultiTexCoord2i( GL_TEXTURE3, w , h -1 ); - glMultiTexCoord2i( GL_TEXTURE4, w , h +1 ); - glMultiTexCoord2i( GL_TEXTURE5, w-1 , h -1 ); - glMultiTexCoord2i( GL_TEXTURE6, w-1 , h +1 ); - glMultiTexCoord2i( GL_TEXTURE7, w+1 , h -1 ); - glVertex2i ( w , h ); - - glMultiTexCoord2i( GL_TEXTURE0, w , 0 ); - glMultiTexCoord2i( GL_TEXTURE1, w-1 , 0 ); - glMultiTexCoord2i( GL_TEXTURE2, w+1 , 0 ); - glMultiTexCoord2i( GL_TEXTURE3, w , -1 ); - glMultiTexCoord2i( GL_TEXTURE4, w , 1 ); - glMultiTexCoord2i( GL_TEXTURE5, w-1 , -1 ); - glMultiTexCoord2i( GL_TEXTURE6, w-1 , 1 ); - glMultiTexCoord2i( GL_TEXTURE7, w+1 , -1 ); - glVertex2i ( w , 0 ); - glEnd (); - glFlush(); -} - - - - -void GLTexImage::DrawImage() -{ - DrawQuad(); -} - - - -void GLTexImage::FitTexViewPort() -{ - GlobalUtil::FitViewPort(_drawWidth, _drawHeight); -} - -void GLTexImage::FitRealTexViewPort() -{ - GlobalUtil::FitViewPort(_texWidth, _texHeight); -} - -void GLTexImage::AttachToFBO(int i) -{ - glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, i+GL_COLOR_ATTACHMENT0_EXT, _texTarget, _texID, 0 ); -} - -void GLTexImage::DetachFBO(int i) -{ - glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, i+GL_COLOR_ATTACHMENT0_EXT, _texTarget, 0, 0 ); -} - - -void GLTexImage::DrawQuad(float x1, float x2, float y1, float y2) -{ - - glBegin (GL_QUADS); - glTexCoord2f ( x1 , y1 ); glVertex2f ( x1 , y1 ); - glTexCoord2f ( x1 , y2 ); glVertex2f ( x1 , y2 ); - glTexCoord2f ( x2 , y2 ); glVertex2f ( x2 , y2 ); - glTexCoord2f ( x2 , y1 ); glVertex2f ( x2 , y1 ); - glEnd (); - glFlush(); -} - -void GLTexImage::TexConvertRGB() -{ - //change 3/22/09 - FrameBufferObject fbo; - //GlobalUtil::FitViewPort(1, 1); - FitTexViewPort(); - - AttachToFBO(0); - ShaderMan::UseShaderRGB2Gray(); - glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); - DrawQuad(); - ShaderMan::UnloadProgram(); - DetachFBO(0); -} - -void GLTexImage::DrawQuadDS(int scale) -{ - DrawScaledQuad(float(scale)); -} - -void GLTexImage::DrawQuadUS(int scale) -{ - DrawScaledQuad(1.0f/scale); -} - -void GLTexImage::DrawScaledQuad(float texscale) -{ - - ////the texture coordinate for 0.5 is to + 0.5*texscale - float to = 0.5f -0.5f * texscale; - float tx = _imgWidth*texscale +to; - float ty = _imgHeight*texscale +to; - glBegin (GL_QUADS); - glTexCoord2f ( to , to ); glVertex2i ( 0 , 0 ); - glTexCoord2f ( to , ty ); glVertex2i ( 0 , _imgHeight ); - glTexCoord2f ( tx , ty ); glVertex2i ( _imgWidth , _imgHeight ); - glTexCoord2f ( tx , to ); glVertex2i ( _imgWidth , 0 ); - glEnd (); - glFlush(); -} - - -void GLTexImage::DrawQuadReduction(int w , int h) -{ - float to = -0.5f; - float tx = w*2 +to; - float ty = h*2 +to; - glBegin (GL_QUADS); - glMultiTexCoord2f ( GL_TEXTURE0, to , to ); - glMultiTexCoord2f ( GL_TEXTURE1, to +1, to ); - glMultiTexCoord2f ( GL_TEXTURE2, to , to+1 ); - glMultiTexCoord2f ( GL_TEXTURE3, to +1, to+1 ); - glVertex2i ( 0 , 0 ); - - glMultiTexCoord2f ( GL_TEXTURE0, to , ty ); - glMultiTexCoord2f ( GL_TEXTURE1, to +1, ty ); - glMultiTexCoord2f ( GL_TEXTURE2, to , ty +1 ); - glMultiTexCoord2f ( GL_TEXTURE3, to +1, ty +1 ); - glVertex2i ( 0 , h ); - - glMultiTexCoord2f ( GL_TEXTURE0, tx , ty ); - glMultiTexCoord2f ( GL_TEXTURE1, tx +1, ty ); - glMultiTexCoord2f ( GL_TEXTURE2, tx , ty +1); - glMultiTexCoord2f ( GL_TEXTURE3, tx +1, ty +1); - - glVertex2i ( w , h ); - - glMultiTexCoord2f ( GL_TEXTURE0, tx , to ); - glMultiTexCoord2f ( GL_TEXTURE1, tx +1, to ); - glMultiTexCoord2f ( GL_TEXTURE2, tx , to +1 ); - glMultiTexCoord2f ( GL_TEXTURE3, tx +1, to +1 ); - glVertex2i ( w , 0 ); - glEnd (); - - glFlush(); -} - - -void GLTexImage::DrawQuadReduction() -{ - float to = -0.5f; - float tx = _drawWidth*2 +to; - float ty = _drawHeight*2 +to; - glBegin (GL_QUADS); - glMultiTexCoord2f ( GL_TEXTURE0, to , to ); - glMultiTexCoord2f ( GL_TEXTURE1, to +1, to ); - glMultiTexCoord2f ( GL_TEXTURE2, to , to+1 ); - glMultiTexCoord2f ( GL_TEXTURE3, to +1, to+1 ); - glVertex2i ( 0 , 0 ); - - glMultiTexCoord2f ( GL_TEXTURE0, to , ty ); - glMultiTexCoord2f ( GL_TEXTURE1, to +1, ty ); - glMultiTexCoord2f ( GL_TEXTURE2, to , ty +1 ); - glMultiTexCoord2f ( GL_TEXTURE3, to +1, ty +1 ); - glVertex2i ( 0 , _drawHeight ); - - glMultiTexCoord2f ( GL_TEXTURE0, tx , ty ); - glMultiTexCoord2f ( GL_TEXTURE1, tx +1, ty ); - glMultiTexCoord2f ( GL_TEXTURE2, tx , ty +1); - glMultiTexCoord2f ( GL_TEXTURE3, tx +1, ty +1); - - glVertex2i ( _drawWidth , _drawHeight ); - - glMultiTexCoord2f ( GL_TEXTURE0, tx , to ); - glMultiTexCoord2f ( GL_TEXTURE1, tx +1, to ); - glMultiTexCoord2f ( GL_TEXTURE2, tx , to +1 ); - glMultiTexCoord2f ( GL_TEXTURE3, tx +1, to +1 ); - glVertex2i ( _drawWidth , 0 ); - glEnd (); - - glFlush(); -} - -void GLTexPacked::TexConvertRGB() -{ - //update the actual size of daw area - _drawWidth = (1 + _imgWidth) >> 1; - _drawHeight = (1 + _imgHeight) >> 1; - /// - FrameBufferObject fbo; - GLuint oldTexID = _texID; - glGenTextures(1, &_texID); - glBindTexture(_texTarget, _texID); - glTexImage2D(_texTarget, 0, _iTexFormat, _texWidth, _texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - - //input - glBindTexture(_texTarget, oldTexID); - //output - AttachToFBO(0); - //program - ShaderMan::UseShaderRGB2Gray(); - //draw buffer - glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); - //run - DrawQuadDS(2); - ShaderMan::UnloadProgram(); - - glDeleteTextures(1, &oldTexID); - DetachFBO(0); -} - - -void GLTexPacked::SetImageSize( int width, int height) -{ - _imgWidth = width; _drawWidth = (width + 1) >> 1; - _imgHeight = height; _drawHeight = (height + 1) >> 1; -} - -void GLTexPacked::InitTexture( int width, int height, int clamp_to_edge) -{ - - if(_texID && width == _imgWidth && height == _imgHeight ) return; - if(_texID==0) glGenTextures(1, &_texID); - - _imgWidth = width; - _imgHeight = height; - if(GlobalUtil::_PreciseBorder) - { - _texWidth = (width + 2) >> 1; - _texHeight = (height + 2) >> 1; - }else - { - _texWidth = (width + 1) >> 1; - _texHeight = (height + 1) >> 1; - } - _drawWidth = (width + 1) >> 1; - _drawHeight = (height + 1) >> 1; - - BindTex(); - - if(clamp_to_edge) - { - glTexParameteri (_texTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri (_texTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - }else - { - //out of bound tex read returns 0?? - glTexParameteri (_texTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - glTexParameteri (_texTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); - } - glTexParameteri(_texTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(_texTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - - glTexImage2D(_texTarget, 0, _iTexFormat, - _texWidth, _texHeight, 0, GL_RGBA, GL_FLOAT, NULL); - - UnbindTex(); - -} - - -void GLTexPacked::DrawImage() -{ - float x1 =0, y1 = 0; //border.. - float x2 = _imgWidth*0.5f +x1; - float y2 = _imgHeight*0.5f + y1; - glBegin (GL_QUADS); - glTexCoord2f ( x1 , y1 ); glVertex2i ( 0 , 0 ); - glTexCoord2f ( x1 , y2 ); glVertex2i ( 0 , _imgHeight ); - glTexCoord2f ( x2 , y2 ); glVertex2i ( _imgWidth , _imgHeight ); - glTexCoord2f ( x2 , y1 ); glVertex2i ( _imgWidth , 0 ); - glEnd (); - glFlush(); -} - -void GLTexPacked::DrawQuadUS(int scale) -{ - int tw =_drawWidth, th = _drawHeight; - float texscale = 1.0f / scale; - float x1 = 0.5f - 0.5f*scale, y1 = x1; - float x2 = tw * texscale + x1; - float y2 = th * texscale + y1; - float step = texscale *0.5f; - glBegin (GL_QUADS); - glMultiTexCoord2f( GL_TEXTURE0, x1 , y1 ); - glMultiTexCoord2f( GL_TEXTURE1, x1+step , y1 ); - glMultiTexCoord2f( GL_TEXTURE2, x1 , y1 +step); - glMultiTexCoord2f( GL_TEXTURE3, x1+step , y1 +step); - glVertex2i ( 0 , 0 ); - - glMultiTexCoord2f( GL_TEXTURE0, x1 , y2 ); - glMultiTexCoord2f( GL_TEXTURE1, x1+step , y2 ); - glMultiTexCoord2f( GL_TEXTURE2, x1 , y2 +step); - glMultiTexCoord2f( GL_TEXTURE3, x1+step , y2 +step); - glVertex2i ( 0 , th ); - - glMultiTexCoord2f( GL_TEXTURE0, x2 , y2 ); - glMultiTexCoord2f( GL_TEXTURE1, x2+step , y2 ); - glMultiTexCoord2f( GL_TEXTURE2, x2 , y2 +step); - glMultiTexCoord2f( GL_TEXTURE3, x2+step , y2 +step); - glVertex2i ( tw , th ); - - glMultiTexCoord2f( GL_TEXTURE0, x2 , y1 ); - glMultiTexCoord2f( GL_TEXTURE1, x2+step , y1 ); - glMultiTexCoord2f( GL_TEXTURE2, x2 , y1 +step); - glMultiTexCoord2f( GL_TEXTURE3, x2+step , y1 +step); - glVertex2i ( tw , 0 ); - glEnd (); -} - -void GLTexPacked::DrawQuadDS(int scale) -{ - int tw = _drawWidth; - int th = _drawHeight; - float x1 = 0.5f - 0.5f*scale; - float x2 = tw * scale + x1; - float y1 = 0.5f - 0.5f * scale; - float y2 = th * scale + y1; - int step = scale / 2; - - glBegin (GL_QUADS); - glMultiTexCoord2f( GL_TEXTURE0, x1 , y1 ); - glMultiTexCoord2f( GL_TEXTURE1, x1+step , y1 ); - glMultiTexCoord2f( GL_TEXTURE2, x1 , y1 +step); - glMultiTexCoord2f( GL_TEXTURE3, x1+step , y1 +step); - glVertex2i ( 0 , 0 ); - - glMultiTexCoord2f( GL_TEXTURE0, x1 , y2 ); - glMultiTexCoord2f( GL_TEXTURE1, x1+step , y2 ); - glMultiTexCoord2f( GL_TEXTURE2, x1 , y2 +step); - glMultiTexCoord2f( GL_TEXTURE3, x1+step , y2 +step); - glVertex2i ( 0 , th ); - - glMultiTexCoord2f( GL_TEXTURE0, x2 , y2 ); - glMultiTexCoord2f( GL_TEXTURE1, x2+step , y2 ); - glMultiTexCoord2f( GL_TEXTURE2, x2 , y2 +step); - glMultiTexCoord2f( GL_TEXTURE3, x2+step , y2 +step); - glVertex2i ( tw , th ); - - glMultiTexCoord2f( GL_TEXTURE0, x2 , y1 ); - glMultiTexCoord2f( GL_TEXTURE1, x2+step , y1 ); - glMultiTexCoord2f( GL_TEXTURE2, x2 , y1 +step); - glMultiTexCoord2f( GL_TEXTURE3, x2+step , y1 +step); - glVertex2i ( tw , 0 ); - glEnd (); -} - -void GLTexPacked::ZeroHistoMargin() -{ - int marginx = (((_imgWidth + 3) /4)*4) - _imgWidth; - int marginy = (((-_imgHeight + 3)/4)*4) - _imgHeight; - if(marginx >0 || marginy > 0) - { - int tw = (_imgWidth + marginx ) >> 1; - int th = (_imgHeight + marginy ) >> 1; - tw = min(_texWidth, tw ); - th = min(_texHeight, th); - GlobalUtil::FitViewPort(tw, th); - AttachToFBO(0); - BindTex(); - ShaderMan::UseShaderZeroPass(); - DrawMargin(tw, th, 1, 1); - } -} - - -void GLTexPacked::FillMargin(int marginx, int marginy) -{ - // - marginx = min(marginx, _texWidth * 2 - _imgWidth); - marginy = min(marginy, _texHeight * 2 - _imgHeight); - if(marginx >0 || marginy > 0) - { - int tw = (_imgWidth + marginx + 1) >> 1; - int th = (_imgHeight + marginy + 1) >> 1; - GlobalUtil::FitViewPort(tw, th); - BindTex(); - AttachToFBO(0); - ShaderMan::UseShaderMarginCopy(_imgWidth , _imgHeight); - DrawMargin(tw, th, marginx, marginy); - } -} -void GLTexPacked::DrawMargin(int right, int bottom, int mx, int my) -{ - int tw = (_imgWidth >>1); - int th = (_imgHeight >>1); - glBegin(GL_QUADS); - if(right>tw && mx) - { - glTexCoord2i ( tw , 0 ); glVertex2i ( tw , 0 ); - glTexCoord2i ( tw , bottom ); glVertex2i ( tw , bottom ); - glTexCoord2i ( right, bottom ); glVertex2i ( right, bottom ); - glTexCoord2i ( right, 0 ); glVertex2i ( right, 0 ); - } - if(bottom>th && my) - { - glTexCoord2i ( 0 , th ); glVertex2i ( 0 , th ); - glTexCoord2i ( 0 , bottom ); glVertex2i ( 0 , bottom ); - glTexCoord2i ( tw , bottom ); glVertex2i ( tw , bottom ); - glTexCoord2i ( tw , th ); glVertex2i ( tw , th ); - } - glEnd(); - glFlush(); - -} - - -void GLTexImage::UnbindMultiTex(int n) -{ - for(int i = n-1; i>=0; i--) - { - glActiveTexture(GL_TEXTURE0+i); - glBindTexture(_texTarget, 0); - } -} - -template <class Uint> int - -#if !defined(_MSC_VER) || _MSC_VER > 1200 -GLTexInput:: -#endif - -DownSamplePixelDataI(unsigned int gl_format, int width, int height, int ds, - const Uint * pin, Uint * pout) -{ - int step, linestep; - int i, j; - int ws = width/ds; - int hs = height/ds; - const Uint * line = pin, * p; - Uint *po = pout; - switch(gl_format) - { - case GL_LUMINANCE: - case GL_LUMINANCE_ALPHA: - step = ds * (gl_format == GL_LUMINANCE? 1: 2); - linestep = width * step; - for(i = 0 ; i < hs; i++, line+=linestep) - { - for(j = 0, p = line; j < ws; j++, p+=step) - { - *po++ = *p; - } - } - break; - case GL_RGB: - case GL_RGBA: - step = ds * (gl_format == GL_RGB? 3: 4); - linestep = width * step; - - for(i = 0 ; i < hs; i++, line+=linestep) - { - for(j = 0, p = line; j < ws; j++, p+=step) - { - //*po++ = int(p[0]*0.299 + p[1] * 0.587 + p[2]* 0.114 + 0.5); - *po++ = ((19595*p[0] + 38470*p[1] + 7471*p[2]+ 32768)>>16); - } - } - break; - case GL_BGR: - case GL_BGRA: - step = ds * (gl_format == GL_BGR? 3: 4); - linestep = width * step; - for(i = 0 ; i < hs; i++, line+=linestep) - { - for(j = 0, p = line; j < ws; j++, p+=step) - { - *po++ = ((7471*p[0] + 38470*p[1] + 19595*p[2]+ 32768)>>16); - } - } - break; - default: - return 0; - } - - return 1; - -} - - -template <class Uint> int - -#if !defined(_MSC_VER) || _MSC_VER > 1200 -GLTexInput:: -#endif - -DownSamplePixelDataI2F(unsigned int gl_format, int width, int height, int ds, - const Uint * pin, float * pout, int skip) -{ - int step, linestep; - int i, j; - int ws = width/ds - skip; - int hs = height/ds; - const Uint * line = pin, * p; - float *po = pout; - const float factor = (sizeof(Uint) == 1? 255.0f : 65535.0f); - switch(gl_format) - { - case GL_LUMINANCE: - case GL_LUMINANCE_ALPHA: - step = ds * (gl_format == GL_LUMINANCE? 1: 2); - linestep = width * step; - for(i = 0 ; i < hs; i++, line+=linestep) - { - for(j = 0, p = line; j < ws; j++, p+=step) - { - *po++ = (*p) / factor; - } - } - break; - case GL_RGB: - case GL_RGBA: - step = ds * (gl_format == GL_RGB? 3: 4); - linestep = width * step; - - for(i = 0 ; i < hs; i++, line+=linestep) - { - for(j = 0, p = line; j < ws; j++, p+=step) - { - //*po++ = int(p[0]*0.299 + p[1] * 0.587 + p[2]* 0.114 + 0.5); - *po++ = ((19595*p[0] + 38470*p[1] + 7471*p[2]) / (65535.0f * factor)); - } - } - break; - case GL_BGR: - case GL_BGRA: - step = ds * (gl_format == GL_BGR? 3: 4); - linestep = width * step; - for(i = 0 ; i < hs; i++, line+=linestep) - { - for(j = 0, p = line; j < ws; j++, p+=step) - { - *po++ = ((7471*p[0] + 38470*p[1] + 19595*p[2]) / (65535.0f * factor)); - } - } - break; - default: - return 0; - } - return 1; -} - -int GLTexInput::DownSamplePixelDataF(unsigned int gl_format, int width, int height, int ds, const float * pin, float * pout, int skip) -{ - int step, linestep; - int i, j; - int ws = width/ds - skip; - int hs = height/ds; - const float * line = pin, * p; - float *po = pout; - switch(gl_format) - { - case GL_LUMINANCE: - case GL_LUMINANCE_ALPHA: - step = ds * (gl_format == GL_LUMINANCE? 1: 2); - linestep = width * step; - for(i = 0 ; i < hs; i++, line+=linestep) - { - for(j = 0, p = line; j < ws; j++, p+=step) - { - *po++ = *p; - } - } - break; - case GL_RGB: - case GL_RGBA: - step = ds * (gl_format == GL_RGB? 3: 4); - linestep = width * step; - for(i = 0 ; i < hs; i++, line+=linestep) - { - for(j = 0, p = line; j < ws; j++, p+=step) - { - *po++ = (0.299f*p[0] + 0.587f*p[1] + 0.114f*p[2]); - } - } - break; - case GL_BGR: - case GL_BGRA: - step = ds * (gl_format == GL_BGR? 3: 4); - linestep = width * step; - for(i = 0 ; i < hs; i++, line+=linestep) - { - for(j = 0, p = line; j < ws; j++, p+=step) - { - *po++ = (0.114f*p[0] + 0.587f*p[1] + 0.299f * p[2]); - } - } - break; - default: - return 0; - } - - return 1; - -} - -int GLTexInput::SetImageData( int width, int height, const void * data, - unsigned int gl_format, unsigned int gl_type ) -{ - int simple_format = IsSimpleGlFormat(gl_format, gl_type);//no cpu code to handle other formats - int ws, hs, done = 1; - - if(_converted_data) {delete [] _converted_data; _converted_data = NULL; } - - _rgb_converted = 1; - _data_modified = 0; - - if( simple_format - && ( width > _texMaxDim || height > _texMaxDim || GlobalUtil::_PreProcessOnCPU) - && GlobalUtil::_octave_min_default >0 ) - { - _down_sampled = GlobalUtil::_octave_min_default; - ws = width >> GlobalUtil::_octave_min_default; - hs = height >> GlobalUtil::_octave_min_default; - }else - { - _down_sampled = 0; - ws = width; - hs = height; - } - - if ( ws > _texMaxDim || hs > _texMaxDim) - { - if(simple_format) - { - if(GlobalUtil::_verbose) std::cout<<"Automatic down-sampling is used\n"; - do - { - _down_sampled ++; - ws >>= 1; - hs >>= 1; - }while(ws > _texMaxDim || hs > _texMaxDim); - }else - { - std::cerr<<"Input images is too big to fit into a texture\n"; - return 0; - } - } - - _texWidth = _imgWidth = _drawWidth = ws; - _texHeight = _imgHeight = _drawHeight = hs; - - if(GlobalUtil::_verbose) - { - std::cout<<"Image size :\t"<<width<<"x"<<height<<"\n"; - if(_down_sampled >0) std::cout<<"Down sample to \t"<<ws<<"x"<<hs<<"\n"; - } - - - if(GlobalUtil::_UseCUDA || GlobalUtil::_UseOpenCL) - { - ////////////////////////////////////// - int tWidth = TruncateWidthCU(_imgWidth); - int skip = _imgWidth - tWidth; - //skip = 0; - if(!simple_format) - { - std::cerr << "Input format not supported under current settings.\n"; - return 0; - }else if(_down_sampled > 0 || gl_format != GL_LUMINANCE || gl_type != GL_FLOAT) - { - _converted_data = new float [_imgWidth * _imgHeight]; - if(gl_type == GL_UNSIGNED_BYTE) - DownSamplePixelDataI2F(gl_format, width, height, 1<<_down_sampled, - ((const unsigned char*) data), _converted_data, skip); - else if(gl_type == GL_UNSIGNED_SHORT) - DownSamplePixelDataI2F(gl_format, width, height, 1<<_down_sampled, - ((const unsigned short*) data), _converted_data, skip); - else - DownSamplePixelDataF(gl_format, width, height, 1<<_down_sampled, (float*)data, _converted_data, skip); - _rgb_converted = 2; //indidates a new data copy - _pixel_data = _converted_data; - }else - { - //Luminance data that doesn't need to down sample - _rgb_converted = 1; - _pixel_data = data; - if(skip > 0) - { - for(int i = 1; i < _imgHeight; ++i) - { - float * dst = ((float*)data) + i * tWidth, * src = ((float*)data) + i * _imgWidth; - for(int j = 0; j < tWidth; ++j) *dst++ = * src++; - } - } - } - _texWidth = _imgWidth = _drawWidth = tWidth; - _data_modified = 1; - }else - { - if(_texID ==0) glGenTextures(1, &_texID); - glBindTexture(_texTarget, _texID); - CheckErrorsGL("glBindTexture"); - glTexParameteri (_texTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri (_texTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glPixelStorei(GL_UNPACK_ALIGNMENT , 1); - - if(simple_format && ( _down_sampled> 0 || (gl_format != GL_LUMINANCE && GlobalUtil::_PreProcessOnCPU) )) - { - - if(gl_type == GL_UNSIGNED_BYTE) - { - unsigned char * newdata = new unsigned char [_imgWidth * _imgHeight]; - DownSamplePixelDataI(gl_format, width, height, 1<<_down_sampled, ((const unsigned char*) data), newdata); - glTexImage2D(_texTarget, 0, GL_LUMINANCE32F_ARB, //internal format changed - _imgWidth, _imgHeight, 0, - GL_LUMINANCE, GL_UNSIGNED_BYTE, newdata); - delete[] newdata; - }else if(gl_type == GL_UNSIGNED_SHORT) - { - unsigned short * newdata = new unsigned short [_imgWidth * _imgHeight]; - DownSamplePixelDataI(gl_format, width, height, 1<<_down_sampled, ((const unsigned short*) data), newdata); - - glTexImage2D(_texTarget, 0, GL_LUMINANCE32F_ARB, //internal format changed - _imgWidth, _imgHeight, 0, - GL_LUMINANCE, GL_UNSIGNED_SHORT, newdata); - delete[] newdata; - }else if(gl_type == GL_FLOAT) - { - float * newdata = new float [_imgWidth * _imgHeight]; - DownSamplePixelDataF(gl_format, width, height, 1<<_down_sampled, (float*)data, newdata); - glTexImage2D(_texTarget, 0, GL_LUMINANCE32F_ARB, //internal format changed - _imgWidth, _imgHeight, 0, - GL_LUMINANCE, GL_FLOAT, newdata); - delete[] newdata; - }else - { - //impossible - done = 0; - _rgb_converted = 0; - } - GlobalUtil::FitViewPort(1, 1); //this used to be necessary - }else - { - //ds must be 0 here if not simpleformat - if(gl_format == GL_LUMINANCE || gl_format == GL_LUMINANCE_ALPHA) - { - //use one channel internal format if data is intensity image - glTexImage2D(_texTarget, 0, GL_LUMINANCE32F_ARB, - _imgWidth, _imgHeight, 0, gl_format, gl_type, data); - GlobalUtil::FitViewPort(1, 1); //this used to be necessary - } - else - { - //convert RGB 2 GRAY if needed - glTexImage2D(_texTarget, 0, _iTexFormat, _imgWidth, _imgHeight, 0, gl_format, gl_type, data); - if(ShaderMan::HaveShaderMan()) - TexConvertRGB(); - else - _rgb_converted = 0; //In CUDA mode, the conversion will be done by CUDA kernel - } - } - UnbindTex(); - } - return done; -} - - -GLTexInput::~GLTexInput() -{ - if(_converted_data) delete [] _converted_data; -} - - -int GLTexInput::LoadImageFile(char *imagepath, int &w, int &h ) -{ -#ifndef SIFTGPU_NO_DEVIL - static int devil_loaded = 0; - unsigned int imID; - int done = 1; - - if(devil_loaded == 0) - { - ilInit(); - ilOriginFunc(IL_ORIGIN_UPPER_LEFT); - ilEnable(IL_ORIGIN_SET); - devil_loaded = 1; - } - - /// - ilGenImages(1, &imID); - ilBindImage(imID); - - if(ilLoadImage(imagepath)) - { - w = ilGetInteger(IL_IMAGE_WIDTH); - h = ilGetInteger(IL_IMAGE_HEIGHT); - int ilformat = ilGetInteger(IL_IMAGE_FORMAT); - - if(SetImageData(w, h, ilGetData(), ilformat, GL_UNSIGNED_BYTE)==0) - { - done =0; - }else if(GlobalUtil::_verbose) - { - std::cout<<"Image loaded :\t"<<imagepath<<"\n"; - } - - }else - { - std::cerr<<"Unable to open image [code = "<<ilGetError()<<"]\n"; - done = 0; - } - - ilDeleteImages(1, &imID); - - return done; -#else - FILE * file = fopen(imagepath, "rb"); if (file ==NULL) return 0; - - char buf[8]; int width, height, cn, g, done = 1; - - if(fscanf(file, "%s %d %d %d", buf, &width, &height, &cn )<4 || cn > 255 || width < 0 || height < 0) - { - fclose(file); - std::cerr << "ERROR: fileformat not supported\n"; - return 0; - }else - { - w = width; - h = height; - } - unsigned char * data = new unsigned char[width * height]; - unsigned char * pixels = data; - if (strcmp(buf, "P5")==0 ) - { - fscanf(file, "%c",buf);//skip one byte - fread(pixels, 1, width*height, file); - }else if (strcmp(buf, "P2")==0 ) - { - for (int i = 0 ; i< height; i++) - { - for ( int j = 0; j < width; j++) - { - fscanf(file, "%d", &g); - *pixels++ = (unsigned char) g; - } - } - }else if (strcmp(buf, "P6")==0 ) - { - fscanf(file, "%c", buf);//skip one byte - int j, num = height*width; - unsigned char buf[3]; - for ( j =0 ; j< num; j++) - { - fread(buf,1,3, file); - *pixels++=int(0.10454f* buf[2]+0.60581f* buf[1]+0.28965f* buf[0]); - } - }else if (strcmp(buf, "P3")==0 ) - { - int r, g, b; - int i , num =height*width; - for ( i = 0 ; i< num; i++) - { - fscanf(file, "%d %d %d", &r, &g, &b); - *pixels++ = int(0.10454f* b+0.60581f* g+0.28965f* r); - } - - }else - { - std::cerr << "ERROR: fileformat not supported\n"; - done = 0; - } - if(done) SetImageData(width, height, data, GL_LUMINANCE, GL_UNSIGNED_BYTE); - fclose(file); - delete data; - if(GlobalUtil::_verbose && done) std::cout<< "Image loaded :\t" << imagepath << "\n"; - return 1; -#endif -} - -int GLTexImage::CopyToPBO(GLuint pbo, int width, int height, GLenum format) -{ - ///////// - if(format != GL_RGBA && format != GL_LUMINANCE) return 0; - - FrameBufferObject fbo; - GLint bsize, esize = width * height * sizeof(float) * (format == GL_RGBA ? 4 : 1); - AttachToFBO(0); - glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, pbo); - glGetBufferParameteriv(GL_PIXEL_PACK_BUFFER_ARB, GL_BUFFER_SIZE, &bsize); - if(bsize < esize) - { - glBufferData(GL_PIXEL_PACK_BUFFER_ARB, esize, NULL, GL_STATIC_DRAW_ARB); - glGetBufferParameteriv(GL_PIXEL_PACK_BUFFER_ARB, GL_BUFFER_SIZE, &bsize); - } - if(bsize >= esize) - { - glReadPixels(0, 0, width, height, format, GL_FLOAT, 0); - } - glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0); - DetachFBO(0); - - return bsize >= esize; -} - -void GLTexImage::SaveToASCII(const char* path) -{ - vector<float> buf(GetImgWidth() * GetImgHeight() * 4); - FrameBufferObject fbo; - AttachToFBO(0); - glReadPixels(0, 0, GetImgWidth(), GetImgHeight(), GL_RGBA, GL_FLOAT, &buf[0]); - ofstream out(path); - - for(int i = 0, idx = 0; i < GetImgHeight(); ++i) - { - for(int j = 0; j < GetImgWidth(); ++j, idx += 4) - { - out << i << " " << j << " " << buf[idx] << " " << buf[idx + 1] << " " - << buf[idx + 2] << " " << buf[idx + 3] << "\n"; - } - } -} - - -void GLTexInput::VerifyTexture() -{ - //for CUDA or OpenCL the texture is not generated by default - if(!_data_modified) return; - if(_pixel_data== NULL) return; - InitTexture(_imgWidth, _imgHeight); - BindTex(); - glTexImage2D( _texTarget, 0, GL_LUMINANCE32F_ARB, //internal format changed - _imgWidth, _imgHeight, 0, - GL_LUMINANCE, GL_FLOAT, _pixel_data); - UnbindTex(); - _data_modified = 0; -} - -void GLTexImage::CopyFromPBO(GLuint pbo, int width, int height, GLenum format) -{ - InitTexture(max(width, _texWidth), max(height, _texHeight)); - SetImageSize(width, height); - if(width > 0 && height > 0) - { - BindTex(); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, pbo); - glTexSubImage2D(GlobalUtil::_texTarget, 0, 0, 0, width, height, format, GL_FLOAT, 0); - GlobalUtil::CheckErrorsGL("GLTexImage::CopyFromPBO->glTexSubImage2D"); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0); - UnbindTex(); - } -} - diff --git a/3rdparty/SiftGPU/src/SiftGPU/GLTexImage.h b/3rdparty/SiftGPU/src/SiftGPU/GLTexImage.h deleted file mode 100644 index 0862e7a9..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/GLTexImage.h +++ /dev/null @@ -1,158 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: GLTexImage.h -// Author: Changchang Wu -// Description : interface for the GLTexImage class. -// GLTexImage: naive texture class. -// sevral different quad drawing functions are provied -// GLTexPacked: packed version (four value packed as four channels of a pixel) -// GLTexInput: GLTexImage + some input information -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - - -#ifndef GL_TEX_IMAGE_H -#define GL_TEX_IMAGE_H - -class GlobalUtil; -class GLTexImage :public GlobalUtil -{ -protected: - GLuint _texID; - int _imgWidth; - int _imgHeight; - int _texWidth; - int _texHeight; - int _drawWidth; - int _drawHeight; -public: - static void DetachFBO(int i); - static void UnbindTex(); - static void UnbindMultiTex(int n); - static void DrawQuad(float x1, float x2, float y1, float y2); - -public: - virtual void DrawQuadUS(int scale); - virtual void DrawQuadDS(int scale); - virtual void DrawImage(); - virtual void TexConvertRGB(); - virtual void ZeroHistoMargin(); - virtual void SetImageSize(int width, int height); - virtual void InitTexture(int width, int height, int clamp_to_edge =1 ); - void InitTexture(int width, int height, int clamp_to_edge, GLuint format); - virtual void FillMargin(int marginx, int marginy); -public: - void DrawScaledQuad(float scale); - int CopyToPBO(GLuint pbo, int width, int height, GLenum format = GL_RGBA); - void CopyFromPBO(GLuint pbo, int width, int height, GLenum format = GL_RGBA); - void FitRealTexViewPort(); - void DrawQuadMT8(); - void DrawQuadMT4(); - void DrawQuadReduction(); - void DrawQuadReduction(int w, int h); - void DrawMargin(int right, int bottom); - void DrawQuad(); - void FitTexViewPort(); - void ZeroHistoMargin(int hw, int hh); - int CheckTexture(); - void SaveToASCII(const char* path); -public: - void AttachToFBO(int i ); - void BindTex(); - operator GLuint (){return _texID;} - GLuint GetTexID(){return _texID;} - int GetImgPixelCount(){return _imgWidth*_imgHeight;} - int GetTexPixelCount(){return _texWidth*_texHeight;} - int GetImgWidth(){return _imgWidth;} - int GetImgHeight(){return _imgHeight;} - int GetTexWidth(){return _texWidth;} - int GetTexHeight(){return _texHeight;} - int GetDrawWidth(){return _drawWidth;} - int GetDrawHeight(){return _drawHeight;} - //int IsTexTight(){return _texWidth == _drawWidth && _texHeight == _drawHeight;} - int IsTexPacked(){return _drawWidth != _imgWidth;} - GLTexImage(); - virtual ~GLTexImage(); - friend class SiftGPU; -}; - -//class for handle data input, to support all openGL-supported data format, -//data are first uploaded to an openGL texture then converted, and optionally -//when the datatype is simple, we downsample/convert on cpu -class GLTexInput:public GLTexImage -{ -public: - int _down_sampled; - int _rgb_converted; - int _data_modified; - - ////////////////////////// - float * _converted_data; - const void* _pixel_data; -public: - static int IsSimpleGlFormat(unsigned int gl_format, unsigned int gl_type) - { - //the formats there is a cpu code to conver rgb and downsample - return (gl_format ==GL_LUMINANCE ||gl_format == GL_LUMINANCE_ALPHA|| - gl_format == GL_RGB|| gl_format == GL_RGBA|| - gl_format == GL_BGR || gl_format == GL_BGRA) && - (gl_type == GL_UNSIGNED_BYTE || gl_type == GL_FLOAT || gl_type == GL_UNSIGNED_SHORT); - } -//in vc6, template member function doesn't work -#if !defined(_MSC_VER) || _MSC_VER > 1200 - template <class Uint> - static int DownSamplePixelDataI(unsigned int gl_format, int width, int height, - int ds, const Uint * pin, Uint * pout); - template <class Uint> - static int DownSamplePixelDataI2F(unsigned int gl_format, int width, int height, - int ds, const Uint * pin, float * pout, int skip = 0); -#endif - static int DownSamplePixelDataF(unsigned int gl_format, int width, int height, - int ds, const float * pin, float * pout, int skip = 0); - static int TruncateWidthCU(int w) {return w & 0xfffffffc; } -public: - GLTexInput() : _down_sampled(0), _rgb_converted(0), _data_modified(0), - _converted_data(0), _pixel_data(0){} - int SetImageData(int width, int height, const void * data, - unsigned int gl_format, unsigned int gl_type); - int LoadImageFile(char * imagepath, int & w, int &h); - void VerifyTexture(); - virtual ~GLTexInput(); -}; - -//GLTexPacked doesn't have any data -//so that we can use the GLTexImage* pointer to index a GLTexPacked Vector - -class GLTexPacked:public GLTexImage -{ -public: - virtual void DrawImage(); - virtual void DrawQuadUS(int scale); - virtual void DrawQuadDS(int scale); - virtual void FillMargin(int marginx, int marginy); - virtual void InitTexture(int width, int height, int clamp_to_edge =1); - virtual void TexConvertRGB(); - virtual void SetImageSize(int width, int height); - virtual void ZeroHistoMargin(); - //virtual void GetHistWH(int& w, int& h){return w = (3 + sz)>>1;} -public: - void DrawMargin(int right, int bottom, int mx, int my); - GLTexPacked():GLTexImage(){} -}; - - -#endif // !defined(GL_TEX_IMAGE_H) - diff --git a/3rdparty/SiftGPU/src/SiftGPU/GlobalUtil.cpp b/3rdparty/SiftGPU/src/SiftGPU/GlobalUtil.cpp deleted file mode 100644 index e068e18d..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/GlobalUtil.cpp +++ /dev/null @@ -1,519 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: GlobalUtil.cpp -// Author: Changchang Wu -// Description : Global Utility class for SiftGPU -// -// -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// -#include <string.h> -#include <iostream> -using std::cout; - -#include "GL/glew.h" -#include "GlobalUtil.h" - -//for windows, the default timing uses timeGetTime, you can define TIMING_BY_CLOCK to use clock() -//for other os, the timing uses gettimeofday - - -#if defined(_WIN32) - #if defined(TIMING_BY_CLOCK) - #include <time.h> - #else - #define WIN32_LEAN_AND_MEAN - #include <windows.h> - #include <mmsystem.h> - #endif -#else - #include <sys/time.h> - #include <stdio.h> -#endif - -#include "LiteWindow.h" - -// -int GlobalParam:: _verbose = 1; -int GlobalParam:: _timingS = 1; //print out information of each step -int GlobalParam:: _timingO = 0; //print out information of each octave -int GlobalParam:: _timingL = 0; //print out information of each level -GLuint GlobalParam:: _texTarget = GL_TEXTURE_RECTANGLE_ARB; //only this one is supported -GLuint GlobalParam:: _iTexFormat =GL_RGBA32F_ARB; //or GL_RGBA16F_ARB -int GlobalParam:: _debug = 0; //enable debug code? -int GlobalParam:: _usePackedTex = 1;//packed implementation -int GlobalParam:: _UseCUDA = 0; -int GlobalParam:: _UseOpenCL = 0; -int GlobalParam:: _MaxFilterWidth = -1; //maximum filter width, use when GPU is not good enough -float GlobalParam:: _FilterWidthFactor = 4.0f; //the filter size will be _FilterWidthFactor*sigma*2+1 -float GlobalParam:: _DescriptorWindowFactor = 3.0f; //descriptor sampling window factor -int GlobalParam:: _SubpixelLocalization = 1; //sub-pixel and sub-scale localization -int GlobalParam:: _MaxOrientation = 2; //whether we find multiple orientations for each feature -int GlobalParam:: _OrientationPack2 = 0; //use one float to store two orientations -float GlobalParam:: _MaxFeaturePercent = 0.005f;//at most 0.005 of all pixels -int GlobalParam:: _MaxLevelFeatureNum = 4096; //maximum number of features of a level -int GlobalParam:: _FeatureTexBlock = 4; //feature texture storagte alignment -int GlobalParam:: _NarrowFeatureTex = 0; - -//if _ForceTightPyramid is not 0, pyramid will be reallocated to fit the size of input images. -//otherwise, pyramid can be reused for smaller input images. -int GlobalParam:: _ForceTightPyramid = 0; - -//use gpu or cpu to generate feature list ...gpu is a little bit faster -int GlobalParam:: _ListGenGPU = 1; -int GlobalParam:: _ListGenSkipGPU = 6; //how many levels are skipped on gpu -int GlobalParam:: _PreProcessOnCPU = 1; //convert rgb 2 intensity on gpu, down sample on GPU - -//hardware parameter, automatically retrieved -int GlobalParam:: _texMaxDim = 3200; //Maximum working size for SiftGPU, 3200 for packed -int GlobalParam:: _texMaxDimGL = 4096; //GPU texture limit -int GlobalParam:: _texMinDim = 16; // -int GlobalParam:: _MemCapGPU = 0; -int GlobalParam:: _FitMemoryCap = 0; -int GlobalParam:: _IsNvidia = 0; //GPU vendor -int GlobalParam:: _KeepShaderLoop = 0; - -//you can't change the following 2 values -//all other versions of code are now dropped -int GlobalParam:: _DescriptorPPR = 8; -int GlobalParam:: _DescriptorPPT = 16; - -//whether orientation/descriptor is supported by hardware -int GlobalParam:: _SupportNVFloat = 0; -int GlobalParam:: _SupportTextureRG = 0; -int GlobalParam:: _UseDynamicIndexing = 0; -int GlobalParam:: _FullSupported = 1; - -//when SiftGPUEX is not used, display VBO generation is skipped -int GlobalParam:: _UseSiftGPUEX = 0; -int GlobalParam:: _InitPyramidWidth=0; -int GlobalParam:: _InitPyramidHeight=0; -int GlobalParam:: _octave_min_default=0; -int GlobalParam:: _octave_num_default=-1; - - -////////////////////////////////////////////////////////////////// -int GlobalParam:: _GoodOpenGL = -1; //indicates OpenGl initialization status -int GlobalParam:: _FixedOrientation = 0; //upright -int GlobalParam:: _LoweOrigin = 0; //(0, 0) to be at the top-left corner. -int GlobalParam:: _NormalizedSIFT = 1; //normalize descriptor -int GlobalParam:: _BinarySIFT = 0; //saving binary format -int GlobalParam:: _ExitAfterSIFT = 0; //exif after saving result -int GlobalParam:: _KeepExtremumSign = 0; // if 1, scales of dog-minimum will be multiplied by -1 -/// -int GlobalParam:: _KeyPointListForceLevel0 = 0; -int GlobalParam:: _DarknessAdaption = 0; -int GlobalParam:: _ProcessOBO = 0; -int GlobalParam:: _TruncateMethod = 0; -int GlobalParam:: _PreciseBorder = 1; - -// parameter changing for better matching with Lowe's SIFT -float GlobalParam:: _OrientationWindowFactor = 2.0f; // 1.0(-v292), 2(v293-), -float GlobalParam:: _OrientationGaussianFactor = 1.5f; // 4.5(-v292), 1.5(v293-) -float GlobalParam:: _MulitiOrientationThreshold = 0.8f; -/// -int GlobalParam:: _FeatureCountThreshold = -1; - -/////////////////////////////////////////////// -int GlobalParam:: _WindowInitX = -1; -int GlobalParam:: _WindowInitY = -1; -int GlobalParam:: _DeviceIndex = 0; -const char * GlobalParam:: _WindowDisplay = NULL; - - - -///////////////// -//// -ClockTimer GlobalUtil:: _globalTimer; - - -#ifdef _DEBUG -void GlobalUtil::CheckErrorsGL(const char* location) -{ - GLuint errnum; - const char *errstr; - while (errnum = glGetError()) - { - errstr = (const char *)(gluErrorString(errnum)); - if(errstr) { - std::cerr << errstr; - } - else { - std::cerr << "Error " << errnum; - } - - if(location) std::cerr << " at " << location; - std::cerr << "\n"; - } - return; -} - -#endif - -void GlobalUtil::CleanupOpenGL() -{ - glActiveTexture(GL_TEXTURE0); -} - -void GlobalUtil::SetDeviceParam(int argc, char** argv) -{ - if(GlobalParam::_GoodOpenGL!= -1) return; - - #define CHAR1_TO_INT(x) ((x >= 'A' && x <= 'Z') ? x + 32 : x) - #define CHAR2_TO_INT(str, i) (str[i] ? CHAR1_TO_INT(str[i]) + (CHAR1_TO_INT(str[i+1]) << 8) : 0) - #define CHAR3_TO_INT(str, i) (str[i] ? CHAR1_TO_INT(str[i]) + (CHAR2_TO_INT(str, i + 1) << 8) : 0) - #define STRING_TO_INT(str) (CHAR1_TO_INT(str[0]) + (CHAR3_TO_INT(str, 1) << 8)) - - char* arg, * opt; - for(int i = 0; i< argc; i++) - { - arg = argv[i]; - if(arg == NULL || arg[0] != '-')continue; - opt = arg+1; - - //////////////////////////////// - switch( STRING_TO_INT(opt)) - { - case 'w' + ('i' << 8) + ('n' << 16) + ('p' << 24): - if(_GoodOpenGL != 2 && i + 1 < argc) - { - int x =0, y=0; - if(sscanf(argv[++i], "%dx%d", &x, &y) == 2) - { - GlobalParam::_WindowInitX = x; - GlobalParam::_WindowInitY = y; - } - } - break; - case 'd' + ('i' << 8) + ('s' << 16) + ('p' << 24): - if(_GoodOpenGL != 2 && i + 1 < argc) - { - GlobalParam::_WindowDisplay = argv[++i]; - } - break; - case 'c' + ('u' << 8) + ('d' << 16) + ('a' << 24): - if(i + 1 < argc) - { - int device = 0; - scanf(argv[++i], "%d", &device) ; - GlobalParam::_DeviceIndex = device; - } - break; - default: - break; - } - } -} - -void GlobalUtil::SetTextureParameter() -{ - - glTexParameteri (_texTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri (_texTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(_texTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(_texTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); -} - -//if image need to be up sampled ..use this one - -void GlobalUtil::SetTextureParameterUS() -{ - - glTexParameteri (_texTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri (_texTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(_texTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(_texTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); -} - - -void GlobalUtil::FitViewPort(int width, int height) -{ - GLint port[4]; - glGetIntegerv(GL_VIEWPORT, port); - if(port[2] !=width || port[3] !=height) - { - glViewport(0, 0, width, height); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0, width, 0, height, 0, 1); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - } -} - - -bool GlobalUtil::CheckFramebufferStatus() { - GLenum status; - status=(GLenum)glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); - switch(status) { - case GL_FRAMEBUFFER_COMPLETE_EXT: - return true; - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: - std::cerr<<("Framebuffer incomplete,incomplete attachment\n"); - return false; - case GL_FRAMEBUFFER_UNSUPPORTED_EXT: - std::cerr<<("Unsupported framebuffer format\n"); - return false; - case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: - std::cerr<<("Framebuffer incomplete,missing attachment\n"); - return false; - case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: - std::cerr<<("Framebuffer incomplete,attached images must have same dimensions\n"); - return false; - case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: - std::cerr<<("Framebuffer incomplete,attached images must have same format\n"); - return false; - case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: - std::cerr<<("Framebuffer incomplete,missing draw buffer\n"); - return false; - case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: - std::cerr<<("Framebuffer incomplete,missing read buffer\n"); - return false; - } - return false; -} - - -int ClockTimer::ClockMS() -{ -#if defined(_WIN32) - #if defined(TIMING_BY_CLOCK) - return clock() * 1000 / CLOCKS_PER_SEC; - #else - static int started = 0; - static int tstart; - if(started == 0) - { - tstart = timeGetTime(); - started = 1; - return 0; - }else - { - return timeGetTime() - tstart; - } - #endif -#else - static int started = 0; - static struct timeval tstart; - if(started == 0) - { - gettimeofday(&tstart, NULL); - started = 1; - return 0; - }else - { - struct timeval now; - gettimeofday(&now, NULL) ; - return (now.tv_usec - tstart.tv_usec + (now.tv_sec - tstart.tv_sec) * 1000000)/1000; - } -#endif -} - -double ClockTimer::CLOCK() -{ - return ClockMS() * 0.001; -} - -void ClockTimer::InitHighResolution() -{ -#if defined(_WIN32) - timeBeginPeriod(1); -#endif -} - -void ClockTimer::StartTimer(const char* event, int verb) -{ - strcpy(_current_event, event); - _time_start = ClockMS(); - if(verb && GlobalUtil::_verbose) - { - std::cout<<"\n["<<_current_event<<"]:\tbegin ...\n"; - } -} - -void ClockTimer::StopTimer(int verb) -{ - _time_stop = ClockMS(); - if(verb && GlobalUtil::_verbose) - { - std::cout<<"["<<_current_event<<"]:\t"<<GetElapsedTime()<<"\n"; - } -} - -float ClockTimer::GetElapsedTime() -{ - return (_time_stop - _time_start) * 0.001f; -} - -void GlobalUtil::SetGLParam() -{ - if(GlobalUtil::_UseCUDA) return; - else if(GlobalUtil::_UseOpenCL) return; - glEnable(GlobalUtil::_texTarget); - glActiveTexture(GL_TEXTURE0); -} - -void GlobalUtil::InitGLParam(int NotTargetGL) -{ - //IF the OpenGL context passed the check - if(GlobalUtil::_GoodOpenGL == 2) return; - //IF the OpenGl context failed the check - if(GlobalUtil::_GoodOpenGL == 0) return; - //IF se use CUDA or OpenCL - if(NotTargetGL && !GlobalUtil::_UseSiftGPUEX) - { - GlobalUtil::_GoodOpenGL = 1; - }else - { - //first time in this function - glewInit(); - - GlobalUtil::_GoodOpenGL = 2; - - const char * vendor = (const char * )glGetString(GL_VENDOR); - if(vendor) - { - GlobalUtil::_IsNvidia = (strstr(vendor, "NVIDIA") !=NULL ? 1 : 0); - - // Let nVidia compiler to take care of the unrolling. - if (GlobalUtil::_IsNvidia) GlobalUtil::_KeepShaderLoop = 1; - -#ifndef WIN32 - else if(!strstr(vendor, "ATI") ) - { - // For non-nVidia non-ATI cards...simply assume it is Mesa - // Keep the original shader loop, because some of the unrolled - // loopes are too large, and it may take too much time to compile - GlobalUtil::_KeepShaderLoop = 1; - } -#endif - - if(GlobalUtil::_IsNvidia && glewGetExtension("GL_NVX_gpu_memory_info")) - { - glGetIntegerv(0x9049/*GL_GPU_MEM_INFO_CURRENT_AVAILABLE_MEM_NVX*/, &_MemCapGPU); - _MemCapGPU /= (1024); - if(GlobalUtil::_verbose) std::cout << "[GPU VENDOR]:\t" << vendor << ' ' <<_MemCapGPU << "MB\n"; - }else if(strstr(vendor, "ATI") && glewGetExtension("GL_ATI_meminfo")) - { - int info[4]; glGetIntegerv(0x87FC/*GL_TEXTURE_FREE_MEMORY_ATI*/, info); - _MemCapGPU = info[0] / (1024); - if(GlobalUtil::_verbose) std::cout << "[GPU VENDOR]:\t" << vendor << ' ' <<_MemCapGPU << "MB\n"; - }else - { - if(GlobalUtil::_verbose) std::cout << "[GPU VENDOR]:\t" << vendor << "\n"; - } - - } - if(GlobalUtil::_IsNvidia == 0 )GlobalUtil::_UseCUDA = 0; - - if (glewGetExtension("GL_ARB_fragment_shader") != GL_TRUE || - glewGetExtension("GL_ARB_shader_objects") != GL_TRUE || - glewGetExtension("GL_ARB_shading_language_100") != GL_TRUE) - { - std::cerr << "Shader not supported by your hardware!\n"; - GlobalUtil::_GoodOpenGL = 0; - } - - if (glewGetExtension("GL_EXT_framebuffer_object") != GL_TRUE) - { - std::cerr << "Framebuffer object not supported!\n"; - GlobalUtil::_GoodOpenGL = 0; - } - - if(glewGetExtension("GL_ARB_texture_rectangle")==GL_TRUE) - { - GLint value; - GlobalUtil::_texTarget = GL_TEXTURE_RECTANGLE_ARB; - glGetIntegerv(GL_MAX_RECTANGLE_TEXTURE_SIZE_EXT, &value); - GlobalUtil::_texMaxDimGL = value; - if(GlobalUtil::_verbose) std::cout << "TEXTURE:\t" << GlobalUtil::_texMaxDimGL << "\n"; - - if(GlobalUtil::_texMaxDim == 0 || GlobalUtil::_texMaxDim > GlobalUtil::_texMaxDimGL) - { - GlobalUtil::_texMaxDim = GlobalUtil::_texMaxDimGL; - } - glEnable(GlobalUtil::_texTarget); - }else - { - std::cerr << "GL_ARB_texture_rectangle not supported!\n"; - GlobalUtil::_GoodOpenGL = 0; - } - - GlobalUtil::_SupportNVFloat = glewGetExtension("GL_NV_float_buffer"); - GlobalUtil::_SupportTextureRG = glewGetExtension("GL_ARB_texture_rg"); - - - glShadeModel(GL_FLAT); - glPolygonMode(GL_FRONT, GL_FILL); - - GlobalUtil::SetTextureParameter(); - - } -} - -void GlobalUtil::SelectDisplay() -{ -#ifdef WIN32 - if(_WindowDisplay == NULL) return; - - HDC hdc = CreateDC(_WindowDisplay, _WindowDisplay, NULL, NULL); - _WindowDisplay = NULL; - if(hdc == NULL) - { - std::cout << "ERROR: invalid dispaly specified\n"; - return; - } - - PIXELFORMATDESCRIPTOR pfd = - { - sizeof(PIXELFORMATDESCRIPTOR),1, - PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER, - PFD_TYPE_RGBA,24,0, 0, 0, 0, 0, 0,0,0,0,0, 0, 0, 0,16,0,0, - PFD_MAIN_PLANE,0,0, 0, 0 - }; - ChoosePixelFormat(hdc, &pfd); -#endif -} - -int GlobalUtil::CreateWindowEZ(LiteWindow* window) -{ - if(window == NULL) return 0; - if(!window->IsValid())window->Create(_WindowInitX, _WindowInitY, _WindowDisplay); - if(window->IsValid()) - { - window->MakeCurrent(); - return 1; - } - else - { - std::cerr << "Unable to create OpenGL Context!\n"; - std::cerr << "For nVidia cards, you can try change to CUDA mode in this case\n"; - return 0; - } -} - -int GlobalUtil::CreateWindowEZ() -{ - static LiteWindow window; - return CreateWindowEZ(&window); -} - -int CreateLiteWindow(LiteWindow* window) -{ - return GlobalUtil::CreateWindowEZ(window); -} diff --git a/3rdparty/SiftGPU/src/SiftGPU/GlobalUtil.h b/3rdparty/SiftGPU/src/SiftGPU/GlobalUtil.h deleted file mode 100644 index 4a0f6e63..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/GlobalUtil.h +++ /dev/null @@ -1,157 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: GlobalUtil.h -// Author: Changchang Wu -// Description : -// GlobalParam: Global parameters -// ClockTimer: Timer -// GlobalUtil: Global Function wrapper -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - - -#ifndef _GLOBAL_UTILITY_H -#define _GLOBAL_UTILITY_H - - -//wrapper for some shader function -//class ProgramGPU; -class LiteWindow; - -class GlobalParam -{ -public: - static GLuint _texTarget; - static GLuint _iTexFormat; - static int _texMaxDim; - static int _texMaxDimGL; - static int _texMinDim; - static int _MemCapGPU; - static int _FitMemoryCap; - static int _verbose; - static int _timingS; - static int _timingO; - static int _timingL; - static int _usePackedTex; - static int _IsNvidia; - static int _KeepShaderLoop; - static int _UseCUDA; - static int _UseOpenCL; - static int _UseDynamicIndexing; - static int _debug; - static int _MaxFilterWidth; - static float _FilterWidthFactor; - static float _OrientationWindowFactor; - static float _DescriptorWindowFactor; - static int _MaxOrientation; - static int _OrientationPack2; - static int _ListGenGPU; - static int _ListGenSkipGPU; - static int _SupportNVFloat; - static int _SupportTextureRG; - static int _FullSupported; - static float _MaxFeaturePercent; - static int _MaxLevelFeatureNum; - static int _DescriptorPPR; - static int _DescriptorPPT; //pixel per texture for one descriptor - static int _FeatureTexBlock; - static int _NarrowFeatureTex; //implemented but no performance improvement - static int _SubpixelLocalization; - static int _ProcessOBO; //not implemented yet - static int _TruncateMethod; - static int _PreciseBorder; //implemented - static int _UseSiftGPUEX; - static int _ForceTightPyramid; - static int _octave_min_default; - static int _octave_num_default; - static int _InitPyramidWidth; - static int _InitPyramidHeight; - static int _PreProcessOnCPU; - static int _GoodOpenGL; - static int _FixedOrientation; - static int _LoweOrigin; - static int _ExitAfterSIFT; - static int _NormalizedSIFT; - static int _BinarySIFT; - static int _KeepExtremumSign; - static int _FeatureCountThreshold; - static int _KeyPointListForceLevel0; - static int _DarknessAdaption; - - //for compatability with old version: - static float _OrientationExtraFactor; - static float _OrientationGaussianFactor; - static float _MulitiOrientationThreshold; - - //////////////////////////////////////// - static int _WindowInitX; - static int _WindowInitY; - static const char* _WindowDisplay; - static int _DeviceIndex; -}; - - -class ClockTimer -{ -private: - char _current_event[256]; - int _time_start; - int _time_stop; -public: - static int ClockMS(); - static double CLOCK(); - static void InitHighResolution(); - void StopTimer(int verb = 1); - void StartTimer(const char * event, int verb=0); - float GetElapsedTime(); -}; - -class GlobalUtil:public GlobalParam -{ - static ClockTimer _globalTimer; -public: - inline static double CLOCK() { return ClockTimer::CLOCK(); } - inline static void StopTimer() { _globalTimer.StopTimer(_timingS); } - inline static void StartTimer(const char * event) { _globalTimer.StartTimer(event, _timingO); } - inline static float GetElapsedTime() { return _globalTimer.GetElapsedTime(); } - - static void FitViewPort(int width, int height); - static void SetTextureParameter(); - static void SetTextureParameterUS(); -#ifdef _DEBUG - static void CheckErrorsGL(const char* location = NULL); -#else - static void inline CheckErrorsGL(const char* location = NULL){}; -#endif - static bool CheckFramebufferStatus(); - //initialize Opengl parameters - static void SelectDisplay(); - static void InitGLParam(int NotTargetGL = 0); - static void SetGLParam(); - static int CreateWindowEZ(); - static void CleanupOpenGL(); - static void SetDeviceParam(int argc, char** argv); - static int CreateWindowEZ(LiteWindow* window); -}; - - -#if defined(_MSC_VER) && _MSC_VER == 1200 -#define max(a,b) (((a) > (b)) ? (a) : (b)) -#define min(a,b) (((a) < (b)) ? (a) : (b)) -#endif - -#endif - diff --git a/3rdparty/SiftGPU/src/SiftGPU/LiteWindow.h b/3rdparty/SiftGPU/src/SiftGPU/LiteWindow.h deleted file mode 100644 index 9c053ebd..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/LiteWindow.h +++ /dev/null @@ -1,197 +0,0 @@ -#ifndef LITE_WINDOW_H -#define LITE_WINDOW_H - -//#define WINDOW_PREFER_GLUT - -#if defined(WINDOW_PREFER_GLUT) - -#ifdef __APPLE__ - #include "GLUT/glut.h" -#else - #include "GL/glut.h" -#endif -//for apple, use GLUT to create the window.. -class LiteWindow -{ - int glut_id; -public: - LiteWindow() { glut_id = 0; } - int IsValid() { return glut_id > 0; } - virtual ~LiteWindow() { if(glut_id > 0) glutDestroyWindow(glut_id); } - void MakeCurrent() { glutSetWindow(glut_id); } - void Create(int x = -1, int y = -1, const char* display = NULL) - { - static int _glut_init_called = 0; - if(glut_id != 0) return; - - //see if there is an existing window - if(_glut_init_called) glut_id = glutGetWindow(); - - //create one if no glut window exists - if(glut_id != 0) return; - - if(_glut_init_called == 0) - { - int argc = 1; - char * argv[4] = { "-iconic", 0 , 0, 0}; - if(display) - { - argc = 3; - argv[1] = "-display"; - argv[2] = (char*) display; - } - glutInit(&argc, argv); - glutInitDisplayMode (GLUT_RGBA ); - _glut_init_called = 1; - } - if(x != -1) glutInitWindowPosition(x, y); - if(display || x != -1) std::cout << "Using display [" - << (display? display : "\0" )<< "] at (" << x << "," << y << ")\n"; - glut_id = glutCreateWindow ("SIFT_GPU_GLUT"); - glutHideWindow(); - } -}; -#elif defined( _WIN32) - -#ifndef _INC_WINDOWS -#ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN -#endif -#include <windows.h> -#endif - -class LiteWindow -{ - HWND hWnd; - HGLRC hContext; - HDC hdc; -public: - LiteWindow() - { - hWnd = NULL; - hContext = NULL; - hdc = NULL; - } - virtual ~LiteWindow() - { - if(hContext)wglDeleteContext(hContext); - if(hdc)ReleaseDC(hWnd, hdc); - if(hWnd)DestroyWindow(hWnd); - } - int IsValid() - { - return hContext != NULL; - } - - //display is ignored under Win32 - void Create(int x = -1, int y = -1, const char* display = NULL) - { - if(hContext) return; - WNDCLASSEX wcex = { sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW, - (WNDPROC)DefWindowProc, 0, 4, 0, 0, 0, 0, 0, - ("SIFT_GPU_LITE"), 0}; - RegisterClassEx(&wcex); - hWnd = CreateWindow("SIFT_GPU_LITE", "SIFT_GPU", 0, - CW_USEDEFAULT, CW_USEDEFAULT, - 100, 100, NULL, NULL, 0, 0); - - //move the window so that it can be on the second monitor - if(x !=-1) - { - MoveWindow(hWnd, x, y, 100, 100, 0); - std::cout << "CreateWindow at (" << x << "," << y<<")\n"; - } - - /////////////////////////////////////////////////// - PIXELFORMATDESCRIPTOR pfd = - { - sizeof(PIXELFORMATDESCRIPTOR), 1, - PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL , - PFD_TYPE_RGBA,16,0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0 - }; - hdc=GetDC(hWnd); - //////////////////////////////////// - int pixelformat = ChoosePixelFormat(hdc, &pfd); - DescribePixelFormat(hdc, pixelformat, sizeof(pfd), &pfd); - SetPixelFormat(hdc, pixelformat, &pfd); - hContext = wglCreateContext(hdc); - - } - void MakeCurrent() - { - wglMakeCurrent(hdc, hContext); - } -}; - -#else - -#include <unistd.h> -#include <X11/Xlib.h> -#include <GL/glx.h> - -class LiteWindow -{ - Display* xDisplay; - XVisualInfo* xVisual; - Window xWin; - GLXContext xContext; - Colormap xColormap; -public: - LiteWindow() - { - xDisplay = NULL; - xVisual = NULL; - xWin = 0; - xColormap = 0; - xContext = NULL; - } - int IsValid () - { - return xContext != NULL && glXIsDirect(xDisplay, xContext); - } - virtual ~LiteWindow() - { - if(xWin) XDestroyWindow(xDisplay, xWin); - if(xContext) glXDestroyContext(xDisplay, xContext); - if(xColormap) XFreeColormap(xDisplay, xColormap); - if(xDisplay) XCloseDisplay(xDisplay); - } - void Create(int x = 0, int y = 0, const char * display = NULL) - { - if(xDisplay) return; - if(display) std::cout << "Using display ["<<display<<"]\n"; - - xDisplay = XOpenDisplay(display && display[0] ? display : NULL); - if(xDisplay == NULL) return; - int attrib[] = {GLX_RGBA, GLX_RED_SIZE, 1, - GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, 0 }; - xVisual = glXChooseVisual(xDisplay, DefaultScreen(xDisplay), attrib); - if(xVisual == NULL) return; - xColormap = XCreateColormap( - xDisplay, RootWindow(xDisplay, xVisual->screen), - xVisual->visual, AllocNone); - - XSetWindowAttributes wa; - wa.event_mask = 0; - wa.border_pixel = 0; - wa.colormap = xColormap; - - xWin = XCreateWindow( xDisplay, RootWindow(xDisplay, xVisual->screen) , - x, y, 100, 100, 0, xVisual->depth, - InputOutput, xVisual->visual, - CWBorderPixel |CWColormap | CWEventMask, &wa); - - xContext = glXCreateContext(xDisplay, xVisual, 0, GL_TRUE); - } - void MakeCurrent() - { - if(xContext) glXMakeCurrent(xDisplay, xWin, xContext); - } -}; - -#endif - - -#endif - diff --git a/3rdparty/SiftGPU/src/SiftGPU/ProgramCG.cpp b/3rdparty/SiftGPU/src/SiftGPU/ProgramCG.cpp deleted file mode 100644 index 09c3f915..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/ProgramCG.cpp +++ /dev/null @@ -1,2765 +0,0 @@ -////////////////////////////////////////////////////////////////////////////// -// File: ProgramCG.cpp -// Author: Changchang Wu -// Description : implementation of cg related class. -// class ProgramCG A simple wrapper of Cg programs -// class ShaderBagCG cg shaders for SIFT -// class FilterCGGL cg gaussian filters for SIFT -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - -#if defined(CG_SIFTGPU_ENABLED) - -#include "GL/glew.h" - -#include <iostream> -#include <iomanip> -#include <vector> -#include <strstream> -#include <algorithm> -#include <stdlib.h> -#include <math.h> -#include <string.h> -using namespace std; - -#include "GlobalUtil.h" -#include "ProgramCG.h" -#include "GLTexImage.h" -#include "ShaderMan.h" -#include "FrameBufferObject.h" - - - -#if defined(_WIN32) - #pragma comment (lib, "../../lib/cg.lib") - #pragma comment (lib, "../../lib/cggl.lib") -#endif - -CGcontext ProgramCG::_Context =0; -CGprofile ProgramCG::_FProfile; - -////////////////////////////////////////////////////////////////////// -// Construction/Destruction -////////////////////////////////////////////////////////////////////// - -ProgramCG::ProgramCG() -{ - _programID = NULL; -} - -ProgramCG::~ProgramCG() -{ - if(_programID) cgDestroyProgram(_programID); -} - -ProgramCG::ProgramCG(const char *code, const char** cg_compile_args, CGprofile profile) -{ - _valid = 0; - _profile = profile; - GLint epos; - const char* ati_args[] = {"-po", "ATI_draw_buffers",0}; - const char* fp40_args[] = {"-ifcvt", "none","-unroll", "all", GlobalUtil::_UseFastMath? "-fastmath" : 0, 0}; - if(cg_compile_args == NULL) cg_compile_args = GlobalUtil::_IsNvidia? (GlobalUtil::_SupportFP40? fp40_args:NULL) : ati_args; - _programID = ::cgCreateProgram(_Context, CG_SOURCE, code, profile, NULL, cg_compile_args); - if(_programID) - { - cgGLLoadProgram(_programID ); - //_texParamID = cgGetNamedParameter(_programID, "tex"); - - glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &epos); - if(epos >=0) - { - std::cout<<cgGetProgramString(_programID, CG_COMPILED_PROGRAM)<<endl; - std::cerr<<glGetString(GL_PROGRAM_ERROR_STRING_ARB)<<endl; - }else - { - _valid = 1; - } - }else - { - std::cerr<<code<<endl; - glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &epos); - if(epos >=0) - { - std::cout<<cgGetProgramString(_programID, CG_COMPILED_PROGRAM)<<endl; - std::cerr<<glGetString(GL_PROGRAM_ERROR_STRING_ARB)<<endl; - }else - { - std::cout<<glGetString(GL_PROGRAM_ERROR_STRING_ARB)<<endl; - } - } - -} - -void ProgramCG::ErrorCallback() -{ - CGerror err = cgGetError(); - if(err) - { - std::cerr<< cgGetErrorString(err)<<endl; - } -} - - -void ProgramCG::InitContext() -{ - if(_Context == 0) - { - _Context = cgCreateContext(); - - ///////////// - _FProfile = cgGLGetLatestProfile(CG_GL_FRAGMENT); - cgGLSetOptimalOptions(_FProfile); - - if(GlobalUtil::_verbose) std::cout<<"Shader Profile: "<<cgGetProfileString(_FProfile)<<endl; - - cgSetErrorCallback(ErrorCallback); - } -} - -void ProgramCG::DestroyContext() -{ - cgDestroyContext(_Context); -} - -ShaderBagCG::ShaderBagCG() -{ - ProgramCG::InitContext(); -} - - -int ProgramCG::UseProgram() -{ - if(_programID) - { - cgGLEnableProfile(_profile); - cgGLBindProgram(_programID); - - return 1; - }else - { - return 0; - } -} - -void ShaderBagCG::UnloadProgram() -{ - - cgGLUnbindProgram(ProgramCG::_FProfile); - cgGLDisableProfile(ProgramCG::_FProfile); -} - - -void ShaderBagCG::LoadFixedShaders() -{ -// s_debug = new ProgramCG( "void main(float4 TexCoord0:TEXCOORD0, out float4 FragColor:COLOR0," -// "uniform samplerRECT tex){ gl_FragColor.rg = gl_TexCoord[0].st;}"); - - s_gray = new ProgramCG( - "void main(float4 TexCoord0 : TEXCOORD0, out float4 FragColor : COLOR0, uniform samplerRECT tex){\n" - "float intensity = dot(float3(0.299, 0.587, 0.114), texRECT(tex,TexCoord0.xy ).rgb);\n" - "FragColor= float4(intensity, intensity, intensity, 1.0);}" ); - - - s_sampling = new ProgramCG( - "void main(float4 TexCoord0 : TEXCOORD0, out float4 FragColor : COLOR0, uniform samplerRECT tex){\n" - "float4 cc = texRECT(tex, TexCoord0.xy); FragColor = float4(cc.rg, 0.0, 0.0); }" ); - - - s_zero_pass = new ProgramCG("void main(out float4 FragColor : COLOR0){FragColor = 0;}"); - - - ProgramCG * program; - s_margin_copy = program = new ProgramCG( - "void main(float4 texCoord0: TEXCOORD0, out float4 FragColor: COLOR0, \n" - "uniform samplerRECT tex, uniform float2 truncate){\n" - "FragColor = texRECT(tex, min(texCoord0.xy, truncate)); }"); - - _param_margin_copy_truncate = cgGetNamedParameter(*program, "truncate"); - - - s_grad_pass = new ProgramCG( - "void main (\n" - "float4 TexCC : TEXCOORD0, float4 TexLC : TEXCOORD1,\n" - "float4 TexRC : TEXCOORD2, float4 TexCD : TEXCOORD3, float4 TexCU : TEXCOORD4,\n" - "out float4 FragData0 : COLOR0, uniform samplerRECT tex)\n" - "{\n" - " float4 v1, v2, gg;\n" - " float4 cc = texRECT(tex, TexCC.xy);\n" - " gg.x = texRECT(tex, TexLC.xy).r;\n" - " gg.y = texRECT(tex, TexRC.xy).r;\n" - " gg.z = texRECT(tex, TexCD.xy).r;\n" - " gg.w = texRECT(tex, TexCU.xy).r;\n" - " float2 dxdy = (gg.yw - gg.xz); \n" - " float grad = 0.5*length(dxdy);\n" - " float theta = grad==0? 0: atan2(dxdy.y, dxdy.x);\n" - " FragData0 = float4(cc.rg, grad, theta);\n" - "}\n\0"); - - - if(GlobalUtil::_SupportFP40) - { - //use the packing mode for cpu list reshape and two orientations - if(GlobalUtil::_MaxOrientation != 2) GlobalUtil::_OrientationPack2 = 0; - - LoadOrientationShader(); - - - if(GlobalUtil::_DescriptorPPT) LoadDescriptorShader(); - - }else - { - s_orientation = program = new ProgramCG( - "void main(out float4 FragColor : COLOR0, \n" - " uniform samplerRECT fTex, uniform samplerRECT oTex, \n" - " uniform float size, \n" - " in float2 tpos : TEXCOORD0){\n" - " float4 cc = texRECT(fTex, tpos);\n" - " float4 oo = texRECT(oTex, cc.rg);\n" - " FragColor = float4(cc.rg, oo.a, size);}"); - _param_orientation_gtex= cgGetNamedParameter(*program, "oTex"); - _param_orientation_size= cgGetNamedParameter(*program, "size"); - - - /// - GlobalUtil::_FullSupported = 0; - GlobalUtil::_MaxOrientation = 0; //0 for simplified version - GlobalUtil::_DescriptorPPT = 0; - std::cerr<<"Orientation simplified on this hardware"<<endl; - std::cerr<<"Descriptor ignored on this hardware"<<endl; - } - - -} - -void ShaderBagCG::LoadDisplayShaders() -{ - s_copy_key = new ProgramCG( - "void main(float4 TexCoord0 : TEXCOORD0, out float4 FragColor : COLOR0, uniform samplerRECT tex){\n" - "FragColor.rg= texRECT(tex, TexCoord0.xy).rg; FragColor.ba = float2(0,1); }"); - - //shader used to write a vertex buffer object - //which is used to draw the quads of each feature - ProgramCG * program; - s_vertex_list = program = new ProgramCG( - "void main(in float4 TexCoord0: TEXCOORD0,\n" - "uniform float4 sizes, \n" - "uniform samplerRECT tex, \n" - "out float4 FragColor: COLOR0){\n" - "float fwidth = sizes.y; \n" - "float twidth = sizes.z; \n" - "float rwidth = sizes.w; \n" - "float index = 0.1*(fwidth*floor(TexCoord0.y) + TexCoord0.x);\n" - "float px = fmod(index, twidth);\n" - "float2 tpos= floor(float2(px, index*rwidth))+0.5;\n" - "float4 cc = texRECT(tex, tpos );\n" - "float size = cc.a * 3.0f;//sizes.x;// \n" - "FragColor.zw = float2(0.0, 1.0);\n" - "if(any(cc.xy <=0)) {FragColor.xy = cc.xy;}else \n" - "{\n" - " float type = frac(px);\n" - " float2 dxy; float s, c;\n" - " dxy.x = type < 0.1 ? 0 : ((type <0.5 || type > 0.9)? size : -size);\n" - " dxy.y = type < 0.2 ? 0 : ((type < 0.3 || type > 0.7 )? -size :size); \n" - " sincos(cc.b, s, c);\n" - " FragColor.x = cc.x + c*dxy.x-s*dxy.y;\n" - " FragColor.y = cc.y + c*dxy.y+s*dxy.x;}\n" - "}\n\0"); - /*FragColor = float4(tpos, 0.0, 1.0);}\n\0");*/ - - _param_genvbo_size = cgGetNamedParameter(*program, "sizes"); - - - s_display_gaussian = new ProgramCG( - "void main(float4 TexCoord0 : TEXCOORD0, out float4 FragColor : COLOR0, uniform samplerRECT tex){\n" - "float r = texRECT(tex, TexCoord0.xy).r;\n" - "FragColor = float4(r, r, r, 1.0);}"); - - - s_display_dog = new ProgramCG( - "void main(float4 TexCoord0 : TEXCOORD0, out float4 FragColor : COLOR0, uniform samplerRECT tex){\n" - "float g = (0.5+20.0*texRECT(tex, TexCoord0.xy).g);\n" - "FragColor = float4(g, g, g, 1.0);}" ); - - - s_display_grad = new ProgramCG( - "void main(float4 TexCoord0 : TEXCOORD0, out float4 FragColor : COLOR0, uniform samplerRECT tex){\n" - "float4 cc = texRECT(tex, TexCoord0.xy); FragColor = float4(5.0 * cc.bbb, 1.0); }"); - - - s_display_keys= new ProgramCG( - "void main(float4 TexCoord0 : TEXCOORD0, out float4 FragColor : COLOR0, uniform samplerRECT tex){\n" - "float4 cc = texRECT(tex, TexCoord0.xy);\n" - "if(cc.r ==1.0) FragColor = float4(1.0, 0, 0,1.0); \n" - "else {if (cc.r ==0.5) FragColor = float4(0.0,1.0,0.0,1.0); else discard;}}"); - -} - -void ShaderBagCG::SetMarginCopyParam(int xmax, int ymax) -{ - float truncate[2] = {xmax - 0.5f , ymax - 0.5f}; - cgGLSetParameter2fv(_param_margin_copy_truncate, truncate); -} - - -int ShaderBagCG::LoadKeypointShaderMR(float threshold, float edge_threshold) -{ - char buffer[10240]; - float threshold0 = threshold * 0.8f; - float threshold1 = threshold; - float threshold2 = (edge_threshold+1)*(edge_threshold+1)/edge_threshold; - int max_refine = max(2, GlobalUtil::_SubpixelLocalization); - ostrstream out(buffer, 10240); - - out << "#define THRESHOLD0 " << threshold0 << "\n" - "#define THRESHOLD1 " << threshold1 << "\n" - "#define THRESHOLD2 " << threshold2 << "\n" - "#define MAX_REFINE " << max_refine << "\n"; - out<< - "void main (\n" - "float4 TexCC : TEXCOORD0, float4 TexLC : TEXCOORD1,\n" - "float4 TexRC : TEXCOORD2, float4 TexCD : TEXCOORD3, \n" - "float4 TexCU : TEXCOORD4, float4 TexLD : TEXCOORD5, \n" - "float4 TexLU : TEXCOORD6, float4 TexRD : TEXCOORD7,\n" - "out float4 FragData0 : COLOR0, out float4 FragData1 : COLOR1, \n" - "uniform samplerRECT tex, uniform samplerRECT texU, uniform samplerRECT texD)\n" - "{\n" - " float4 v1, v2, gg;\n" - " float2 TexRU = float2(TexRC.x, TexCU.y); \n" - " float4 cc = texRECT(tex, TexCC.xy);\n" - " v1.x = texRECT(tex, TexLC.xy).g;\n" - " gg.x = texRECT(tex, TexLC.xy).r;\n" - " v1.y = texRECT(tex, TexRC.xy).g;\n" - " gg.y = texRECT(tex, TexRC.xy).r;\n" - " v1.z = texRECT(tex, TexCD.xy).g;\n" - " gg.z = texRECT(tex, TexCD.xy).r;\n" - " v1.w = texRECT(tex, TexCU.xy).g;\n" - " gg.w = texRECT(tex, TexCU.xy).r;\n" - " v2.x = texRECT(tex, TexLD.xy).g;\n" - " v2.y = texRECT(tex, TexLU.xy).g;\n" - " v2.z = texRECT(tex, TexRD.xy).g;\n" - " v2.w = texRECT(tex, TexRU.xy).g;\n" - " float2 dxdy = 0.5*(gg.yw - gg.xz); \n" - " float grad = length(dxdy);\n" - " float theta = grad==0? 0: atan2(dxdy.y, dxdy.x);\n" - " FragData0 = float4(cc.rg, grad, theta);\n" - << - " float dog = 0.0; \n" - " FragData1 = float4(0, 0, 0, 0); \n" - " float2 v3; float4 v4, v5, v6;\n" - << - " if( cc.g > THRESHOLD0 && all(cc.gggg > max(v1, v2)))\n" - " {\n" - " v3.x = texRECT(texU, TexCC.xy).g;\n" - " v4.x = texRECT(texU, TexLC.xy).g;\n" - " v4.y = texRECT(texU, TexRC.xy).g;\n" - " v4.z = texRECT(texU, TexCD.xy).g;\n" - " v4.w = texRECT(texU, TexCU.xy).g;\n" - " v6.x = texRECT(texU, TexLD.xy).g;\n" - " v6.y = texRECT(texU, TexLU.xy).g;\n" - " v6.z = texRECT(texU, TexRD.xy).g;\n" - " v6.w = texRECT(texU, TexRU.xy).g;\n" - " if(cc.g < v3.x || any(cc.gggg<v4.xyzw || cc.gggg<v6.xyzw))return; \n" - " v3.y = texRECT(texD, TexCC.xy).g;\n" - " v5.x = texRECT(texD, TexLC.xy).g;\n" - " v5.y = texRECT(texD, TexRC.xy).g;\n" - " v5.z = texRECT(texD, TexCD.xy).g;\n" - " v5.w = texRECT(texD, TexCU.xy).g;\n" - " v6.x = texRECT(texD, TexLD.xy).g;\n" - " v6.y = texRECT(texD, TexLU.xy).g;\n" - " v6.z = texRECT(texD, TexRD.xy).g;\n" - " v6.w = texRECT(texD, TexRU.xy).g;\n" - " if(cc.g < v3.y || any(cc.gggg<v5.xyzw || cc.gggg<v6.xyzw))return; \n" - " dog = 1.0; \n" - " }\n" - //the minimum case - << - " else if(cc.g < -THRESHOLD0 && all(cc.gggg < min(v1, v2)))\n" - " {\n" - " v3.x = texRECT(texU, TexCC.xy).g;\n" - " v4.x = texRECT(texU, TexLC.xy).g;\n" - " v4.y = texRECT(texU, TexRC.xy).g;\n" - " v4.z = texRECT(texU, TexCD.xy).g;\n" - " v4.w = texRECT(texU, TexCU.xy).g;\n" - " v6.x = texRECT(texU, TexLD.xy).g;\n" - " v6.y = texRECT(texU, TexLU.xy).g;\n" - " v6.z = texRECT(texU, TexRD.xy).g;\n" - " v6.w = texRECT(texU, TexRU.xy).g;\n" - " if(cc.g > v3.x || any(cc.gggg>v4.xyzw || cc.gggg>v6.xyzw))return; \n" - " v3.y = texRECT(texD, TexCC.xy).g;\n" - " v5.x = texRECT(texD, TexLC.xy).g;\n" - " v5.y = texRECT(texD, TexRC.xy).g;\n" - " v5.z = texRECT(texD, TexCD.xy).g;\n" - " v5.w = texRECT(texD, TexCU.xy).g;\n" - " v6.x = texRECT(texD, TexLD.xy).g;\n" - " v6.y = texRECT(texD, TexLU.xy).g;\n" - " v6.z = texRECT(texD, TexRD.xy).g;\n" - " v6.w = texRECT(texD, TexRU.xy).g;\n" - " if(cc.g > v3.y || any(cc.gggg>v5.xyzw || cc.gggg>v6.xyzw))return; \n" - " dog = 0.5 ; \n" - " }\n" - " else\n" - " return;\n" - << - " int i = 0; \n" - " float2 offset = float2(0, 0);\n" - " float2 offsets = float2(0, 0);\n" - " float3 dxys; bool key_moved; \n" - " float fx, fy, fs; \n" - " float fxx, fyy, fxy; \n" - " float fxs, fys, fss; \n" - " do\n" - " {\n" - " dxys = float3(0, 0, 0);\n" - " offset = float2(0, 0);\n" - " float4 D2 = v1.xyzw - cc.gggg;\n" - " fxx = D2.x + D2.y;\n" - " fyy = D2.z + D2.w;\n" - " float2 D4 = v2.xw - v2.yz;\n" - " fxy = 0.25*(D4.x + D4.y);\n" - " float2 D5 = 0.5*(v1.yw-v1.xz); \n" - " fx = D5.x;\n" - " fy = D5.y ; \n" - " fs = 0.5*( v3.x - v3.y ); \n" - " fss = v3.x + v3.y - cc.g - cc.g;\n" - " fxs = 0.25 * ( v4.y + v5.x - v4.x - v5.y);\n" - " fys = 0.25 * ( v4.w + v5.z - v4.z - v5.w);\n" - " float4 A0, A1, A2 ; \n" - " A0 = float4(fxx, fxy, fxs, -fx); \n" - " A1 = float4(fxy, fyy, fys, -fy); \n" - " A2 = float4(fxs, fys, fss, -fs); \n" - " float3 x3 = abs(float3(fxx, fxy, fxs)); \n" - " float maxa = max(max(x3.x, x3.y), x3.z); \n" - " if(maxa > 1e-10 ) \n" - " {\n" - " if(x3.y ==maxa ) \n" - " { \n" - " float4 TEMP = A1; A1 = A0; A0 = TEMP; \n" - " }else if( x3.z == maxa ) \n" - " { \n" - " float4 TEMP = A2; A2 = A0; A0 = TEMP; \n" - " } \n" - " A0 /= A0.x; \n" - " A1 -= A1.x * A0; \n" - " A2 -= A2.x * A0; \n" - " float2 x2 = abs(float2(A1.y, A2.y)); \n" - " if( x2.y > x2.x ) \n" - " { \n" - " float3 TEMP = A2.yzw; \n" - " A2.yzw = A1.yzw; \n" - " A1.yzw = TEMP; \n" - " x2.x = x2.y; \n" - " } \n" - " if(x2.x > 1e-10) \n" - " {\n" - " A1.yzw /= A1.y; \n" - " A2.yzw -= A2.y * A1.yzw; \n" - " if(abs(A2.z) > 1e-10) \n" - " {\n" - // compute dx, dy, ds: - << - " dxys.z = A2.w /A2.z; \n" - " dxys.y = A1.w - dxys.z*A1.z; \n" - " dxys.x = A0.w - dxys.z*A0.z - dxys.y*A0.y; \n" - " }\n" - " }\n" - " }\n" - " offset.x = dxys.x > 0.6 ? 1 : 0 + dxys.x < -0.6 ? -1 : 0;\n" - " offset.y = dxys.y > 0.6 ? 1 : 0 + dxys.y < - 0.6? -1 : 0;\n" - " i++; key_moved = i < MAX_REFINE && any(abs(offset)>0) ; \n" - " if(key_moved)\n" - " {\n" - " offsets += offset; \n" - " cc = texRECT(tex, TexCC.xy + offsets);\n" - " v1.x = texRECT(tex , TexLC.xy + offsets).g;\n" - " v1.y = texRECT(tex , TexRC.xy + offsets).g;\n" - " v1.z = texRECT(tex , TexCD.xy + offsets).g;\n" - " v1.w = texRECT(tex , TexCU.xy + offsets).g;\n" - " v2.x = texRECT(tex , TexLD.xy + offsets).g;\n" - " v2.y = texRECT(tex , TexLU.xy + offsets).g;\n" - " v2.z = texRECT(tex , TexRD.xy + offsets).g;\n" - " v2.w = texRECT(tex , TexRU.xy + offsets).g;\n" - " v3.x = texRECT(texU, TexCC.xy + offsets).g;\n" - " v4.x = texRECT(texU, TexLC.xy + offsets).g;\n" - " v4.y = texRECT(texU, TexRC.xy + offsets).g;\n" - " v4.z = texRECT(texU, TexCD.xy + offsets).g;\n" - " v4.w = texRECT(texU, TexCU.xy + offsets).g;\n" - " v3.y = texRECT(texD, TexCC.xy + offsets).g;\n" - " v5.x = texRECT(texD, TexLC.xy + offsets).g;\n" - " v5.y = texRECT(texD, TexRC.xy + offsets).g;\n" - " v5.z = texRECT(texD, TexCD.xy + offsets).g;\n" - " v5.w = texRECT(texD, TexCU.xy + offsets).g;\n" - " }\n" - " }while(key_moved);\n" - << - " bool test1 = (abs(cc.g + 0.5*dot(float3(fx, fy, fs), dxys ))> THRESHOLD1) ;\n" - " float test2_v1= fxx*fyy - fxy *fxy; \n" - " float test2_v2 = (fxx+fyy); \n" - " test2_v2 = test2_v2*test2_v2;\n" - " bool test2 = test2_v1>0 && test2_v2 < THRESHOLD2 * test2_v1; \n " - //keep the point when the offset is less than 1 - << - " FragData1 = test1 && test2 && all( abs(dxys) < 1)? float4( dog, dxys.xy+offsets, dxys.z) : float4(0, 0, 0, 0); \n" - "}\n" - <<'\0'; - - ProgramCG * program; - s_keypoint = program = new ProgramCG(buffer); - //parameter - _param_dog_texu = cgGetNamedParameter(*program, "texU"); - _param_dog_texd = cgGetNamedParameter(*program, "texD"); - - return 1; - -} - -//keypoint detection shader -//1. compare with 26 neighbours -//2. sub-pixel sub-scale localization -//3. output: [dog, offset(x,y,s)] - -void ShaderBagCG:: LoadKeypointShader(float threshold, float edge_threshold) -{ - char buffer[10240]; - float threshold0 = threshold* (GlobalUtil::_SubpixelLocalization?0.8f:1.0f); - float threshold1 = threshold; - float threshold2 = (edge_threshold+1)*(edge_threshold+1)/edge_threshold; - ostrstream out(buffer, 10240); - out<<setprecision(8); - streampos pos; - //tex(X)(Y) - //X: (CLR) (CENTER 0, LEFT -1, RIGHT +1) - //Y: (CDU) (CENTER 0, DOWN -1, UP +1) - - out << "#define THRESHOLD0 " << threshold0 << "\n" - "#define THRESHOLD1 " << threshold1 << "\n" - "#define THRESHOLD2 " << threshold2 << "\n"; - out<< - "void main (\n" - "float4 TexCC : TEXCOORD0, float4 TexLC : TEXCOORD1,\n" - "float4 TexRC : TEXCOORD2, float4 TexCD : TEXCOORD3, \n" - "float4 TexCU : TEXCOORD4, float4 TexLD : TEXCOORD5, \n" - "float4 TexLU : TEXCOORD6, float4 TexRD : TEXCOORD7,\n" - "out float4 FragData0 : COLOR0, out float4 FragData1 : COLOR1, \n" - "uniform samplerRECT tex, uniform samplerRECT texU, uniform samplerRECT texD)\n" - "{\n" - " float4 v1, v2, gg;\n" - " float2 TexRU = float2(TexRC.x, TexCU.y); \n" - " float4 cc = texRECT(tex, TexCC.xy);\n" - " v1.x = texRECT(tex, TexLC.xy).g;\n" - " gg.x = texRECT(tex, TexLC.xy).r;\n" - " v1.y = texRECT(tex, TexRC.xy).g;\n" - " gg.y = texRECT(tex, TexRC.xy).r;\n" - " v1.z = texRECT(tex, TexCD.xy).g;\n" - " gg.z = texRECT(tex, TexCD.xy).r;\n" - " v1.w = texRECT(tex, TexCU.xy).g;\n" - " gg.w = texRECT(tex, TexCU.xy).r;\n" - " v2.x = texRECT(tex, TexLD.xy).g;\n" - " v2.y = texRECT(tex, TexLU.xy).g;\n" - " v2.z = texRECT(tex, TexRD.xy).g;\n" - " v2.w = texRECT(tex, TexRU.xy).g;\n" - " float2 dxdy = (gg.yw - gg.xz); \n" - " float grad = 0.5*length(dxdy);\n" - " float theta = grad==0? 0: atan2(dxdy.y, dxdy.x);\n" - " FragData0 = float4(cc.rg, grad, theta);\n" - - //test against 8 neighbours - //use variable to identify type of extremum - //1.0 for local maximum and 0.5 for minimum - << - " float dog = 0.0; \n" - " FragData1 = float4(0, 0, 0, 0); \n" - " dog = cc.g > THRESHOLD0 && all(cc.gggg > max(v1, v2))?1.0: 0.0;\n" - " dog = cc.g < -THRESHOLD0 && all(cc.gggg < min(v1, v2))?0.5: dog;\n"; - - pos = out.tellp(); - //do edge supression first.. - //vector v1 is < (-1, 0), (1, 0), (0,-1), (0, 1)> - //vector v2 is < (-1,-1), (-1,1), (1,-1), (1, 1)> - - out<< - " if(dog == 0.0) return;\n" - " float fxx, fyy, fxy; \n" - " float4 D2 = v1.xyzw - cc.gggg;\n" - " float2 D4 = v2.xw - v2.yz;\n" - " fxx = D2.x + D2.y;\n" - " fyy = D2.z + D2.w;\n" - " fxy = 0.25*(D4.x + D4.y);\n" - " float fxx_plus_fyy = fxx + fyy;\n" - " float score_up = fxx_plus_fyy*fxx_plus_fyy; \n" - " float score_down = (fxx*fyy - fxy*fxy);\n" - " if( score_down <= 0 || score_up > THRESHOLD2 * score_down)return;\n" - //... - << - " float2 D5 = 0.5*(v1.yw-v1.xz); \n" - " float fx = D5.x, fy = D5.y ; \n" - " float fs, fss , fxs, fys ; \n" - " float2 v3; float4 v4, v5, v6;\n" - //read 9 pixels of upper level - << - " v3.x = texRECT(texU, TexCC.xy).g;\n" - " v4.x = texRECT(texU, TexLC.xy).g;\n" - " v4.y = texRECT(texU, TexRC.xy).g;\n" - " v4.z = texRECT(texU, TexCD.xy).g;\n" - " v4.w = texRECT(texU, TexCU.xy).g;\n" - " v6.x = texRECT(texU, TexLD.xy).g;\n" - " v6.y = texRECT(texU, TexLU.xy).g;\n" - " v6.z = texRECT(texU, TexRD.xy).g;\n" - " v6.w = texRECT(texU, TexRU.xy).g;\n" - //compare with 9 pixels of upper level - //read and compare with 9 pixels of lower level - //the maximum case - << - " if(dog == 1.0)\n" - " {\n" - " bool4 test = cc.gggg < max(v4, v6); \n" - " if(cc.g < v3.x || any(test.xy||test.zw))return; \n" - " v3.y = texRECT(texD, TexCC.xy).g;\n" - " v5.x = texRECT(texD, TexLC.xy).g;\n" - " v5.y = texRECT(texD, TexRC.xy).g;\n" - " v5.z = texRECT(texD, TexCD.xy).g;\n" - " v5.w = texRECT(texD, TexCU.xy).g;\n" - " v6.x = texRECT(texD, TexLD.xy).g;\n" - " v6.y = texRECT(texD, TexLU.xy).g;\n" - " v6.z = texRECT(texD, TexRD.xy).g;\n" - " v6.w = texRECT(texD, TexRU.xy).g;\n" - " test = cc.gggg<max(v5, v6); \n" - " if(cc.g < v3.y || any(test.xy||test.zw))return; \n" - " }\n" - //the minimum case - << - " else{\n" - " bool4 test = cc.gggg>min(v4, v6); \n" - " if(cc.g > v3.x || any(test.xy||test.zw))return; \n" - " v3.y = texRECT(texD, TexCC.xy).g;\n" - " v5.x = texRECT(texD, TexLC.xy).g;\n" - " v5.y = texRECT(texD, TexRC.xy).g;\n" - " v5.z = texRECT(texD, TexCD.xy).g;\n" - " v5.w = texRECT(texD, TexCU.xy).g;\n" - " v6.x = texRECT(texD, TexLD.xy).g;\n" - " v6.y = texRECT(texD, TexLU.xy).g;\n" - " v6.z = texRECT(texD, TexRD.xy).g;\n" - " v6.w = texRECT(texD, TexRU.xy).g;\n" - " test = cc.gggg>min(v5, v6); \n" - " if(cc.g > v3.y || any(test.xy||test.zw))return; \n" - " }\n"; - - if(GlobalUtil::_SubpixelLocalization) - - // sub-pixel localization FragData1 = float4(dog, 0, 0, 0); return; - out << - " fs = 0.5*( v3.x - v3.y ); //bug fix 9/12/2007 \n" - " fss = v3.x + v3.y - cc.g - cc.g;\n" - " fxs = 0.25 * ( v4.y + v5.x - v4.x - v5.y);\n" - " fys = 0.25 * ( v4.w + v5.z - v4.z - v5.w);\n" - - ///////////////////////////////////////////////////////////////// - // let dog difference be quatratic function of dx, dy, ds; - // df(dx, dy, ds) = fx * dx + fy*dy + fs * ds + - // + 0.5 * ( fxx * dx * dx + fyy * dy * dy + fss * ds * ds) - // + (fxy * dx * dy + fxs * dx * ds + fys * dy * ds) - // (fx, fy, fs, fxx, fyy, fss, fxy, fxs, fys are the derivatives) - - //the local extremum satisfies - // df/dx = 0, df/dy = 0, df/dz = 0 - - //that is - // |-fx| | fxx fxy fxs | |dx| - // |-fy| = | fxy fyy fys | * |dy| - // |-fs| | fxs fys fss | |ds| - // need to solve dx, dy, ds - - // Use Gauss elimination to solve the linear system - << - " float3 dxys = float3(0.0); \n" - " float4 A0, A1, A2 ; \n" - " A0 = float4(fxx, fxy, fxs, -fx); \n" - " A1 = float4(fxy, fyy, fys, -fy); \n" - " A2 = float4(fxs, fys, fss, -fs); \n" - " float3 x3 = abs(float3(fxx, fxy, fxs)); \n" - " float maxa = max(max(x3.x, x3.y), x3.z); \n" - " if(maxa >= 1e-10 ) { \n" - " if(x3.y ==maxa ) \n" - " { \n" - " float4 TEMP = A1; A1 = A0; A0 = TEMP; \n" - " }else if( x3.z == maxa ) \n" - " { \n" - " float4 TEMP = A2; A2 = A0; A0 = TEMP; \n" - " } \n" - " A0 /= A0.x; \n" - " A1 -= A1.x * A0; \n" - " A2 -= A2.x * A0; \n" - " float2 x2 = abs(float2(A1.y, A2.y)); \n" - " if( x2.y > x2.x ) \n" - " { \n" - " float3 TEMP = A2.yzw; \n" - " A2.yzw = A1.yzw; \n" - " A1.yzw = TEMP; \n" - " x2.x = x2.y; \n" - " } \n" - " if(x2.x >= 1e-10) { \n" - " A1.yzw /= A1.y; \n" - " A2.yzw -= A2.y * A1.yzw; \n" - " if(abs(A2.z) >= 1e-10) { \n" - // compute dx, dy, ds: - << - " dxys.z = A2.w /A2.z; \n" - " dxys.y = A1.w - dxys.z*A1.z; \n" - " dxys.x = A0.w - dxys.z*A0.z - dxys.y*A0.y; \n" - - //one more threshold which I forgot in versions prior to 286 - << - " bool bugfix_test = (abs(cc.g + 0.5*dot(float3(fx, fy, fs), dxys )) < THRESHOLD1) ;\n" - " if(bugfix_test || any(abs(dxys) >= 1.0)) dog = 0; \n" - " }}}\n" - //keep the point when the offset is less than 1 - << - " FragData1 = float4( dog, dxys); \n" - "}\n" <<'\0'; - - else out<< - " FragData1 = float4( dog, 0, 0, 0) ; \n" - "}\n" <<'\0'; - - ProgramCG * program; - s_keypoint = program = new ProgramCG(buffer); - if(!program->IsValidProgram()) - { - delete program; - out.seekp(pos); - out << - " FragData1 = float4( fabs(cc.g) > 2.0 * THRESHOLD0? dog : 0, 0, 0, 0) ; \n" - "}\n" <<'\0'; - s_keypoint = program = new ProgramCG(buffer); - GlobalUtil::_SubpixelLocalization = 0; - std::cerr<<"Detection simplified on this hardware"<<endl; - } - //parameter - _param_dog_texu = cgGetNamedParameter(*program, "texU"); - _param_dog_texd = cgGetNamedParameter(*program, "texD"); - - - - -} - - -void ShaderBagCG::SetDogTexParam(int texU, int texD) -{ - cgGLSetTextureParameter(_param_dog_texu, texU); - cgGLEnableTextureParameter(_param_dog_texu); - cgGLSetTextureParameter(_param_dog_texd, texD); - cgGLEnableTextureParameter(_param_dog_texd); -} - -void ShaderBagCG::SetGenListStepParam(int tex, int tex0) -{ - cgGLSetTextureParameter(_param_genlist_step_tex, tex); - cgGLEnableTextureParameter(_param_genlist_step_tex); - cgGLSetTextureParameter(_param_genlist_step_tex0, tex0); - cgGLEnableTextureParameter(_param_genlist_step_tex0); -} - -void ShaderBagCG::SetGenVBOParam(float width, float fwidth, float size) -{ - float sizes[4] = {size*3.0f, fwidth, width, 1.0f/width}; - cgGLSetParameter4fv(_param_genvbo_size, sizes); -} - - -ProgramGPU* FilterGLCG::CreateFilterH(float kernel[], float offset[], int width) -{ - - - char buffer[10240]; - ostrstream out(buffer, 10240); - - out<<setprecision(8); - - if(GlobalUtil::_BetaFilter) - { - out<< "void main(uniform samplerRECT tex,"; - out<<"\n\tin float4 TexCoord0: TEXCOORD0,"; - out<<"\n\tout float4 FragColor : COLOR0 )"; - out<<"\n{\n\tfloat4 intensity4 = float4(0, 0, 0, 0), data;\n"; - out<<"float or = texRECT(tex, TexCoord0.xy).r, intensity;\n"; - - for(int i = 0; i< width; i+=4) - { - out <<"data = float4("; - for(int j = i; j < i + 4; j++) - { - if(j != i) out <<", \n"; - if(j >= width) - { - out<<"0"; - }else if(offset[j]==0.0) - { - out<<"or"; - }else - { - out<<"texRECT(tex, TexCoord0.xy + float2(float("<<offset[j] <<") , 0)).r"; - } - } - out << ");\n"; - out << "intensity4 += data * float4("; - for(int k = i; k < i + 4; k++) - { - if(k != i) out <<", "; - if(k >= width) out<<"0"; - else out<<kernel[k]; - } - out << ");\n"; - - } - out << "intensity4.xy += intensity4.zw;\n"; - out << "intensity = intensity4.x + intensity4.y;\n"; - }else - { - out<< "void main(uniform samplerRECT tex,"; - out<<"\n\tin float4 TexCoord0: TEXCOORD0,"; - out<<"\n\tout float4 FragColor : COLOR0 )"; - out<<"\n{\n\tfloat intensity = 0.0 ; float2 pos;\n"; - - for(int i = 0; i< width; i++) - { - if(offset[i]==0.0) - { - out<<"float or = texRECT(tex, TexCoord0.xy).r;\n"; - out<<"intensity+= or * "<<kernel[i]<<";\n"; - - }else - { - out<<"pos = TexCoord0.xy + float2(float("<<offset[i] <<") , 0);\n"; - out<<"intensity+= "<<kernel[i]<<"*texRECT(tex, pos).r;\n"; - } - } - } - //copy original data to red channel - out<<"FragColor.r = or;\n"; - out<<"FragColor.b = intensity;}\n"<<'\0'; - - return new ProgramCG( buffer); -} - - -ProgramGPU* FilterGLCG::CreateFilterV(float kernel[], float offset[], int height) -{ - char buffer[10240]; - ostrstream out(buffer, 10240); - out<<setprecision(8); - - if(GlobalUtil::_BetaFilter) - { - out<< "void main(uniform samplerRECT tex,"; - out<<"\n\tin float4 TexCoord0: TEXCOORD0,"; - out<<"\n\tout float4 FragColor : COLOR0 )"; - out<<"\n{\n\tfloat4 intensity4 = float4(0, 0, 0, 0), data;\n"; - out<<"float2 orb = texRECT(tex, TexCoord0.xy).rb; float intensity;\n"; - - for(int i = 0; i< height; i+=4) - { - out <<"data = float4("; - for(int j = i; j < i + 4; j++) - { - if(j != i) out <<", \n"; - if(j >= height) - { - out<<"0"; - }else if(offset[j]==0.0) - { - out<<"orb.y"; - }else - { - out<<"texRECT(tex, TexCoord0.xy + float2(0, float("<<offset[j] <<"))).b"; - } - } - out << ");\n"; - out << "intensity4 += data * float4("; - for(int k = i; k < i + 4; k++) - { - if(k != i) out <<", "; - if(k >= height) out<<"0"; - else out<<kernel[k]; - } - out << ");\n"; - - } - out << "intensity4.xy += intensity4.zw;\n"; - out << "intensity = intensity4.x + intensity4.y;\n"; - }else - { - out<< "void main(uniform samplerRECT tex,"; - out<<"\n\tin float4 TexCoord0: TEXCOORD0,"; - out<<"\n\tout float4 FragColor : COLOR0 )"; - out<<"\n{\n\tfloat intensity = 0.0 ; float2 pos;\n"; - - for(int i = 0; i< height; i++) - { - if(offset[i]==0.0) - { - out<<"float2 orb = texRECT(tex, TexCoord0.xy).rb;\n"; - out<<"intensity+= orb.y * "<<kernel[i]<<";\n"; - - }else - { - out<<"pos = TexCoord0.xy + float2(0, float("<<offset[i] <<"));\n"; - out<<"intensity+= "<<kernel[i]<<"*texRECT(tex, pos).b;\n"; - } - } - } - out<<"FragColor.b = orb.y;\n"; - out<<"FragColor.g = intensity - orb.x;\n"; // difference of gaussian.. - out<<"FragColor.r = intensity;}\n"<<'\0'; - - return new ProgramCG( buffer); -} - - -ProgramGPU* FilterGLCG::CreateFilterHPK(float kernel[], float offset[], int width) -{ - //both h and v are packed... - int i, j , xw, xwn; - int halfwidth = width >>1; - float * pf = kernel + halfwidth; - int nhpixel = (halfwidth+1)>>1; //how many neighbour pixels need to be looked up - int npixel = (nhpixel<<1)+1;// - char buffer[10240]; - float weight[3]; - ostrstream out(buffer, 10240); - out<<setprecision(8); - - out<< "void main(uniform samplerRECT tex, float4 TexCoord0 : TEXCOORD0, out float4 FragColor : COLOR0 ){\n"; - out<< "float4 result = float4(0, 0, 0, 0); \nfloat4 pc; float2 coord; \n"; - ///use multi texture coordinate because nhpixels can be at most 3 - for( i = 0 ; i < npixel ; i++) - { - - out<<"coord = TexCoord0.xy + float2(float("<<i-nhpixel<<"),0);\n"; - out<<"pc=texRECT(tex, coord);\n"; - if(GlobalUtil::_PreciseBorder) out<<"if(coord.x < 0) pc = pc.rrbb;\n"; - - //for each sub-pixel j in center, the weight of sub-pixel k - xw = (i - nhpixel)*2; - for( j = 0; j < 3; j++) - { - xwn = xw + j -1; - weight[j] = xwn < -halfwidth || xwn > halfwidth? 0 : pf[xwn]; - } - //if(weight[1]!=0.0) out<<"FragColor += "<<weight[1]<<"*pc;\n"; - //out<<"FragColor += float4("<<weight[2]<<","<<weight[0]<<","<<weight[2]<<","<<weight[0]<<")*pc.grab;\n"; - - if(weight[1] == 0.0) - { - out<<"result += float4("<<weight[2]<<","<<weight[0]<<","<<weight[2]<<","<<weight[0]<<")*pc.grab;\n"; - } - else - { - out<<"result += float4("<<weight[1]<<", "<<weight[0]<<", "<<weight[1]<<", "<<weight[0]<<")*pc.rrbb;\n"; - out<<"result += float4("<<weight[2]<<", "<<weight[1]<<", "<<weight[2]<<", "<<weight[1]<<")*pc.ggaa;\n"; - } - - } - out<< - " FragColor = result; }\n"<<'\0'; - return new ProgramCG( buffer); -} - -ProgramGPU* FilterGLCG::CreateFilterVPK(float kernel[], float offset[], int height) -{ - - //both h and v are packed... - int i, j , yw, ywn; - int halfh = height >>1; - float * pf = kernel + halfh; - int nhpixel = (halfh+1)>>1; //how many neighbour pixels need to be looked up - int npixel = (nhpixel<<1)+1;// - char buffer[10240]; - float weight[3]; - ostrstream out(buffer, 10240); - out<<setprecision(8); - - out<< "void main(uniform samplerRECT tex, float4 TexCoord0 : TEXCOORD0, out float4 FragColor : COLOR0 ){\n"; - out<< "float4 result = float4(0, 0, 0, 0);\nfloat4 pc; float2 coord;\n"; - ///use multi texture coordinate because nhpixels can be at most 3 - - for( i = 0 ; i < npixel ; i++) - { - - out<<"coord = TexCoord0.xy + float2(0, float("<<i-nhpixel<<"));\n"; - out<<"pc=texRECT(tex, coord);\n"; - if(GlobalUtil::_PreciseBorder) out<<"if(coord.y < 0) pc = pc.rgrg;\n"; - //for each sub-pixel j in center, the weight of sub-pixel k - yw = (i - nhpixel)*2; - for( j = 0; j < 3; j++) - { - ywn = yw + j -1; - weight[j] = ywn < -halfh || ywn > halfh? 0 : pf[ywn]; - } - //if(weight[1]!=0.0) out<<"FragColor += "<<weight[1]<<"*pc;\n"; - //out<<"FragColor += float4("<<weight[2]<<","<<weight[2]<<","<<weight[0]<<","<<weight[0]<<")*pc.barg;\n"; - if(weight[1] == 0.0) - { - out<<"result += float4("<<weight[2]<<","<<weight[2]<<","<<weight[0]<<","<<weight[0]<<")*pc.barg;\n"; - }else - { - out<<"result += float4("<<weight[1]<<","<<weight[1]<<","<<weight[0]<<","<<weight[0]<<")*pc.rgrg;\n"; - out<<"result += float4("<<weight[2]<<","<<weight[2]<<","<<weight[1]<<","<<weight[1]<<")*pc.baba;\n"; - } - } - out<< - " FragColor = result; }\n"<<'\0'; - return new ProgramCG( buffer); -} - - -void ShaderBagCG::LoadGenListShader(int ndoglev, int nlev) -{ - ProgramCG * program; - - s_genlist_init_tight = new ProgramCG( - "void main (\n" - "uniform samplerRECT tex, in float4 TexCoord0 : TEXCOORD0,\n" - "in float4 TexCoord1 : TEXCOORD1, in float4 TexCoord2 : TEXCOORD2, in float4 TexCoord3 : TEXCOORD3,\n" - "out float4 FragColor : COLOR0){\n" - "float4 helper = float4( texRECT(tex, TexCoord0.xy).r, texRECT(tex, TexCoord1.xy).r,\n" - "texRECT(tex, TexCoord2.xy).r, texRECT(tex, TexCoord3.xy).r);\n" - "FragColor = float4(helper>0.0);\n" - "}"); - - s_genlist_init_ex = program = new ProgramCG( - "void main (uniform float2 bbox, \n" - "uniform samplerRECT tex, \n" - "in float4 TexCoord0 : TEXCOORD0,\n" - "in float4 TexCoord1 : TEXCOORD1, \n" - "in float4 TexCoord2 : TEXCOORD2, \n" - "in float4 TexCoord3 : TEXCOORD3,\n" - "out float4 FragColor : COLOR0){\n" - "float4 helper = float4( \n" - "texRECT(tex, TexCoord0.xy).r, texRECT(tex, TexCoord1.xy).r,\n" - "texRECT(tex, TexCoord2.xy).r, texRECT(tex, TexCoord3.xy).r);\n" - "bool4 helper4 = bool4(TexCoord0.xy < bbox, TexCoord3.xy < bbox); \n" - "bool4 helper2 = helper4.xzxz && helper4.yyww; \n" - "FragColor = float4(helper2 && (helper>0.0 ));\n" - "}"); - _param_genlist_init_bbox = cgGetNamedParameter( *program, "bbox"); - - - //reduction ... - s_genlist_histo = new ProgramCG( - "void main (\n" - "uniform samplerRECT tex, in float2 TexCoord0 : TEXCOORD0,\n" - "in float2 TexCoord1 : TEXCOORD1, in float2 TexCoord2 : TEXCOORD2, in float2 TexCoord3 : TEXCOORD3,\n" - "out float4 FragColor : COLOR0){\n" - "float4 helper; float4 helper2; \n" - "helper = texRECT(tex, TexCoord0); helper2.xy = helper.xy + helper.zw; \n" - "helper = texRECT(tex, TexCoord1); helper2.zw = helper.xy + helper.zw; \n" - "FragColor.rg = helper2.xz + helper2.yw;\n" - "helper = texRECT(tex, TexCoord2); helper2.xy = helper.xy + helper.zw; \n" - "helper = texRECT(tex, TexCoord3); helper2.zw = helper.xy + helper.zw; \n" - "FragColor.ba= helper2.xz+helper2.yw;\n" - "}"); - - - //read of the first part, which generates tex coordinates - - s_genlist_start= program = LoadGenListStepShader(1, 1); - _param_ftex_width= cgGetNamedParameter(*program, "width"); - _param_genlist_start_tex0 = cgGetNamedParameter(*program, "tex0"); - //stepping - s_genlist_step = program = LoadGenListStepShader(0, 1); - _param_genlist_step_tex= cgGetNamedParameter(*program, "tex"); - _param_genlist_step_tex0= cgGetNamedParameter(*program, "tex0"); - - -} - -ProgramCG* ShaderBagCG::LoadGenListStepShader(int start, int step) -{ - int i; - char buffer[10240]; - //char chanels[5] = "rgba"; - ostrstream out(buffer, 10240); - out<<"void main(out float4 FragColor : COLOR0, \n"; - - for(i = 0; i < step; i++) out<<"uniform samplerRECT tex"<<i<<",\n"; - - if(start) - { - out<<"uniform float width, \nin float2 tpos : TEXCOORD0){\n"; - out<<"float index = floor(tpos.y) * width + floor(tpos.x) + 0.0001;\n"; - out<<"float2 pos = float2(0.5, 0.5);\n"; - }else - { - out<<"uniform samplerRECT tex, in float2 tpos: TEXCOORD0 ){\n"; - out<<"float4 tc = texRECT( tex, tpos);\n"; - out<<"float2 pos = tc.rg; float index = tc.b;\n"; - } - out<<"float2 sum; float4 cc;\n"; - - - - if(step>0) - { - out<<"float2 cpos = float2(-0.5, 0.5);\t float2 opos;\n"; - for(i = 0; i < step; i++) - { -//#define SETP_CODE_2 - -#ifndef SETP_CODE_2 -/* out<<"cc = texRECT(tex"<<i<<", pos);\n"; - out<<"float sum3[3] = {cc.r, cc.r + cc.g, cc.r + cc.g + cc.b};\n"; - out<<"float3 cmp = float3(index > float3(sum3[0], sum3[1], sum3[2]));\n"; - out<<"opos.y = -0.5 + cmp.y; opos.x = -0.5 + cmp.x + (cmp.z - cmp.y);\n"; - out<<"index -= dot(cmp, cc.rgb);\n"; - out<<"pos = (pos + pos + opos);\n";*/ - - out<<"cc = texRECT(tex"<<i<<", pos); sum.x = cc.r + cc.g;\n"; - out<<"if (index < sum.x){ if(index < cc.r) opos = cpos.xx; else {opos = cpos.yx; index -= cc.r;}}\n"; - out<<"else {index -= sum.x; if(index < cc.b) opos = cpos.xy; else{opos = cpos.yy; index -= cc.b;}}"; - out<<"pos = (pos + pos + opos);\n"; - -/* out<<"cc = texRECT(tex"<<i<<", pos);\n"; - out<<"if (index <cc.r){ opos = cpos.xx;}\n"; - out<<"else{sum.x = cc.r + cc.g;"; - out<<"if(index < sum.x ) {opos = cpos.yx; index -= cc.r;}\n"; - out<<"else{sum.y = sum.x + cc.b;"; - out<<"if(index < sum.y ) {opos = cpos.xy; index -= sum.x;}\n"; - out<<"else {opos = cpos.yy; index -= sum.y;}}}\n"; - out<<"pos = (pos + pos + opos);\n";*/ - -#else - out<<"cc = texRECT(tex"<<i<<", pos);\n"; - out<<"if (index < cc.r) opos = cpos.xx;\n"; - out<<"else if (index < cc.r + cc.g){opos = cpos.yx; index -= cc.r;}\n"; - out<<"else if (index < cc.r + cc.g + cc.b){opos = cpos.xy; index -= (cc.r + cc.g);}\n"; - out<<"else {opos = cpos.yy; index -= (cc.r + cc.g + cc.b);}\n"; - out<<"pos = (pos + pos + opos);\n"; -#endif - } - } - out<<"FragColor = float4(pos, index, 1);\n"; - out<<"}\n"<<'\0'; - return new ProgramCG(buffer); -} - -void ShaderBagCG::SetGenListInitParam(int w, int h) -{ - float bbox[2] = {w -1.0f, h - 1.0f}; - cgGLSetParameter2fv(_param_genlist_init_bbox, bbox); -} - -void ShaderBagCG::SetGenListStartParam(float width, int tex0) -{ - cgGLSetParameter1f(_param_ftex_width, width); - - if(_param_genlist_start_tex0) - { - cgGLSetTextureParameter(_param_genlist_start_tex0, tex0); - cgGLEnableTextureParameter(_param_genlist_start_tex0); - } -} - -void ShaderBagCG::LoadDescriptorShaderF2() -{ - //one shader outpout 128/8 = 16 , each fragout encodes 4 - //const double twopi = 2.0*3.14159265358979323846; - //const double rpi = 8.0/twopi; - char buffer[10240]; - ostrstream out(buffer, 10240); - - out<<setprecision(8); - - out<<"\n" - "#define M_PI 3.14159265358979323846\n" - "#define TWO_PI (2.0*M_PI)\n" - "#define RPI 1.2732395447351626861510701069801\n" - "#define WF size.z\n" - "void main(uniform samplerRECT tex, \n" - "uniform samplerRECT gradTex, \n" - "uniform float4 dsize, \n" - "uniform float3 size, \n" - "in float2 TexCoord0 : TEXCOORD0, \n" - "out float4 FragData0:COLOR0, \n" - "out float4 FragData1:COLOR1) \n" - "{\n" - " float2 dim = size.xy; //image size \n" - " float index = dsize.x * floor(TexCoord0.y * 0.5) + TexCoord0.x;\n" - " float idx = 8.0 * frac(index * 0.125) + 8.0 * floor(2.0 * frac(TexCoord0.y * 0.5)); \n" - " index = floor(index*0.125) + 0.49; \n" - " float2 coord = floor( float2( fmod(index, dsize.z), index*dsize.w)) + 0.5 ;\n" - " float2 pos = texRECT(tex, coord).xy; \n" - " if(any(pos.xy <= 1) || any(pos.xy >=dim-1)) " - " //discard; \n" - " { FragData0 = FragData1 = float4(0.0); return; }\n" - " float anglef = texRECT(tex, coord).z;\n" - " if(anglef > M_PI) anglef -= TWO_PI;\n" - " float sigma = texRECT(tex, coord).w; \n" - " float spt = abs(sigma * WF); //default to be 3*sigma \n"; - - //rotation - out<< - " float4 cscs, rots; \n" - " sincos(anglef, cscs.y, cscs.x); \n" - " cscs.zw = - cscs.xy; \n" - " rots = cscs /spt; \n" - " cscs *= spt; \n"; - - //here cscs is actually (cos, sin, -cos, -sin) * (factor: 3)*sigma - //and rots is (cos, sin, -cos, -sin ) /(factor*sigma) - //devide the 4x4 sift grid into 16 1x1 block, and each corresponds to a shader thread - //To use linear interoplation, 1x1 is increased to 2x2, by adding 0.5 to each side - out<< - " float4 temp; float2 pt, offsetpt; \n" - " /*the fraction part of idx is .5*/ \n" - " offsetpt.x = 4.0 * frac(idx*0.25) - 2.0; \n" - " offsetpt.y = floor(idx*0.25) - 1.5; \n" - " temp = cscs.xwyx*offsetpt.xyxy; \n" - " pt = pos + temp.xz + temp.yw; \n"; - - //get a horizontal bounding box of the rotated rectangle - out<< - " float2 bwin = abs(cscs.xy); \n" - " float bsz = bwin.x + bwin.y; \n" - " float4 sz; float2 spos; \n" - " sz.xy = max(pt - bsz, float2(1,1));\n" - " sz.zw = min(pt + bsz, dim - 2); \n" - " sz = floor(sz)+0.5;"; //move sample point to pixel center - - //get voting for two box - out<<"\n" - " float4 DA, DB; \n" - " DA = DB = float4(0, 0, 0, 0); \n" - " for(spos.y = sz.y; spos.y <= sz.w; spos.y+=1.0) \n" - " { \n" - " for(spos.x = sz.x; spos.x <= sz.z; spos.x+=1.0) \n" - " { \n" - " float2 diff = spos - pt; \n" - " temp = rots.xywx * diff.xyxy; \n" - " float2 nxy = (temp.xz + temp.yw); \n" - " float2 nxyn = abs(nxy); \n" - " if(all(nxyn < float2(1.0)))\n" - " {\n" - " float4 cc = texRECT(gradTex, spos); \n" - " float mod = cc.b; float angle = cc.a; \n" - " float theta0 = (anglef - angle)*RPI; \n" - " float theta = theta0 < 0? theta0 + 8.0 : theta0; // fmod(theta0 + 8.0, 8.0); \n" - " diff = nxy + offsetpt.xy; \n" - " float ww = exp(-0.125*dot(diff, diff));\n" - " float2 weights = 1 - nxyn;\n" - " float weight = weights.x * weights.y *mod*ww; \n" - " float theta1 = floor(theta); \n" - " float weight2 = (theta - theta1) * weight; \n" - " float weight1 = weight - weight2;\n" - " DA += float4(theta1 == float4(0, 1, 2, 3))*weight1; \n" - " DA += float4(theta1 == float4(7, 0, 1, 2))*weight2; \n" - " DB += float4(theta1 == float4(4, 5, 6, 7))*weight1; \n" - " DB += float4(theta1 == float4(3, 4, 5, 6))*weight2; \n" - " }\n" - " }\n" - " }\n"; - - out<< - " FragData0 = DA; FragData1 = DB;\n" - "}\n"<<'\0'; - - ProgramCG * program; - s_descriptor_fp = program = new ProgramCG(buffer); - _param_descriptor_gtex = cgGetNamedParameter(*program, "gradTex"); - _param_descriptor_size = cgGetNamedParameter(*program, "size"); - _param_descriptor_dsize = cgGetNamedParameter(*program, "dsize"); - - -} - -//the shader that computes the descriptors -void ShaderBagCG::LoadDescriptorShader() -{ - GlobalUtil::_DescriptorPPT = 16; - LoadDescriptorShaderF2(); -} - -void ShaderBagCG::LoadOrientationShader() -{ - - char buffer[10240]; - ostrstream out(buffer,10240); - - - out<<"\n" - "#define GAUSSIAN_WF "<<GlobalUtil::_OrientationGaussianFactor<<" \n" - "#define SAMPLE_WF ("<<GlobalUtil::_OrientationWindowFactor<< " )\n" - "#define ORIENTATION_THRESHOLD "<< GlobalUtil::_MulitiOrientationThreshold << "\n" - "void main(uniform samplerRECT tex, \n" - "uniform samplerRECT gradTex, \n" - " uniform float4 size, \n" - " in float2 TexCoord0 : TEXCOORD0, \n" - " out float4 FeatureData : COLOR0 "; - - //multi orientation output - //use one additional texture to store up to four orientations - //when we use one 32bit float to store two orientations, no extra texture is required - - if(GlobalUtil::_MaxOrientation >1 && GlobalUtil::_OrientationPack2 == 0) - out<<", out float4 OrientationData : COLOR1"; - - if(GlobalUtil::_SubpixelLocalization || GlobalUtil::_KeepExtremumSign) - { - //data for sub-pixel localization - out<<", uniform samplerRECT texS"; - } - - //use 9 float4 to store histogram of 36 directions - out<<") \n" - "{ \n" - " float4 bins[10]; \n" - " for (int i=0; i<9; i++) bins[i] = float4(0,0,0,0); \n" - " const float4 loc = texRECT(tex, TexCoord0); \n" - " const bool orientation_mode = (size.z != 0); \n" - " float2 pos = loc.xy; \n" - " float sigma = orientation_mode? abs(size.z) : loc.w; \n"; - if(GlobalUtil::_SubpixelLocalization || GlobalUtil::_KeepExtremumSign) - { - out<< - " if(orientation_mode) {\n" - " float4 keyx = texRECT(texS, pos);\n" - " sigma = sigma * pow(size.w, keyx.w); \n" - " pos.xy = pos.xy + keyx.yz; \n" - " #if " << GlobalUtil::_KeepExtremumSign << "\n" - " if(keyx.x<0.6) sigma = - sigma;\n" - " #endif\n" - " }\n"; - } - - out<< - " //bool fixed_orientation = (size.z < 0); \n" - " if(size.z < 0) {FeatureData = float4(pos, 0, sigma); return;}" - " const float gsigma = sigma * GAUSSIAN_WF; \n" - " const float2 win = abs(sigma.xx) * (SAMPLE_WF * GAUSSIAN_WF); \n" - " const float2 dim = size.xy; \n" - " const float dist_threshold = win.x*win.x+0.5; \n" - " const float factor = -0.5/(gsigma*gsigma); \n" - " float4 sz; float2 spos; \n" - " //if(any(pos.xy <= 1)) discard; \n" - " sz.xy = max( pos - win, float2(1,1)); \n" - " sz.zw = min( pos + win, dim-2); \n" - " sz = floor(sz)+0.5;"; - //loop to get the histogram - - out<<"\n" - " for(spos.y = sz.y; spos.y <= sz.w; spos.y+=1.0) \n" - " { \n" - " for(spos.x = sz.x; spos.x <= sz.z; spos.x+=1.0) \n" - " { \n" - " const float2 offset = spos - pos; \n" - " const float sq_dist = dot(offset,offset); \n" - " if( sq_dist < dist_threshold){ \n" - " const float4 cc = texRECT(gradTex, spos); \n" - " const float grad = cc.b; float theta = cc.a; \n" - " float idx = floor(degrees(theta)*0.1); \n" - " const float weight = grad*exp(sq_dist * factor); \n" - " if(idx < 0 ) idx += 36; \n" - " const float vidx = 4.0 * fract(idx * 0.25);//fmod(idx, 4); \n" - " const float4 inc = weight*float4(vidx == float4(0,1,2,3)); "; - - if(GlobalUtil::_UseDynamicIndexing && strcmp(cgGetProfileString(ProgramCG::_FProfile), "gp4fp")==0) -// if(ProgramCG::_FProfile == CG_PROFILE_GPU_FP) this enumerant is not defined in cg1.5 - { - //gp_fp supports dynamic indexing - out<<"\n" - " int iidx = int(floor(idx*0.25)); \n" - " bins[iidx]+=inc; \n" - " } \n" - " } \n" - " }"; - - }else - { - //nvfp40 still does not support dynamic array indexing - //unrolled binary search... - out<<"\n" - " if(idx < 16) \n" - " { \n" - " if(idx < 8) \n" - " { \n" - " if(idx < 4) { bins[0]+=inc;} \n" - " else { bins[1]+=inc;} \n" - " }else \n" - " { \n" - " if(idx < 12){ bins[2]+=inc;} \n" - " else { bins[3]+=inc;} \n" - " } \n" - " }else if(idx < 32) \n" - " { \n" - " if(idx < 24) \n" - " { \n" - " if(idx <20) { bins[4]+=inc;} \n" - " else { bins[5]+=inc;} \n" - " }else \n" - " { \n" - " if(idx < 28){ bins[6]+=inc;} \n" - " else { bins[7]+=inc;} \n" - " } \n" - " }else \n" - " { \n" - " bins[8]+=inc; \n" - " } \n" - " } \n" - " } \n" - " }"; - - } - - WriteOrientationCodeToStream(out); - - ProgramCG * program; - s_orientation = program = new ProgramCG(buffer); - _param_orientation_gtex = cgGetNamedParameter(*program, "gradTex"); - _param_orientation_size = cgGetNamedParameter(*program, "size"); - _param_orientation_stex = cgGetNamedParameter(*program, "texS"); -} - -void ShaderBagCG::WriteOrientationCodeToStream(std::ostream& out) -{ - //smooth histogram and find the largest -/* - smoothing kernel: (1 3 6 7 6 3 1 )/27 - the same as 3 pass of (1 1 1)/3 averaging - maybe better to use 4 pass on the vectors... -*/ - - - //the inner loop on different array numbers is always unrolled in fp40 - - //bug fixed here:) - out<<"\n" - " float3x3 mat1 = float3x3(1, 0, 0, 3, 1, 0, 6, 3, 1)/27.0;; //bug fix.. \n" - " float4x4 mat2 = float4x4( 7, 6, 3, 1, 6, 7, 6, 3, 3, 6, 7, 6, 1, 3, 6, 7)/27.0;;\n" - " for (int j=0; j<2; j++) \n" - " { \n" - " float4 prev = bins[8]; \n" - " bins[9] = bins[0]; \n" - " for (int i=0; i<9; i++) \n" - " { \n" - " float4 newb = mul ( bins[i], mat2); \n" - " newb.xyz += mul ( prev.yzw, mat1); \n" - " prev = bins[i]; \n" - " newb.wzy += mul ( bins[i+1].zyx, mat1); \n" - " bins[i] = newb; \n" - " } \n" - " }"; - - - //find the maximum voting - out<<"\n" - " float4 maxh; float2 maxh2; float4 maxh4 = bins[0]; \n" - " for (int i=1; i<9; i++) maxh4 = max(maxh4, bins[i]); \n" - " maxh2 = max(maxh4.xy, maxh4.zw); maxh = float4(max(maxh2.x, maxh2.y));"; - - char *testpeak_code; - char *savepeak_code; - - - - //save two/three/four orientations with the largest votings? - - // - if(GlobalUtil::_MaxOrientation>1) - { - out<<"\n" - " float4 Orientations = float4(0, 0, 0, 0); \n" - " float4 weights = float4(0,0,0,0); "; - - testpeak_code = "\n" - " {test = bins[i]>hh;"; - - //save the orientations in weight-decreasing order - if(GlobalUtil::_MaxOrientation ==2) - { - savepeak_code = "\n" - " if(weight <=weights.g){}\n" - " else if(weight >weights.r)\n" - " {weights.rg = float2(weight, weights.r); Orientations.rg = float2(th, Orientations.r);}\n" - " else {weights.g = weight; Orientations.g = th;}"; - - }else if(GlobalUtil::_MaxOrientation ==3) - { - savepeak_code = "\n" - " if(weight <=weights.b){}\n" - " else if(weight >weights.r)\n" - " {weights.rgb = float3(weight, weights.rg); Orientations.rgb = float3(th, Orientations.rg);}\n" - " else if(weight >weights.g)\n" - " {weights.gb = float2(weight, weights.g); Orientations.gb = float2(th, Orientations.g);}\n" - " else {weights.b = weight; Orientations.b = th;}"; - }else - { - savepeak_code = "\n" - " if(weight <=weights.a){}\n" - " else if(weight >weights.r)\n" - " {weights = float4(weight, weights.rgb); Orientations = float4(th, Orientations.rgb);}\n" - " else if(weight >weights.g)\n" - " {weights.gba = float3(weight, weights.gb); Orientations.gba = float3(th, Orientations.gb);}\n" - " else if(weight >weights.b)\n" - " {weights.ba = float2(weight, weights.b); Orientations.ba = float2(th, Orientations.b);}\n" - " else {weights.a = weight; Orientations.a = th;}"; - } - - }else - { - out<<"\n" - " float Orientations = 0; "; - testpeak_code ="\n" - " if(npeaks==0){ \n" - " test = (bins[i] >= maxh) ;"; - savepeak_code="\n" - " npeaks++; \n" - " Orientations = th.x;"; - - } - - //find the peaks - //the following loop will be unrolled - - out<<"\n" - " const float4 hh = maxh * ORIENTATION_THRESHOLD; bool4 test; \n" - " bins[9] = bins[0]; \n" - " float npeaks = 0, k = 0; \n" - " float prevb = bins[8].w; \n" - " for (int i = 0; i <9 ; i++) \n" - " {" - <<testpeak_code<<" \n" - " if( any ( test.xy || test.zw) ) \n" - " { \n" - " if(test.r && bins[i].x > prevb && bins[i].x > bins[i].y ) \n" - " { \n" - " float di = 0.5 * (bins[i].y-prevb) / (bins[i].x *2.0 -bins[i].y -prevb) ; \n" - " float th = (k+di+0.5); float weight = bins[i].x;" - <<savepeak_code<<"\n" - " }\n" - " else if(test.g && all( bins[i].yy > bins[i].xz) ) \n" - " { \n" - " float di = 0.5 * (bins[i].z-bins[i].x) / (bins[i].y * 2.0 - bins[i].z - bins[i].x) ; \n" - " float th = (k+di+1.5); float weight = bins[i].y; " - <<savepeak_code<<" \n" - " }" - <<"\n" - " if(test.b && all( bins[i].zz > bins[i].yw) ) \n" - " { \n" - " float di = 0.5 * (bins[i].w-bins[i].y) / (bins[i].z * 2.0-bins[i].w-bins[i].y) ; \n" - " float th = (k+di+2.5); float weight = bins[i].z; " - <<savepeak_code<<" \n" - " }\n" - " else if(test.a && bins[i].w > bins[i].z && bins[i].w > bins[i+1].x ) \n" - " { \n" - " float di = 0.5 * (bins[i+1].x-bins[i].z) / (bins[i].w * 2.0- bins[i+1].x-bins[i].z) ; \n" - " float th = (k+di+3.5); float weight = bins[i].w; " - <<savepeak_code<<" \n" - " }\n" - " }}\n" - " k = k + 4.0; \n" - " prevb = bins[i].w;\n" - " }"; - //WRITE output - if(GlobalUtil::_OrientationPack2) - { - //pack two orientations in one float - out<<"\n" - " if(orientation_mode){\n" - " Orientations.xy = frac(Orientations.xy / 36.0 + 1.0);\n" - " if(weights.x <= 0) Orientations.x = 1.0;\n" - " if(weights.y <= 0) Orientations.y = 1.0;\n" - " float packed_orientation = pack_2ushort(Orientations.xy); \n" - " FeatureData = float4(pos, packed_orientation, sigma);\n" - " }else{\n" - " FeatureData = float4(pos, radians((Orientations.x)*10.0), sigma);\n" - " }\n"; - }else if(GlobalUtil::_MaxOrientation>1) - { - out<<"\n" - " if(orientation_mode){\n" - " npeaks = dot(float4(1,1," - <<(GlobalUtil::_MaxOrientation>2 ? 1 : 0)<<"," - <<(GlobalUtil::_MaxOrientation >3? 1 : 0)<<"), float4(weights>hh));\n" - " OrientationData = radians((Orientations )*10.0);\n" - " FeatureData = float4(pos, npeaks, sigma);\n" - " }else{\n" - " FeatureData = float4(pos, radians((Orientations.x)*10.0), sigma);\n" - " }\n"; - }else - { - out<<"\n" - " FeatureData = float4(pos, radians((Orientations.x)*10.0), sigma);"; - } - //end - out<<"\n" - "}\n"<<'\0'; - - -} - -void ShaderBagCG::SetSimpleOrientationInput(int oTex, float sigma, float sigma_step) -{ - cgGLSetTextureParameter(_param_orientation_gtex, oTex); - cgGLEnableTextureParameter(_param_orientation_gtex); - cgGLSetParameter1f(_param_orientation_size, sigma); -} - -void ShaderBagCG::SetFeatureOrientationParam(int gtex, int width, int height, float sigma, int stex, float step) -{ - /// - cgGLSetTextureParameter(_param_orientation_gtex, gtex); - cgGLEnableTextureParameter(_param_orientation_gtex); - - if((GlobalUtil::_SubpixelLocalization || GlobalUtil::_KeepExtremumSign)&& stex) - { - //specify texutre for subpixel subscale localization - cgGLSetTextureParameter(_param_orientation_stex, stex); - cgGLEnableTextureParameter(_param_orientation_stex); - } - - float size[4]; - size[0] = (float)width; - size[1] = (float)height; - size[2] = sigma; - size[3] = step; - cgGLSetParameter4fv(_param_orientation_size, size); - -} - -void ShaderBagCG::SetFeatureDescirptorParam(int gtex, int otex, float dwidth, float fwidth, float width, float height, float sigma) -{ - /// - cgGLSetTextureParameter(_param_descriptor_gtex, gtex); - cgGLEnableTextureParameter(_param_descriptor_gtex); - - float dsize[4] ={dwidth, 1.0f/dwidth, fwidth, 1.0f/fwidth}; - cgGLSetParameter4fv(_param_descriptor_dsize, dsize); - float size[3]; - size[0] = width; - size[1] = height; - size[2] = GlobalUtil::_DescriptorWindowFactor; - cgGLSetParameter3fv(_param_descriptor_size, size); -} - - -/////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////PACKED VERSION?/////////////////////////////////// - -ShaderBagPKCG::ShaderBagPKCG() -{ - ProgramCG::InitContext(); -} - -void ShaderBagPKCG::UnloadProgram() -{ - - cgGLUnbindProgram(ProgramCG::_FProfile); - cgGLDisableProfile(ProgramCG::_FProfile); -} - -void ShaderBagPKCG::LoadFixedShaders() -{ - ProgramCG * program; - - /* - char *rgb2gray_packing_code = - "void main(uniform samplerRECT rgbTex, in float4 TexCoord0 : TEXCOORD0, \n" - " in float4 TexCoord1 : TEXCOORD1, in float4 TexCoord2 : TEXCOORD2, \n" - " in float4 TexCoord3 : TEXCOORD3, out float4 FragData : COLOR0){\n" - " const float3 weight = vec3(0.299, 0.587, 0.114);\n" - " FragData.r = dot(weight, texRECT(rgbTex,TexCoord0.st ).rgb);\n" - " FragData.g = dot(weight, texRECT(rgbTex,TexCoord1.st ).rgb);\n" - " FragData.b = dot(weight, texRECT(rgbTex,TexCoord2.st ).rgb);\n" - " FragData.a = dot(weight, texRECT(rgbTex,TexCoord3.st ).rgb);}";// - s_gray = new ProgramCG( rgb2gray_packing_code); - */ - - s_gray = new ProgramCG( - "void main(float4 TexCoord0 : TEXCOORD0, out float4 FragColor : COLOR0, uniform samplerRECT tex){\n" - "float intensity = dot(float3(0.299, 0.587, 0.114), texRECT(tex,TexCoord0.xy ).rgb);\n" - "FragColor= float4(intensity, intensity, intensity, 1.0);}" ); - - - s_sampling = new ProgramCG( - "void main(uniform samplerRECT tex, in float4 TexCoord0 : TEXCOORD0, \n" - " in float4 TexCoord1 : TEXCOORD1, in float4 TexCoord2 : TEXCOORD2, \n" - " in float4 TexCoord3 : TEXCOORD3, out float4 FragData : COLOR0 ){\n" - " FragData= float4( texRECT(tex,TexCoord0.st ).r,texRECT(tex,TexCoord1.st ).r,\n" - " texRECT(tex,TexCoord2.st ).r,texRECT(tex,TexCoord3.st ).r);}" ); - - - s_margin_copy = program = new ProgramCG( - "void main(in float4 texCoord0: TEXCOORD0, out float4 FragColor: COLOR0, \n" - "uniform samplerRECT tex, uniform float4 truncate){\n" - "float4 cc = texRECT(tex, min(texCoord0.xy, truncate.xy)); \n" - "bool2 ob = texCoord0.xy < truncate.xy;\n" - "if(ob.y) { FragColor = (truncate.z ==0 ? cc.rrbb : cc.ggaa); } \n" - "else if(ob.x) {FragColor = (truncate.w <1.5 ? cc.rgrg : cc.baba);} \n" - "else { float4 weights = float4(float4(0, 1, 2, 3) == truncate.w);\n" - "float v = dot(weights, cc); FragColor = v.xxxx;}}"); - - _param_margin_copy_truncate = cgGetNamedParameter(*program, "truncate"); - - - s_zero_pass = new ProgramCG("void main(out float4 FragColor : COLOR0){FragColor = 0;}"); - - s_grad_pass = program = new ProgramCG( - "void main (\n" - "float4 TexCC : TEXCOORD0, float4 TexLC : TEXCOORD1,\n" - "float4 TexRC : TEXCOORD2, float4 TexCD : TEXCOORD3, float4 TexCU : TEXCOORD4,\n" - "out float4 FragData0 : COLOR0, out float4 FragData1 : COLOR1, \n" - "out float4 FragData2 : COLOR2, uniform samplerRECT tex, uniform samplerRECT texp)\n" - "{\n" - " float4 v1, v2, gg;\n" - " float4 cc = texRECT(tex, TexCC.xy);\n" - " float4 cp = texRECT(texp, TexCC.xy);\n" - " FragData0 = cc - cp; \n" - " float4 cl = texRECT(tex, TexLC.xy); float4 cr = texRECT(tex, TexRC.xy);\n" - " float4 cd = texRECT(tex, TexCD.xy); float4 cu = texRECT(tex, TexCU.xy);\n" - " float4 dx = (float4(cr.rb, cc.ga) - float4(cc.rb, cl.ga)).zxwy;\n" - " float4 dy = (float4(cu.rg, cc.ba) - float4(cc.rg, cd.ba)).zwxy;\n" - " FragData1 = 0.5 * sqrt(dx*dx + dy * dy);\n" - " FragData2 = FragData1 > 0? atan2(dy, dx) : float4(0);\n" - "}\n\0"); - - _param_grad_pass_texp = cgGetNamedParameter(*program, "texp"); - - - s_dog_pass = program = new ProgramCG( - "void main (float4 TexCC : TEXCOORD0, out float4 FragData0 : COLOR0, \n" - " uniform samplerRECT tex, uniform samplerRECT texp)\n" - "{\n" - " float4 cc = texRECT(tex, TexCC.xy);\n" - " float4 cp = texRECT(texp, TexCC.xy);\n" - " FragData0 = cc - cp; \n" - "}\n\0"); - - //// - if(GlobalUtil::_SupportFP40) - { - LoadOrientationShader(); - if(GlobalUtil::_DescriptorPPT) LoadDescriptorShader(); - }else - { - s_orientation = program = new ProgramCG( - "void main(out float4 FragColor : COLOR0, \n" - " uniform samplerRECT fTex, uniform samplerRECT oTex, \n" - " uniform float2 size, \n" - " in float2 tpos : TEXCOORD0){\n" - " float4 cc = texRECT(fTex, tpos);\n" - " float2 co = cc.xy * 0.5; \n" - " float4 oo = texRECT(oTex, co);\n" - " bool2 bo = frac(co) < 0.5; \n" - " float o = bo.y? (bo.x? oo.r : oo.g) : (bo.x? oo.b : oo.a); \n" - " FragColor = float4(cc.rg, o, size.x * pow(size.y, cc.a));}"); - _param_orientation_gtex= cgGetNamedParameter(*program, "oTex"); - _param_orientation_size= cgGetNamedParameter(*program, "size"); - - GlobalUtil::_FullSupported = 0; - GlobalUtil::_MaxOrientation = 0; - GlobalUtil::_DescriptorPPT = 0; - std::cerr<<"Orientation simplified on this hardware"<<endl; - std::cerr<<"Descriptor ignored on this hardware"<<endl; - } -} - -void ShaderBagPKCG::LoadDisplayShaders() -{ - ProgramCG * program; - - s_copy_key = new ProgramCG( - "void main(in float4 TexCoord0 : TEXCOORD0, out float4 FragColor : COLOR0, uniform samplerRECT tex){\n" - "FragColor.rg= texRECT(tex, TexCoord0.xy).rg; FragColor.ba = float2(0,1); }"); - - //shader used to write a vertex buffer object - //which is used to draw the quads of each feature - s_vertex_list = program = new ProgramCG( - "void main(in float4 TexCoord0: TEXCOORD0,\n" - "uniform float4 sizes, \n" - "uniform samplerRECT tex, \n" - "out float4 FragColor: COLOR0){\n" - "float fwidth = sizes.y; \n" - "float twidth = sizes.z; \n" - "float rwidth = sizes.w; \n" - "float index = 0.1*(fwidth*floor(TexCoord0.y) + TexCoord0.x);\n" - "float px = fmod(index, twidth);\n" - "float2 tpos= floor(float2(px, index*rwidth))+0.5;\n" - "float4 cc = texRECT(tex, tpos );\n" - "float size = 3.0f * cc.a;// sizes.x;// \n" - "FragColor.zw = float2(0.0, 1.0);\n" - "if(any(cc.xy <=0)) {FragColor.xy = cc.xy;}else \n" - "{\n" - " float type = frac(px);\n" - " float2 dxy; float s, c;\n" - " dxy.x = type < 0.1 ? 0 : ((type <0.5 || type > 0.9)? size : -size);\n" - " dxy.y = type < 0.2 ? 0 : ((type < 0.3 || type > 0.7 )? -size :size); \n" - " sincos(cc.b, s, c);\n" - " FragColor.x = cc.x + c*dxy.x-s*dxy.y;\n" - " FragColor.y = cc.y + c*dxy.y+s*dxy.x;}\n" - "}\n\0"); - /*FragColor = float4(tpos, 0.0, 1.0);}\n\0");*/ - - _param_genvbo_size = cgGetNamedParameter(*program, "sizes"); - - s_display_gaussian = new ProgramCG( - "void main(uniform samplerRECT tex, in float4 TexCoord0:TEXCOORD0, out float4 FragData: COLOR0 ){\n" - "float4 pc = texRECT(tex, TexCoord0.xy); bool2 ff = (frac(TexCoord0.xy) < 0.5);\n" - "float v = ff.y?(ff.x? pc.r : pc.g):(ff.x?pc.b:pc.a); FragData = float4(v.xxx, 1.0);}"); - - s_display_dog = new ProgramCG( - "void main(in float4 TexCoord0 : TEXCOORD0, out float4 FragColor : COLOR0, uniform samplerRECT tex){\n" - "float4 pc = texRECT(tex, TexCoord0.xy); bool2 ff = (frac(TexCoord0.xy) < 0.5);\n" - "float v = ff.y ?(ff.x ? pc.r : pc.g):(ff.x ? pc.b : pc.a);float g = (0.5+20.0*v);\n" - "FragColor = float4(g, g, g, 1.0);}" ); - - - s_display_grad = new ProgramCG( - "void main(in float4 TexCoord0 : TEXCOORD0, out float4 FragColor : COLOR0, uniform samplerRECT tex){\n" - "float4 pc = texRECT(tex, TexCoord0.xy); bool2 ff = (frac(TexCoord0.xy) < 0.5);\n" - "float v = ff.y ?(ff.x ? pc.r : pc.g):(ff.x ? pc.b : pc.a); FragColor = float4(5.0 *v.xxx, 1.0); }"); - - s_display_keys= new ProgramCG( - "void main(in float4 TexCoord0 : TEXCOORD0, out float4 FragColor : COLOR0, uniform samplerRECT tex){\n" - "float4 oc = texRECT(tex, TexCoord0.xy); \n" - "float4 cc = float4(abs(oc.r) == float4(1.0, 2.0, 3.0, 4.0));\n" - "bool2 ff = (frac(TexCoord0.xy) < 0.5);\n" - "float v = ff.y ?(ff.x ? cc.r : cc.g):(ff.x ? cc.b : cc.a);\n" - "if(oc.r == 0) discard;\n" - "else if(oc.r > 0) FragColor = float4(1.0, 0, 0,1.0); \n" - "else FragColor = float4(0.0,1.0,0.0,1.0); }" ); -} - -void ShaderBagPKCG::LoadGenListShader(int ndoglev, int nlev) -{ - - //the V2 algorithms are only slightly faster, but way more complicated - //LoadGenListShaderV2(ndoglev, nlev); return; - ProgramCG * program; - - s_genlist_init_tight = new ProgramCG( - "void main (uniform samplerRECT tex, in float4 TexCoord0 : TEXCOORD0,\n" - "in float4 TexCoord1 : TEXCOORD1, in float4 TexCoord2 : TEXCOORD2, \n" - "in float4 TexCoord3 : TEXCOORD3, out float4 FragColor : COLOR0)\n" - "{\n" - " float4 data = float4( texRECT(tex, TexCoord0.xy).r,\n" - " texRECT(tex, TexCoord1.xy).r,\n" - " texRECT(tex, TexCoord2.xy).r,\n" - " texRECT(tex, TexCoord3.xy).r);\n" - " FragColor = float4(data != 0);\n" - "}"); - - s_genlist_init_ex = program = new ProgramCG( - "void main (uniform float4 bbox, uniform samplerRECT tex, \n" - "in float4 TexCoord0 : TEXCOORD0, in float4 TexCoord1 : TEXCOORD1, \n" - "in float4 TexCoord2 : TEXCOORD2, in float4 TexCoord3 : TEXCOORD3,\n" - "out float4 FragColor : COLOR0)\n" - "{\n" - " bool4 helper1 = abs(texRECT(tex, TexCoord0.xy).r)== float4(1.0, 2.0, 3.0, 4.0); \n" - " bool4 helper2 = abs(texRECT(tex, TexCoord1.xy).r)== float4(1.0, 2.0, 3.0, 4.0);\n" - " bool4 helper3 = abs(texRECT(tex, TexCoord2.xy).r)== float4(1.0, 2.0, 3.0, 4.0);\n" - " bool4 helper4 = abs(texRECT(tex, TexCoord3.xy).r)== float4(1.0, 2.0, 3.0, 4.0);\n" - " bool4 bx1 = TexCoord0.xxyy < bbox; \n" - " bool4 bx4 = TexCoord3.xxyy < bbox; \n" - " bool4 bx2 = bool4(bx4.xy, bx1.zw); \n" - " bool4 bx3 = bool4(bx1.xy, bx4.zw);\n" - " helper1 = (bx1.xyxy && bx1.zzww && helper1);\n" - " helper2 = (bx2.xyxy && bx2.zzww && helper2);\n" - " helper3 = (bx3.xyxy && bx3.zzww && helper3);\n" - " helper4 = (bx4.xyxy && bx4.zzww && helper4);\n" - " FragColor.r = any(helper1.xy || helper1.zw); \n" - " FragColor.g = any(helper2.xy || helper2.zw); \n" - " FragColor.b = any(helper3.xy || helper3.zw); \n" - " FragColor.a = any(helper4.xy || helper4.zw); \n" - "}"); - _param_genlist_init_bbox = cgGetNamedParameter( *program, "bbox"); - - s_genlist_end = program = new ProgramCG( - GlobalUtil::_KeepExtremumSign == 0 ? - - "void main( uniform samplerRECT tex, uniform samplerRECT ktex,\n" - " in float4 tpos : TEXCOORD0, out float4 FragColor : COLOR0)\n" - "{\n" - " float4 tc = texRECT( tex, tpos.xy);\n" - " float2 pos = tc.rg; float index = tc.b;\n" - " float4 tk = texRECT( ktex, pos); \n" - " float4 keys = float4(abs(tk.x) == float4(1.0, 2.0, 3.0, 4.0)); \n" - " float2 opos; \n" - " opos.x = dot(keys, float4(-0.5, 0.5, -0.5, 0.5));\n" - " opos.y = dot(keys, float4(-0.5, -0.5, 0.5, 0.5));\n" - " FragColor = float4(opos + pos + pos + tk.yz, 1.0, tk.w);\n" - "}" : - - "void main( uniform samplerRECT tex, uniform samplerRECT ktex,\n" - " in float4 tpos : TEXCOORD0, out float4 FragColor : COLOR0)\n" - "{\n" - " float4 tc = texRECT( tex, tpos.xy);\n" - " float2 pos = tc.rg; float index = tc.b;\n" - " float4 tk = texRECT( ktex, pos); \n" - " float4 keys = float4(abs(tk.x) == float4(1.0, 2.0, 3.0, 4.0)); \n" - " float2 opos; \n" - " opos.x = dot(keys, float4(-0.5, 0.5, -0.5, 0.5));\n" - " opos.y = dot(keys, float4(-0.5, -0.5, 0.5, 0.5));\n" - " FragColor = float4(opos + pos + pos + tk.yz, sign(tk.x), tk.w);\n" - "}" - ); - _param_genlist_end_ktex = cgGetNamedParameter(*program, "ktex"); - - //reduction ... - s_genlist_histo = new ProgramCG( - "void main (uniform samplerRECT tex, in float2 TexCoord0 : TEXCOORD0,\n" - "in float2 TexCoord1 : TEXCOORD1, in float2 TexCoord2 : TEXCOORD2, \n" - "in float2 TexCoord3 : TEXCOORD3, out float4 FragColor : COLOR0)\n" - "{\n" - " float4 helper; float4 helper2; \n" - " helper = texRECT(tex, TexCoord0); helper2.xy = helper.xy + helper.zw; \n" - " helper = texRECT(tex, TexCoord1); helper2.zw = helper.xy + helper.zw; \n" - " FragColor.rg = helper2.xz + helper2.yw;\n" - " helper = texRECT(tex, TexCoord2); helper2.xy = helper.xy + helper.zw; \n" - " helper = texRECT(tex, TexCoord3); helper2.zw = helper.xy + helper.zw; \n" - " FragColor.ba= helper2.xz+helper2.yw;\n" - "}"); - - - //read of the first part, which generates tex coordinates - - s_genlist_start= program = ShaderBagCG::LoadGenListStepShader(1, 1); - _param_ftex_width= cgGetNamedParameter(*program, "width"); - _param_genlist_start_tex0 = cgGetNamedParameter(*program, "tex0"); - //stepping - s_genlist_step = program = ShaderBagCG::LoadGenListStepShader(0, 1); - _param_genlist_step_tex= cgGetNamedParameter(*program, "tex"); - _param_genlist_step_tex0= cgGetNamedParameter(*program, "tex0"); - - -} - - - -void ShaderBagPKCG::LoadGenListShaderV2(int ndoglev, int nlev) -{ - ProgramCG * program; - - s_genlist_init_tight = new ProgramCG( - "void main (uniform samplerRECT tex, in float4 TexCoord0 : TEXCOORD0,\n" - "in float4 TexCoord1 : TEXCOORD1, in float4 TexCoord2 : TEXCOORD2, \n" - "in float4 TexCoord3 : TEXCOORD3, out float4 FragColor : COLOR0)\n" - "{\n" - " float4 data1 = texRECT(tex, TexCoord0.xy);\n" - " float4 data2 = texRECT(tex, TexCoord1.xy);\n" - " float4 data3 = texRECT(tex, TexCoord2.xy);\n" - " float4 data4 = texRECT(tex, TexCoord3.xy);\n" - " bool4 helper1 = (abs(data1.r), float4(1.0, 2.0, 3.0, 4.0)); \n" - " bool4 helper2 = (abs(data2.r), float4(1.0, 2.0, 3.0, 4.0));\n" - " bool4 helper3 = (abs(data3.r), float4(1.0, 2.0, 3.0, 4.0));\n" - " bool4 helper4 = (abs(data4.r), float4(1.0, 2.0, 3.0, 4.0));\n" - " FragColor.r = any(helper1.xy || helper1.zw); \n" - " FragColor.g = any(helper2.xy || helper2.zw); \n" - " FragColor.b = any(helper3.xy || helper3.zw); \n" - " FragColor.a = any(helper4.xy || helper4.zw); \n" - " if(dot(FragColor, float4(1,1,1,1)) == 1) \n" - " {\n" - " //use a special method if there is only one in the 16, \n" - " float4 data, helper; float2 pos, opos; \n" - " if(FragColor.r){ \n" - " data = data1; helper = helper1; pos = TexCoord0.xy;\n" - " }else if(FragColor.g){\n" - " data = data2; helper = helper2; pos = TexCoord1.xy;\n" - " }else if(FragColor.b){\n" - " data = data3; helper = helper3; pos = TexCoord2.xy;\n" - " }else{\n" - " data = data4; helper = helper4; pos = TexCoord3.xy;\n" - " }\n" - " opos.x = dot(helper, float4(-0.5, 0.5, -0.5, 0.5));\n" - " opos.y = dot(helper, float4(-0.5, -0.5, 0.5, 0.5));\n" - " FragColor = float4( pos + pos + opos + data.yz, -1, data.w); \n" - " }\n" - "}"); - - s_genlist_init_ex = program = new ProgramCG( - "void main (uniform float4 bbox, uniform samplerRECT tex, \n" - "in float4 TexCoord0 : TEXCOORD0, in float4 TexCoord1 : TEXCOORD1, \n" - "in float4 TexCoord2 : TEXCOORD2, in float4 TexCoord3 : TEXCOORD3,\n" - "out float4 FragColor : COLOR0)\n" - "{\n" - " float4 data1 = texRECT(tex, TexCoord0.xy);\n" - " float4 data2 = texRECT(tex, TexCoord1.xy);\n" - " float4 data3 = texRECT(tex, TexCoord2.xy);\n" - " float4 data4 = texRECT(tex, TexCoord3.xy);\n" - " bool4 helper1 = (abs(data1.r), float4(1.0, 2.0, 3.0, 4.0)); \n" - " bool4 helper2 = (abs(data2.r), float4(1.0, 2.0, 3.0, 4.0));\n" - " bool4 helper3 = (abs(data3.r), float4(1.0, 2.0, 3.0, 4.0));\n" - " bool4 helper4 = (abs(data4.r), float4(1.0, 2.0, 3.0, 4.0));\n" - " bool4 bx1 = TexCoord0.xxyy < bbox; \n" - " bool4 bx4 = TexCoord3.xxyy < bbox; \n" - " bool4 bx2 = bool4(bx4.xy, bx1.zw); \n" - " bool4 bx3 = bool4(bx1.xy, bx4.zw);\n" - " helper1 = bx1.xyxy && bx1.zzww && helper1; \n" - " helper2 = bx2.xyxy && bx2.zzww && helper2; \n" - " helper3 = bx3.xyxy && bx3.zzww && helper3; \n" - " helper4 = bx4.xyxy && bx4.zzww && helper4; \n" - " FragColor.r = any(helper1.xy || helper1.zw); \n" - " FragColor.g = any(helper2.xy || helper2.zw); \n" - " FragColor.b = any(helper3.xy || helper3.zw); \n" - " FragColor.a = any(helper4.xy || helper4.zw); \n" - " if(dot(FragColor, float4(1,1,1,1)) == 1) \n" - " {\n" - " //use a special method if there is only one in the 16, \n" - " float4 data, helper; bool4 bhelper; float2 pos, opos; \n" - " if(FragColor.r){ \n" - " data = data1; bhelper = helper1; pos = TexCoord0.xy;\n" - " }else if(FragColor.g){\n" - " data = data2; bhelper = helper2; pos = TexCoord1.xy;\n" - " }else if(FragColor.b){\n" - " data = data3; bhelper = helper3; pos = TexCoord2.xy;\n" - " }else{\n" - " data = data4; bhelper = helper4; pos = TexCoord3.xy;\n" - " }\n" - " helper = float4(bhelper); \n" - " opos.x = dot(helper, float4(-0.5, 0.5, -0.5, 0.5));\n" - " opos.y = dot(helper, float4(-0.5, -0.5, 0.5, 0.5));\n" - " FragColor = float4(pos + pos + opos + data.yz, -1, data.w); \n" - " }\n" - "}"); - _param_genlist_init_bbox = cgGetNamedParameter( *program, "bbox"); - - s_genlist_end = program = new ProgramCG( - - "void main( uniform samplerRECT tex, uniform samplerRECT ktex,\n" - " in float4 tpos : TEXCOORD0, out float4 FragColor : COLOR0)\n" - "{\n" - " float4 tc = texRECT( tex, tpos.xy);\n" - " float2 pos = tc.rg; float index = tc.b;\n" - " if(index == -1)\n" - " {\n" - " FragColor = float4(tc.xy, 0, tc.w);\n" - " }else\n" - " {\n" - " float4 tk = texRECT( ktex, pos); \n" - " float4 keys = float4(abs(tk.r) == float4(1.0, 2.0, 3.0, 4.0)); \n" - " float2 opos; \n" - " opos.x = dot(keys, float4(-0.5, 0.5, -0.5, 0.5));\n" - " opos.y = dot(keys, float4(-0.5, -0.5, 0.5, 0.5));\n" - " FragColor = float4(opos + pos + pos + tk.yz, 0, tk.w);\n" - " }\n" - "}"); - _param_genlist_end_ktex = cgGetNamedParameter(*program, "ktex"); - - //reduction ... - s_genlist_histo = new ProgramCG( - "void main (uniform samplerRECT tex, in float2 TexCoord0 : TEXCOORD0,\n" - "in float2 TexCoord1 : TEXCOORD1, in float2 TexCoord2 : TEXCOORD2, \n" - "in float2 TexCoord3 : TEXCOORD3, out float4 FragColor : COLOR0)\n" - "{\n" - " float4 helper[4]; float4 helper2; \n" - " helper[0] = texRECT(tex, TexCoord0); helper2.xy = helper[0].xy + helper[0].zw; \n" - " helper[1] = texRECT(tex, TexCoord1); helper2.zw = helper[1].xy + helper[1].zw; \n" - " FragColor.rg = helper2.xz + helper2.yw;\n" - " helper[2] = texRECT(tex, TexCoord2); helper2.xy = helper[2].xy + helper[2].zw; \n" - " helper[3] = texRECT(tex, TexCoord3); helper2.zw = helper[3].xy + helper[3].zw; \n" - " FragColor.ba= helper2.xz+helper2.yw;\n" - " bool4 keyt = float4(helper[0].z, helper[1].z, helper[2].z, helper[3].z) == -1.0; \n" - " float keyc = dot(float4(keyt), float4(1,1,1,1)); \n" - " if(keyc == 1.0 && dot(FragColor, float4(1,1,1,1)) == -1.0) \n" - " {\n" - " if(keyt.x) FragColor = helper[0];\n" - " else if(keyt.y) FragColor = helper[1]; \n" - " else if(keyt.z) FragColor = helper[2]; \n" - " else FragColor = helper[3]; \n" - " }else\n" - " {\n" - " FragColor = keyt? float4(1,1,1,1) : FragColor;\n" - " }\n" - "}"); - - //read of the first part, which generates tex coordinates - - s_genlist_start= program = ShaderBagCG::LoadGenListStepShaderV2(1, 1); - _param_ftex_width= cgGetNamedParameter(*program, "width"); - _param_genlist_start_tex0 = cgGetNamedParameter(*program, "tex0"); - //stepping - s_genlist_step = program = ShaderBagCG::LoadGenListStepShaderV2(0, 1); - _param_genlist_step_tex= cgGetNamedParameter(*program, "tex"); - _param_genlist_step_tex0= cgGetNamedParameter(*program, "tex0"); - - -} - - - -ProgramCG* ShaderBagCG::LoadGenListStepShaderV2(int start, int step) -{ - int i; - char buffer[10240]; - //char chanels[5] = "rgba"; - ostrstream out(buffer, 10240); - out<<"void main(out float4 FragColor : COLOR0, \n"; - - for(i = 0; i < step; i++) out<<"uniform samplerRECT tex"<<i<<",\n"; - - if(start) - { - out<<"uniform float width, \nin float2 tpos : TEXCOORD0){\n"; - out<<"float index = floor(tpos.y) * width + floor(tpos.x);\n"; - out<<"float2 pos = float2(0.5, 0.5);\n"; - }else - { - out<<"uniform samplerRECT tex, in float2 tpos: TEXCOORD0 ){\n"; - out<<"float4 tc = texRECT( tex, tpos);\n"; - out<<"float2 pos = tc.rg; float index = tc.b;\n"; - out<<"if(index==-1) {FragColor = tc; return;}\n"; - } - out<<"float2 sum; float4 cc;\n"; - - - - if(step>0) - { - out<<"float2 cpos = float2(-0.5, 0.5);\t float2 opos;\n"; - for(i = 0; i < step; i++) - { - - out<<"cc = texRECT(tex"<<i<<", pos);\n"; - out<<"if(cc.z == -1){FragColor = cc; return;}"; - out<<"sum.x = cc.r + cc.g;if (index < sum.x){ if(index < cc.r) opos = cpos.xx; else {opos = cpos.yx; index -= cc.r;}}\n"; - out<<"else {index -= sum.x; if(index < cc.b) opos = cpos.xy; else{opos = cpos.yy; index -= cc.b;}}"; - out<<"pos = (pos + pos + opos);\n"; - } - } - out<<"FragColor = float4(pos, index, 1);\n"; - out<<"}\n"<<'\0'; - return new ProgramCG(buffer); -} - - -void ShaderBagPKCG:: LoadKeypointShader(float threshold, float edge_threshold) -{ - // - ProgramCG * program; - char buffer[10240]; - float threshold0 = threshold* (GlobalUtil::_SubpixelLocalization?0.8f:1.0f); - float threshold1 = threshold; - float threshold2 = (edge_threshold+1)*(edge_threshold+1)/edge_threshold; - ostrstream out(buffer, 10240); - out<<setprecision(8); - //tex(X)(Y) - //X: (CLR) (CENTER 0, LEFT -1, RIGHT +1) - //Y: (CDU) (CENTER 0, DOWN -1, UP +1) - out << "#define THRESHOLD0 " << threshold0 << "\n" - "#define THRESHOLD1 " << threshold1 << "\n" - "#define THRESHOLD2 " << threshold2 << "\n"; - - out<< - "void main (\n" - "float4 TexCC : TEXCOORD0, float4 TexLC : TEXCOORD1,\n" - "float4 TexRC : TEXCOORD2, float4 TexCD : TEXCOORD3, \n" - "float4 TexCU : TEXCOORD4, float4 TexLD : TEXCOORD5, \n" - "float4 TexLU : TEXCOORD6, float4 TexRD : TEXCOORD7,\n" - "out float4 FragData0 : COLOR0, uniform samplerRECT tex, \n" - "uniform samplerRECT texU, uniform samplerRECT texD)\n" - "{\n" - " float2 TexRU = float2(TexRC.x, TexCU.y); \n" - " float4 ccc = texRECT(tex, TexCC.xy);\n" - " float4 clc = texRECT(tex, TexLC.xy);\n" - " float4 crc = texRECT(tex, TexRC.xy);\n" - " float4 ccd = texRECT(tex, TexCD.xy);\n" - " float4 ccu = texRECT(tex, TexCU.xy);\n" - " float4 cld = texRECT(tex, TexLD.xy);\n" - " float4 clu = texRECT(tex, TexLU.xy);\n" - " float4 crd = texRECT(tex, TexRD.xy);\n" - " float4 cru = texRECT(tex, TexRU.xy);\n" - " float4 cc = ccc;\n" - " float4 v1[4], v2[4];\n" - " v1[0] = float4(clc.g, ccc.g, ccd.b, ccc.b);\n" - " v1[1] = float4(ccc.r, crc.r, ccd.a, ccc.a);\n" - " v1[2] = float4(clc.a, ccc.a, ccc.r, ccu.r);\n" - " v1[3] = float4(ccc.b, crc.b, ccc.g, ccu.g);\n" - " v2[0] = float4(cld.a, clc.a, ccd.a, ccc.a);\n" - " v2[1] = float4(ccd.b, ccc.b, crd.b, crc.b);\n" - " v2[2] = float4(clc.g, clu.g, ccc.g, ccu.g);\n" - " v2[3] = float4(ccc.r, ccu.r, crc.r, cru.r);\n" - - //test against 8 neighbours - //use variable to identify type of extremum - //1.0 for local maximum and -1.0 for minimum - << - " float4 key ={0, 0, 0, 0}; \n" - " for(int i = 0; i < 4; i++)\n" - " {\n" - " bool4 test1 = cc[i] > max(v1[i], v2[i]), test2 = cc[i] < min(v1[i], v2[i]);\n" - " key[i] = cc[i] > THRESHOLD0 && all(test1.xy&&test1.zw)?1.0: 0.0;\n" - " key[i] = cc[i] < -THRESHOLD0 && all(test2.xy&&test2.zw)? -1.0: key[i];\n" - " }\n" - " if(TexCC.x < 1.0) {key.rb = 0;}\n" - " if(TexCC.y < 1.0) {key.rg = 0;}\n" - " FragData0 = float4(0.0);\n" - " if(all(key == 0.0)) return; \n"; - - //do edge supression first.. - //vector v1 is < (-1, 0), (1, 0), (0,-1), (0, 1)> - //vector v2 is < (-1,-1), (-1,1), (1,-1), (1, 1)> - - out<< - " float fxx[4], fyy[4], fxy[4], fx[4], fy[4];\n" - " for(int i = 0; i < 4; i++) \n" - " {\n" - " if(key[i] != 0)\n" - " {\n" - " float4 D2 = v1[i].xyzw - cc[i];\n" - " float2 D4 = v2[i].xw - v2[i].yz;\n" - " float2 D5 = 0.5*(v1[i].yw-v1[i].xz); \n" - " fx[i] = D5.x;\n" - " fy[i] = D5.y ;\n" - " fxx[i] = D2.x + D2.y;\n" - " fyy[i] = D2.z + D2.w;\n" - " fxy[i] = 0.25*(D4.x + D4.y);\n" - " float fxx_plus_fyy = fxx[i] + fyy[i];\n" - " float score_up = fxx_plus_fyy*fxx_plus_fyy; \n" - " float score_down = (fxx[i]*fyy[i] - fxy[i]*fxy[i]);\n" - " if( score_down <= 0 || score_up > THRESHOLD2 * score_down)key[i] = 0;\n" - " }\n" - " }\n" - " if(all(key == 0.0)) return; \n\n"; - - //////////////////////////////////////////////// - //read 9 pixels of upper/lower level - out<< - " float4 v4[4], v5[4], v6[4];\n" - " ccc = texRECT(texU, TexCC.xy);\n" - " clc = texRECT(texU, TexLC.xy);\n" - " crc = texRECT(texU, TexRC.xy);\n" - " ccd = texRECT(texU, TexCD.xy);\n" - " ccu = texRECT(texU, TexCU.xy);\n" - " cld = texRECT(texU, TexLD.xy);\n" - " clu = texRECT(texU, TexLU.xy);\n" - " crd = texRECT(texU, TexRD.xy);\n" - " cru = texRECT(texU, TexRU.xy);\n" - " float4 cu = ccc;\n" - " v4[0] = float4(clc.g, ccc.g, ccd.b, ccc.b);\n" - " v4[1] = float4(ccc.r, crc.r, ccd.a, ccc.a);\n" - " v4[2] = float4(clc.a, ccc.a, ccc.r, ccu.r);\n" - " v4[3] = float4(ccc.b, crc.b, ccc.g, ccu.g);\n" - " v6[0] = float4(cld.a, clc.a, ccd.a, ccc.a);\n" - " v6[1] = float4(ccd.b, ccc.b, crd.b, crc.b);\n" - " v6[2] = float4(clc.g, clu.g, ccc.g, ccu.g);\n" - " v6[3] = float4(ccc.r, ccu.r, crc.r, cru.r);\n" - << - " for(int i = 0; i < 4; i++)\n" - " {\n" - " if(key[i] == 1.0)\n" - " {\n" - " bool4 test = cc[i]< max(v4[i], v6[i]); \n" - " if(cc[i] < cu[i] || any(test.xy||test.zw))key[i] = 0.0; \n" - " }else if(key[i] == -1.0)\n" - " {\n" - " bool4 test = cc[i]> min( v4[i], v6[i]); \n" - " if(cc[i] > cu[i] || any(test.xy||test.zw))key[i] = 0.0; \n" - " }\n" - " }\n" - " if(all(key == 0.0)) return; \n" - << - " ccc = texRECT(texD, TexCC.xy);\n" - " clc = texRECT(texD, TexLC.xy);\n" - " crc = texRECT(texD, TexRC.xy);\n" - " ccd = texRECT(texD, TexCD.xy);\n" - " ccu = texRECT(texD, TexCU.xy);\n" - " cld = texRECT(texD, TexLD.xy);\n" - " clu = texRECT(texD, TexLU.xy);\n" - " crd = texRECT(texD, TexRD.xy);\n" - " cru = texRECT(texD, TexRU.xy);\n" - " float4 cd = ccc;\n" - " v5[0] = float4(clc.g, ccc.g, ccd.b, ccc.b);\n" - " v5[1] = float4(ccc.r, crc.r, ccd.a, ccc.a);\n" - " v5[2] = float4(clc.a, ccc.a, ccc.r, ccu.r);\n" - " v5[3] = float4(ccc.b, crc.b, ccc.g, ccu.g);\n" - " v6[0] = float4(cld.a, clc.a, ccd.a, ccc.a);\n" - " v6[1] = float4(ccd.b, ccc.b, crd.b, crc.b);\n" - " v6[2] = float4(clc.g, clu.g, ccc.g, ccu.g);\n" - " v6[3] = float4(ccc.r, ccu.r, crc.r, cru.r);\n" - << - " for(int i = 0; i < 4; i++)\n" - " {\n" - " if(key[i] == 1.0)\n" - " {\n" - " bool4 test = cc[i]< max(v5[i], v6[i]);\n" - " if(cc[i] < cd[i] || any(test.xy||test.zw))key[i] = 0.0; \n" - " }else if(key[i] == -1.0)\n" - " {\n" - " bool4 test = cc[i]>min(v5[i],v6[i]);\n" - " if(cc[i] > cd[i] || any(test.xy||test.zw))key[i] = 0.0; \n" - " }\n" - " }\n" - " float keysum = dot(abs(key), float4(1, 1, 1, 1)) ;\n" - " //assume there is only one keypoint in the four. \n" - " if(keysum != 1.0) return; \n"; - - ////////////////////////////////////////////////////////////////////// - if(GlobalUtil::_SubpixelLocalization) - - out << - " float3 offset = float3(0, 0, 0); \n" - " /*The unrolled follwing loop is faster than a dynamic indexing version.*/\n" - " for(int idx = 1; idx < 4; idx++)\n" - " {\n" - " if(key[idx] != 0) \n" - " {\n" - " cu[0] = cu[idx]; cd[0] = cd[idx]; cc[0] = cc[idx]; \n" - " v4[0] = v4[idx]; v5[0] = v5[idx]; \n" - " fxy[0] = fxy[idx]; fxx[0] = fxx[idx]; fyy[0] = fyy[idx]; \n" - " fx[0] = fx[idx]; fy[0] = fy[idx]; \n" - " }\n" - " }\n" - << - - " float fs = 0.5*( cu[0] - cd[0] ); \n" - " float fss = cu[0] + cd[0] - cc[0] - cc[0];\n" - " float fxs = 0.25 * (v4[0].y + v5[0].x - v4[0].x - v5[0].y);\n" - " float fys = 0.25 * (v4[0].w + v5[0].z - v4[0].z - v5[0].w);\n" - " float4 A0, A1, A2 ; \n" - " A0 = float4(fxx[0], fxy[0], fxs, -fx[0]); \n" - " A1 = float4(fxy[0], fyy[0], fys, -fy[0]); \n" - " A2 = float4(fxs, fys, fss, -fs); \n" - " float3 x3 = abs(float3(fxx[0], fxy[0], fxs)); \n" - " float maxa = max(max(x3.x, x3.y), x3.z); \n" - " if(maxa >= 1e-10 ) \n" - " { \n" - " if(x3.y ==maxa ) \n" - " { \n" - " float4 TEMP = A1; A1 = A0; A0 = TEMP; \n" - " }else if( x3.z == maxa ) \n" - " { \n" - " float4 TEMP = A2; A2 = A0; A0 = TEMP; \n" - " } \n" - " A0 /= A0.x; \n" - " A1 -= A1.x * A0; \n" - " A2 -= A2.x * A0; \n" - " float2 x2 = abs(float2(A1.y, A2.y)); \n" - " if( x2.y > x2.x ) \n" - " { \n" - " float3 TEMP = A2.yzw; \n" - " A2.yzw = A1.yzw; \n" - " A1.yzw = TEMP; \n" - " x2.x = x2.y; \n" - " } \n" - " if(x2.x >= 1e-10) { \n" - " A1.yzw /= A1.y; \n" - " A2.yzw -= A2.y * A1.yzw; \n" - " if(abs(A2.z) >= 1e-10) {\n" - " offset.z = A2.w /A2.z; \n" - " offset.y = A1.w - offset.z*A1.z; \n" - " offset.x = A0.w - offset.z*A0.z - offset.y*A0.y; \n" - " bool test = (abs(cc[0] + 0.5*dot(float3(fx[0], fy[0], fs), offset ))>THRESHOLD1) ;\n" - " if(!test || any( abs(offset) >= 1.0)) return;\n" - " }\n" - " }\n" - " }\n" - <<"\n" - " float keyv = dot(key, float4(1.0, 2.0, 3.0, 4.0));\n" - " FragData0 = float4(keyv, offset);\n" - "}\n" <<'\0'; - - else out << "\n" - " float keyv = dot(key, float4(1.0, 2.0, 3.0, 4.0));\n" - " FragData0 = float4(keyv, 0, 0, 0);\n" - "}\n" <<'\0'; - - s_keypoint = program = new ProgramCG(buffer); - //parameter - _param_dog_texu = cgGetNamedParameter(*program, "texU"); - _param_dog_texd = cgGetNamedParameter(*program, "texD"); -} - -void ShaderBagPKCG::LoadOrientationShader() -{ - char buffer[10240]; - ostrstream out(buffer,10240); - - out<<"\n" - "#define GAUSSIAN_WF "<<GlobalUtil::_OrientationGaussianFactor<<" \n" - "#define SAMPLE_WF ("<<GlobalUtil::_OrientationWindowFactor<< " )\n" - "#define ORIENTATION_THRESHOLD "<< GlobalUtil::_MulitiOrientationThreshold << "\n" - "void main(uniform samplerRECT tex, uniform samplerRECT gtex, \n" - " uniform samplerRECT otex, uniform float4 size, in float2 TexCoord0 : TEXCOORD0, \n" - " out float4 FeatureData : COLOR0 "; - - //multi orientation output - //use one additional texture to store up to four orientations - //when we use one 32bit float to store two orientations, no extra texture is required - - if(GlobalUtil::_MaxOrientation >1 && GlobalUtil::_OrientationPack2 == 0) - out<<", out float4 OrientationData : COLOR1"; - - - //use 9 float4 to store histogram of 36 directions - out<<") \n" - "{ \n" - " float4 bins[10]; \n" - " for (int i=0; i<9; i++) bins[i] = float4(0,0,0,0); \n" - " float4 sift = texRECT(tex, TexCoord0); \n" - " float2 pos = sift.xy; \n" - " bool orientation_mode = (size.z != 0); \n" - " float sigma = orientation_mode? (abs(size.z) * pow(size.w, sift.w) * sift.z) : (sift.w); \n" - " //bool fixed_orientation = (size.z < 0); \n" - " if(size.z < 0) {FeatureData = float4(pos, 0, sigma); return;}" - " float gsigma = sigma * GAUSSIAN_WF; \n" - " float2 win = abs(sigma.xx) * (SAMPLE_WF * GAUSSIAN_WF); \n" - " float2 dim = size.xy; \n" - " float4 dist_threshold = float4(win.x*win.x+0.5); \n" - " float factor = -0.5/(gsigma*gsigma); \n" - " float4 sz; float2 spos; \n" - " //if(any(pos.xy <= 1)) discard; \n" - " sz.xy = max( pos - win, float2(2,2)); \n" - " sz.zw = min( pos + win, dim-3); \n" - " sz = floor(sz*0.5) + 0.5; "; - //loop to get the histogram - - out<<"\n" - " for(spos.y = sz.y; spos.y <= sz.w; spos.y+=1.0) \n" - " { \n" - " for(spos.x = sz.x; spos.x <= sz.z; spos.x+=1.0) \n" - " { \n" - " float2 offset = 2* spos - pos - 0.5; \n" - " float4 off = float4(offset, offset + 1); \n" - " float4 distsq = off.xzxz * off.xzxz + off.yyww * off.yyww; \n" - " bool4 inside = distsq < dist_threshold; \n" - " if(any(inside.xy||inside.zw)) \n" - " { \n" - " float4 gg = texRECT(gtex, spos); \n" - " float4 oo = texRECT(otex, spos); \n" - " float4 weight = gg * exp(distsq * factor); \n" - " float4 idxv = floor(degrees(oo)*0.1); \n" - " idxv = idxv<0? idxv + 36.0: idxv; \n" - " float4 vidx = 4.0* fract(idxv * 0.25);//fmod(idxv, 4.0);\n"; - - // - if(GlobalUtil::_UseDynamicIndexing && strcmp(cgGetProfileString(ProgramCG::_FProfile), "gp4fp")==0) - //if(ProgramCG::_FProfile == CG_PROFILE_GPU_FP) this enumerant is not defined in cg1.5 - { - //gp4fp supports dynamic indexing, but it might be slow on some GPUs - out<<"\n" - " for(int i = 0 ; i < 4; i++)\n" - " {\n" - " if(inside[i])\n" - " {\n" - " float idx = idxv[i]; \n" - " float4 inc = weight[i] * float4(vidx[i] == float4(0,1,2,3)); \n" - " int iidx = int(floor(idx*0.25)); \n" - " bins[iidx]+=inc; \n" - " } \n" - " } \n" - " } \n" - " } \n" - " }"; - - }else - { - //nvfp40 still does not support dynamic array indexing - //unrolled binary search - //it seems to be faster than the dyanmic indexing version on some GPUs - out<<"\n" - " for(int i = 0 ; i < 4; i++)\n" - " {\n" - " if(inside[i])\n" - " {\n" - " float idx = idxv[i]; \n" - " float4 inc = weight[i] * float4(vidx[i] == float4(0,1,2,3)); \n" - " if(idx < 16) \n" - " { \n" - " if(idx < 8) \n" - " { \n" - " if(idx < 4) { bins[0]+=inc;} \n" - " else { bins[1]+=inc;} \n" - " }else \n" - " { \n" - " if(idx < 12){ bins[2]+=inc;} \n" - " else { bins[3]+=inc;} \n" - " } \n" - " }else if(idx < 32) \n" - " { \n" - " if(idx < 24) \n" - " { \n" - " if(idx <20) { bins[4]+=inc;} \n" - " else { bins[5]+=inc;} \n" - " }else \n" - " { \n" - " if(idx < 28){ bins[6]+=inc;} \n" - " else { bins[7]+=inc;} \n" - " } \n" - " }else \n" - " { \n" - " bins[8]+=inc; \n" - " } \n" - " } \n" - " } \n" - " } \n" - " } \n" - " }"; - - } - - //reuse the code from the unpacked version.. - ShaderBagCG::WriteOrientationCodeToStream(out); - - - ProgramCG * program; - s_orientation = program = new ProgramCG(buffer); - _param_orientation_gtex = cgGetNamedParameter(*program, "gtex"); - _param_orientation_otex = cgGetNamedParameter(*program, "otex"); - _param_orientation_size = cgGetNamedParameter(*program, "size"); - - -} - -void ShaderBagPKCG::LoadDescriptorShader() -{ - GlobalUtil::_DescriptorPPT = 16; - LoadDescriptorShaderF2(); - -} - -void ShaderBagPKCG::LoadDescriptorShaderF2() -{ - //one shader outpout 128/8 = 16 , each fragout encodes 4 - //const double twopi = 2.0*3.14159265358979323846; - //const double rpi = 8.0/twopi; - char buffer[10240]; - ostrstream out(buffer, 10240); - - out<<setprecision(8); - - out<<"\n" - "#define M_PI 3.14159265358979323846\n" - "#define TWO_PI (2.0*M_PI)\n" - "#define RPI 1.2732395447351626861510701069801\n" - "#define WF size.z\n" - "void main(uniform samplerRECT tex, \n" - "uniform samplerRECT gtex, \n" - "uniform samplerRECT otex, \n" - "uniform float4 dsize, \n" - "uniform float3 size, \n" - "in float2 TexCoord0 : TEXCOORD0, \n" - "out float4 FragData0:COLOR0, \n" - "out float4 FragData1:COLOR1) \n" - "{\n" - " float2 dim = size.xy; //image size \n" - " float index = dsize.x*floor(TexCoord0.y * 0.5) + TexCoord0.x;\n" - " float idx = 8.0 * frac(index * 0.125) + 8.0 * floor(2.0 * frac(TexCoord0.y * 0.5)); \n" - " index = floor(index*0.125)+ 0.49; \n" - " float2 coord = floor( float2( fmod(index, dsize.z), index*dsize.w)) + 0.5 ;\n" - " float2 pos = texRECT(tex, coord).xy; \n" - " if(any(pos.xy <= 1) || any(pos.xy >=dim-1)) " - " //discard; \n" - " { FragData0 = FragData1 = float4(0.0); return; }\n" - " float anglef = texRECT(tex, coord).z;\n" - " if(anglef > M_PI) anglef -= TWO_PI;\n" - " float sigma = texRECT(tex, coord).w; \n" - " float spt = abs(sigma * WF); //default to be 3*sigma \n"; - //rotation - out<< - " float4 cscs, rots; \n" - " sincos(anglef, cscs.y, cscs.x); \n" - " cscs.zw = - cscs.xy; \n" - " rots = cscs /spt; \n" - " cscs *= spt; \n"; - - //here cscs is actually (cos, sin, -cos, -sin) * (factor: 3)*sigma - //and rots is (cos, sin, -cos, -sin ) /(factor*sigma) - //devide the 4x4 sift grid into 16 1x1 block, and each corresponds to a shader thread - //To use linear interoplation, 1x1 is increased to 2x2, by adding 0.5 to each side - out<< - " float4 temp; float2 pt, offsetpt; \n" - " /*the fraction part of idx is .5*/ \n" - " offsetpt.x = 4.0 * fract(idx * 0.25) - 2.0; \n" - " offsetpt.y = floor(idx*0.25) - 1.5; \n" - " temp = cscs.xwyx*offsetpt.xyxy; \n" - " pt = pos + temp.xz + temp.yw; \n"; - - //get a horizontal bounding box of the rotated rectangle - out<< - " float2 bwin = abs(cscs.xy); \n" - " float bsz = bwin.x + bwin.y; \n" - " float4 sz; float2 spos; \n" - " sz.xy = max(pt - bsz, float2(2,2));\n" - " sz.zw = min(pt + bsz, dim - 3); \n" - " sz = floor(sz * 0.5) + 0.5;"; //move sample point to pixel center - //get voting for two box - - out<<"\n" - " float4 DA, DB; \n" - " DA = DB = float4(0, 0, 0, 0); \n" - " float4 nox = float4(0, rots.xy, rots.x + rots.y); \n" - " float4 noy = float4(0, rots.wx, rots.w + rots.x); \n" - " for(spos.y = sz.y; spos.y <= sz.w; spos.y+=1.0) \n" - " { \n" - " for(spos.x = sz.x; spos.x <= sz.z; spos.x+=1.0) \n" - " { \n" - " float2 tpt = spos * 2.0 - pt - 0.5; \n" - " float4 temp = rots.xywx * tpt.xyxy; \n" - " float2 temp2 = temp.xz + temp.yw; \n" - " float4 nx = temp2.x + nox; \n" - " float4 ny = temp2.y + noy; \n" - " float4 nxn = abs(nx), nyn = abs(ny); \n" - " bool4 inside = (max(nxn, nyn) < 1.0); \n" - " if(any(inside.xy || inside.zw))\n" - " {\n" - " float4 gg = texRECT(gtex, spos);\n" - " float4 oo = texRECT(otex, spos);\n" - " float4 theta0 = (anglef - oo)*RPI;\n" - " float4 theta = theta0 < 0? theta0 + 8.0 : theta0;//8.0 * frac(1.0 + 0.125 * theta0);// \n" - " float4 theta1 = floor(theta); \n" - " float4 diffx = nx + offsetpt.x, diffy = ny + offsetpt.y; \n" - " float4 ww = exp(-0.125 * (diffx * diffx + diffy * diffy )); \n" - " float4 weight = (1 - nxn) * (1 - nyn) * gg * ww; \n" - " float4 weight2 = (theta - theta1) * weight; \n" - " float4 weight1 = weight - weight2; \n" - " for(int i = 0;i < 4; i++)\n" - " {\n" - " if(inside[i])\n" - " {\n" - " DA += float4(theta1[i] == float4(0, 1, 2, 3))*weight1[i]; \n" - " DA += float4(theta1[i] == float4(7, 0, 1, 2))*weight2[i]; \n" - " DB += float4(theta1[i] == float4(4, 5, 6, 7))*weight1[i]; \n" - " DB += float4(theta1[i] == float4(3, 4, 5, 6))*weight2[i]; \n" - " }\n" - " }\n" - " }\n" - " }\n" - " }\n"; - out<< - " FragData0 = DA; FragData1 = DB;\n" - "}\n"<<'\0'; - ProgramCG * program; - - s_descriptor_fp = program = new ProgramCG(buffer); - _param_descriptor_gtex = cgGetNamedParameter(*program, "gtex"); - _param_descriptor_otex = cgGetNamedParameter(*program, "otex"); - _param_descriptor_size = cgGetNamedParameter(*program, "size"); - _param_descriptor_dsize = cgGetNamedParameter(*program, "dsize"); - -} - -void ShaderBagPKCG::SetMarginCopyParam(int xmax, int ymax) -{ - float truncate[4]; - truncate[0] = (xmax - 0.5f) * 0.5f; //((xmax + 1) >> 1) - 0.5f; - truncate[1] = (ymax - 0.5f) * 0.5f; //((ymax + 1) >> 1) - 0.5f; - truncate[2] = (xmax %2 == 1)? 0.0f: 1.0f; - truncate[3] = truncate[2] + (((ymax % 2) == 1)? 0.0f : 2.0f); - cgGLSetParameter4fv(_param_margin_copy_truncate, truncate); -} - -void ShaderBagPKCG::SetGradPassParam(int texP) -{ - cgGLSetTextureParameter(_param_grad_pass_texp, texP); - cgGLEnableTextureParameter(_param_grad_pass_texp); -} - -void ShaderBagPKCG::SetGenListEndParam(int ktex) -{ - cgGLSetTextureParameter(_param_genlist_end_ktex, ktex); - cgGLEnableTextureParameter(_param_genlist_end_ktex); -} - -void ShaderBagPKCG::SetDogTexParam(int texU, int texD) -{ - cgGLSetTextureParameter(_param_dog_texu, texU); - cgGLEnableTextureParameter(_param_dog_texu); - cgGLSetTextureParameter(_param_dog_texd, texD); - cgGLEnableTextureParameter(_param_dog_texd); -} - -void ShaderBagPKCG::SetGenListInitParam(int w, int h) -{ - float bbox[4] = {(w -1.0f) * 0.5f +0.25f, (w-1.0f) * 0.5f - 0.25f, (h - 1.0f) * 0.5f + 0.25f, (h-1.0f) * 0.5f - 0.25f}; - cgGLSetParameter4fv(_param_genlist_init_bbox, bbox); -} - - -void ShaderBagPKCG::SetGenListStartParam(float width, int tex0) -{ - cgGLSetParameter1f(_param_ftex_width, width); - - if(_param_genlist_start_tex0) - { - cgGLSetTextureParameter(_param_genlist_start_tex0, tex0); - cgGLEnableTextureParameter(_param_genlist_start_tex0); - } -} - - - -void ShaderBagPKCG::SetGenListStepParam(int tex, int tex0) -{ - cgGLSetTextureParameter(_param_genlist_step_tex, tex); - cgGLEnableTextureParameter(_param_genlist_step_tex); - cgGLSetTextureParameter(_param_genlist_step_tex0, tex0); - cgGLEnableTextureParameter(_param_genlist_step_tex0); -} - -void ShaderBagPKCG::SetGenVBOParam(float width, float fwidth, float size) -{ - float sizes[4] = {size*3.0f, fwidth, width, 1.0f/width}; - cgGLSetParameter4fv(_param_genvbo_size, sizes); -} - -void ShaderBagPKCG::SetSimpleOrientationInput(int oTex, float sigma, float sigma_step) -{ - cgGLSetTextureParameter(_param_orientation_gtex, oTex); - cgGLEnableTextureParameter(_param_orientation_gtex); - cgGLSetParameter2f(_param_orientation_size, sigma, sigma_step); -} - - -void ShaderBagPKCG::SetFeatureOrientationParam(int gtex, int width, int height, float sigma, int otex, float step) -{ - /// - cgGLSetTextureParameter(_param_orientation_gtex, gtex); - cgGLEnableTextureParameter(_param_orientation_gtex); - cgGLSetTextureParameter(_param_orientation_otex, otex); - cgGLEnableTextureParameter(_param_orientation_otex); - - float size[4]; - size[0] = (float)width; - size[1] = (float)height; - size[2] = sigma; - size[3] = step; - cgGLSetParameter4fv(_param_orientation_size, size); - -} - -void ShaderBagPKCG::SetFeatureDescirptorParam(int gtex, int otex, float dwidth, float fwidth, float width, float height, float sigma) -{ - /// - - cgGLSetTextureParameter(_param_descriptor_gtex, gtex); - cgGLEnableTextureParameter(_param_descriptor_gtex); - cgGLSetTextureParameter(_param_descriptor_otex, otex); - cgGLEnableTextureParameter(_param_descriptor_otex); - - - float dsize[4] ={dwidth, 1.0f/dwidth, fwidth, 1.0f/fwidth}; - cgGLSetParameter4fv(_param_descriptor_dsize, dsize); - float size[3]; - size[0] = width; - size[1] = height; - size[2] = GlobalUtil::_DescriptorWindowFactor; - cgGLSetParameter3fv(_param_descriptor_size, size); - - -} - -#endif - diff --git a/3rdparty/SiftGPU/src/SiftGPU/ProgramCG.h b/3rdparty/SiftGPU/src/SiftGPU/ProgramCG.h deleted file mode 100644 index 6ce072cd..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/ProgramCG.h +++ /dev/null @@ -1,161 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: ProgramCG.h -// Author: Changchang Wu -// Description : interface for the ProgramCG classes. -// ProgramCG: Cg programs -// ShaderBagCG: All Cg shaders for Sift in a bag -// FilterGLCG: Cg Gaussian Filters -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - - -#if defined(CG_SIFTGPU_ENABLED) - -#ifndef _PROGRAM_CG_H -#define _PROGRAM_CG_H - -#include "ProgramGPU.h" -class FragmentProgram; -#include "Cg/cgGL.h" - -class ProgramCG:public ProgramGPU -{ - CGprogram _programID; - CGprofile _profile; - int _valid; -public: - static CGcontext _Context; - static CGprofile _FProfile; -public: - operator CGprogram (){return _programID;} - CGprogram GetProgramID(){return _programID;} - int UseProgram(); - int IsValidProgram(){return _programID && _valid;} - static void ErrorCallback(); - static void InitContext(); - static void DestroyContext(); - ProgramCG(const char * code, const char** cg_compile_args= NULL, CGprofile profile = ProgramCG::_FProfile); - ProgramCG(); - virtual ~ProgramCG(); - -}; - -class ShaderBagCG:public ShaderBag -{ - CGparameter _param_dog_texu; - CGparameter _param_dog_texd; - CGparameter _param_genlist_start_tex0; - CGparameter _param_ftex_width; - CGparameter _param_genlist_step_tex; - CGparameter _param_genlist_step_tex0; - CGparameter _param_genvbo_size; - CGparameter _param_orientation_gtex; - CGparameter _param_orientation_stex; - CGparameter _param_orientation_size; - CGparameter _param_descriptor_gtex; - CGparameter _param_descriptor_size; - CGparameter _param_descriptor_dsize; - CGparameter _param_margin_copy_truncate; - CGparameter _param_genlist_init_bbox; -public: - virtual void LoadDescriptorShader(); - void LoadDescriptorShaderF2(); - static void WriteOrientationCodeToStream(ostream& out); - virtual void SetGenListInitParam(int w, int h); - virtual void SetMarginCopyParam(int xmax, int ymax); - virtual void SetFeatureOrientationParam(int gtex, int width, int height, float sigma, int stex = 0, float step = 1.0f); - virtual void SetFeatureDescirptorParam(int gtex, int otex, float dwidth, float fwidth, float width, float height, float sigma); - virtual void SetSimpleOrientationInput(int oTex, float sigma, float sigma_step); - void LoadOrientationShader(); - virtual void SetGenListStartParam(float width, int tex0); - static ProgramCG* LoadGenListStepShader(int start, int step); - static ProgramCG* LoadGenListStepShaderV2(int start, int step); - void LoadGenListShader(int ndoglev, int nlev); - virtual void UnloadProgram(); - virtual void SetDogTexParam(int texU, int texD); - virtual void SetGenListStepParam(int tex, int tex0); - virtual void SetGenVBOParam( float width, float fwidth, float size); - virtual void LoadFixedShaders(); - virtual void LoadDisplayShaders(); - virtual void LoadKeypointShader(float threshold, float edgeThreshold); - virtual int LoadKeypointShaderMR(float threshold, float edgeThreshold); - ShaderBagCG(); - virtual ~ShaderBagCG(){} -}; - - -class FilterGLCG : public FilterProgram -{ -private: - ProgramGPU* CreateFilterH(float kernel[], float offset[], int width); - ProgramGPU* CreateFilterV(float kernel[], float offset[], int height); - //packed version - ProgramGPU* CreateFilterHPK(float kernel[], float offset[], int width); - ProgramGPU* CreateFilterVPK(float kernel[], float offset[], int height); -}; - -class ShaderBagPKCG:public ShaderBag -{ -private: - CGparameter _param_dog_texu; - CGparameter _param_dog_texd; - CGparameter _param_margin_copy_truncate; - CGparameter _param_grad_pass_texp; - CGparameter _param_genlist_init_bbox; - CGparameter _param_genlist_start_tex0; - CGparameter _param_ftex_width; - CGparameter _param_genlist_step_tex; - CGparameter _param_genlist_step_tex0; - CGparameter _param_genlist_end_ktex; - CGparameter _param_genvbo_size; - CGparameter _param_orientation_gtex; - CGparameter _param_orientation_otex; - CGparameter _param_orientation_size; - CGparameter _param_descriptor_gtex; - CGparameter _param_descriptor_otex; - CGparameter _param_descriptor_size; - CGparameter _param_descriptor_dsize; - -public: - ShaderBagPKCG(); - virtual ~ShaderBagPKCG(){} - virtual void LoadDescriptorShader(); - virtual void LoadDescriptorShaderF2(); - virtual void LoadOrientationShader(); - virtual void LoadGenListShader(int ndoglev, int nlev); - virtual void LoadGenListShaderV2(int ndoglev, int nlev); - virtual void UnloadProgram() ; - virtual void LoadKeypointShader(float threshold, float edgeTrheshold); - virtual void LoadFixedShaders(); - virtual void LoadDisplayShaders(); - virtual void SetGradPassParam(int texP); - virtual void SetGenListEndParam(int ktex); -public: - //parameters - virtual void SetGenListStartParam(float width, int tex0); - virtual void SetGenListInitParam(int w, int h); - virtual void SetMarginCopyParam(int xmax, int ymax); - virtual void SetDogTexParam(int texU, int texD); - virtual void SetGenListStepParam(int tex, int tex0); - virtual void SetGenVBOParam( float width, float fwidth, float size); - virtual void SetFeatureDescirptorParam(int gtex, int otex, float dwidth, float fwidth, float width, float height, float sigma); - virtual void SetFeatureOrientationParam(int gtex, int width, int height, float sigma, int stex, float step); - virtual void SetSimpleOrientationInput(int oTex, float sigma, float sigma_step); -}; -#endif -#endif - diff --git a/3rdparty/SiftGPU/src/SiftGPU/ProgramCL.cpp b/3rdparty/SiftGPU/src/SiftGPU/ProgramCL.cpp deleted file mode 100644 index 3b13ff12..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/ProgramCL.cpp +++ /dev/null @@ -1,1592 +0,0 @@ -////////////////////////////////////////////////////////////////////////////// -// File: ProgramCL.cpp -// Author: Changchang Wu -// Description : implementation of CL related class. -// class ProgramCL A simple wrapper of Cg programs -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - -#if defined(CL_SIFTGPU_ENABLED) - -#include <CL/opencl.h> -#include <GL/glew.h> - -#include <iostream> -#include <iomanip> -#include <vector> -#include <strstream> -#include <algorithm> -#include <stdlib.h> -#include <math.h> -#include <string.h> -using namespace std; - -#include "GlobalUtil.h" -#include "CLTexImage.h" -#include "ProgramCL.h" -#include "SiftGPU.h" - - -#if defined(_WIN32) - #pragma comment (lib, "OpenCL.lib") -#endif - -#ifndef _INC_WINDOWS -#ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN -#endif -#include <windows.h> -#endif - -////////////////////////////////////////////////////////////////////// -// Construction/Destruction -////////////////////////////////////////////////////////////////////// - -ProgramCL::ProgramCL() -{ - _program = NULL; - _kernel = NULL; - _valid = 0; -} - -ProgramCL::~ProgramCL() -{ - if(_kernel) clReleaseKernel(_kernel); - if(_program) clReleaseProgram(_program); -} - -ProgramCL::ProgramCL(const char* name, const char * code, cl_context context, cl_device_id device) : _valid(1) -{ - const char * src[1] = {code}; cl_int status; - - _program = clCreateProgramWithSource(context, 1, src, NULL, &status); - if(status != CL_SUCCESS) _valid = 0; - - status = clBuildProgram(_program, 0, NULL, - GlobalUtil::_debug ? - "-cl-fast-relaxed-math -cl-single-precision-constant -cl-nv-verbose" : - "-cl-fast-relaxed-math -cl-single-precision-constant", NULL, NULL); - - if(status != CL_SUCCESS) {PrintBuildLog(device, 1); _valid = 0;} - else if(GlobalUtil::_debug) PrintBuildLog(device, 0); - - _kernel = clCreateKernel(_program, name, &status); - if(status != CL_SUCCESS) _valid = 0; -} - -void ProgramCL::PrintBuildLog(cl_device_id device, int all) -{ - char buffer[10240] = "\0"; - cl_int status = clGetProgramBuildInfo( - _program, device, CL_PROGRAM_BUILD_LOG, sizeof(buffer), buffer, NULL); - if(all ) - { - std::cerr << buffer << endl; - }else - { - const char * pos = strstr(buffer, "ptxas"); - if(pos) std::cerr << pos << endl; - } -} - -/////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////PACKED VERSION?/////////////////////////////////// - -ProgramBagCL::ProgramBagCL() -{ - //////////////////////////////////// - _context = NULL; _queue = NULL; - s_gray = s_sampling = NULL; - s_packup = s_zero_pass = NULL; - s_gray_pack = s_unpack = NULL; - s_sampling_u = NULL; - s_dog_pass = NULL; - s_grad_pass = NULL; - s_grad_pass2 = NULL; - s_unpack_dog = NULL; - s_unpack_grd = NULL; - s_unpack_key = NULL; - s_keypoint = NULL; - f_gaussian_skip0 = NULL; - f_gaussian_skip1 = NULL; - f_gaussian_step = 0; - - //////////////////////////////// - GlobalUtil::StartTimer("Initialize OpenCL"); - if(!InitializeContext()) return; - GlobalUtil::StopTimer(); - -} - - - -ProgramBagCL::~ProgramBagCL() -{ - if(s_gray) delete s_gray; - if(s_sampling) delete s_sampling; - if(s_zero_pass) delete s_zero_pass; - if(s_packup) delete s_packup; - if(s_unpack) delete s_unpack; - if(s_gray_pack) delete s_gray_pack; - if(s_sampling_u) delete s_sampling_u; - if(s_dog_pass) delete s_dog_pass; - if(s_grad_pass) delete s_grad_pass; - if(s_grad_pass2) delete s_grad_pass2; - if(s_unpack_dog) delete s_unpack_dog; - if(s_unpack_grd) delete s_unpack_grd; - if(s_unpack_key) delete s_unpack_key; - if(s_keypoint) delete s_keypoint; - - if(f_gaussian_skip1) delete f_gaussian_skip1; - - for(unsigned int i = 0; i < f_gaussian_skip0_v.size(); i++) - { - if(f_gaussian_skip0_v[i]) delete f_gaussian_skip0_v[i]; - } - if(f_gaussian_step && _gaussian_step_num > 0) - { - for(int i = 0; i< _gaussian_step_num; i++) - { - delete f_gaussian_step[i]; - } - delete[] f_gaussian_step; - } - - ////////////////////////////////////// - if(_context) clReleaseContext(_context); - if(_queue) clReleaseCommandQueue(_queue); -} - -bool ProgramBagCL::InitializeContext() -{ - cl_uint num_platform, num_device; - cl_int status; - // Get OpenCL platform count - status = clGetPlatformIDs (0, NULL, &num_platform); - if (status != CL_SUCCESS || num_platform == 0) return false; - - cl_platform_id platforms[16]; - if(num_platform > 16 ) num_platform = 16; - status = clGetPlatformIDs (num_platform, platforms, NULL); - _platform = platforms[0]; - - /////////////////////////////// - status = clGetDeviceIDs(_platform, CL_DEVICE_TYPE_GPU, 0, NULL, &num_device); - if(status != CL_SUCCESS || num_device == 0) return false; - - // Create the device list - cl_device_id* devices = new cl_device_id [num_device]; - status = clGetDeviceIDs(_platform, CL_DEVICE_TYPE_GPU, num_device, devices, NULL); - _device = (status == CL_SUCCESS? devices[0] : 0); delete[] devices; - if(status != CL_SUCCESS) return false; - - - if(GlobalUtil::_verbose) - { - cl_device_mem_cache_type is_gcache; - clGetDeviceInfo(_device, CL_DEVICE_GLOBAL_MEM_CACHE_TYPE, sizeof(is_gcache), &is_gcache, NULL); - if(is_gcache == CL_NONE) std::cout << "No cache for global memory\n"; - //else if(is_gcache == CL_READ_ONLY_CACHE) std::cout << "Read only cache for global memory\n"; - //else std::cout << "Read/Write cache for global memory\n"; - } - - //context; - if(GlobalUtil::_UseSiftGPUEX) - { - cl_context_properties prop[] = { - CL_CONTEXT_PLATFORM, (cl_context_properties)_platform, - CL_GL_CONTEXT_KHR, (cl_context_properties)wglGetCurrentContext(), - CL_WGL_HDC_KHR, (cl_context_properties)wglGetCurrentDC(), 0 }; - _context = clCreateContext(prop, 1, &_device, NULL, NULL, &status); - if(status != CL_SUCCESS) return false; - }else - { - _context = clCreateContext(0, 1, &_device, NULL, NULL, &status); - if(status != CL_SUCCESS) return false; - } - - //command queue - _queue = clCreateCommandQueue(_context, _device, 0, &status); - return status == CL_SUCCESS; -} - -void ProgramBagCL::InitProgramBag(SiftParam&param) -{ - GlobalUtil::StartTimer("Load Programs"); - LoadFixedShaders(); - LoadDynamicShaders(param); - if(GlobalUtil::_UseSiftGPUEX) LoadDisplayShaders(); - GlobalUtil::StopTimer(); -} - - -void ProgramBagCL::UnloadProgram() -{ - -} - -void ProgramBagCL::FinishCL() -{ - clFinish(_queue); -} - -void ProgramBagCL::LoadFixedShaders() -{ - - - s_gray = new ProgramCL( "gray", - "__kernel void gray(__read_only image2d_t input, __write_only image2d_t output) {\n" - "sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;\n" - "int2 coord = (int2)(get_global_id(0), get_global_id(1));\n" - "float4 weight = (float4)(0.299, 0.587, 0.114, 0.0);\n" - "float intensity = dot(weight, read_imagef(input,sampler, coord ));\n" - "float4 result= (float4)(intensity, intensity, intensity, 1.0);\n" - "write_imagef(output, coord, result); }", _context, _device ); - - - s_sampling = new ProgramCL("sampling", - "__kernel void sampling(__read_only image2d_t input, __write_only image2d_t output,\n" - " int width, int height) {\n" - "sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;\n" - "int x = get_global_id(0), y = get_global_id(1); \n" - "if( x >= width || y >= height) return;\n" - "int xa = x + x, ya = y + y; \n" - "int xb = xa + 1, yb = ya + 1; \n" - "float v1 = read_imagef(input, sampler, (int2) (xa, ya)).x; \n" - "float v2 = read_imagef(input, sampler, (int2) (xb, ya)).x; \n" - "float v3 = read_imagef(input, sampler, (int2) (xa, yb)).x; \n" - "float v4 = read_imagef(input, sampler, (int2) (xb, yb)).x; \n" - "float4 result = (float4) (v1, v2, v3, v4);" - "write_imagef(output, (int2) (x, y), result); }" , _context, _device); - - s_sampling_k = new ProgramCL("sampling_k", - "__kernel void sampling_k(__read_only image2d_t input, __write_only image2d_t output, " - " int width, int height,\n" - " int step, int halfstep) {\n" - "sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;\n" - "int x = get_global_id(0), y = get_global_id(1); \n" - "if( x >= width || y >= height) return;\n" - "int xa = x * step, ya = y *step; \n" - "int xb = xa + halfstep, yb = ya + halfstep; \n" - "float v1 = read_imagef(input, sampler, (int2) (xa, ya)).x; \n" - "float v2 = read_imagef(input, sampler, (int2) (xb, ya)).x; \n" - "float v3 = read_imagef(input, sampler, (int2) (xa, yb)).x; \n" - "float v4 = read_imagef(input, sampler, (int2) (xb, yb)).x; \n" - "float4 result = (float4) (v1, v2, v3, v4);" - "write_imagef(output, (int2) (x, y), result); }" , _context, _device); - - - s_sampling_u = new ProgramCL("sampling_u", - "__kernel void sampling_u(__read_only image2d_t input, \n" - " __write_only image2d_t output,\n" - " int width, int height,\n" - " float step, float halfstep) {\n" - "sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_LINEAR;\n" - "int x = get_global_id(0), y = get_global_id(1); \n" - "if( x >= width || y >= height) return;\n" - "float xa = x * step, ya = y *step; \n" - "float xb = xa + halfstep, yb = ya + halfstep; \n" - "float v1 = read_imagef(input, sampler, (float2) (xa, ya)).x; \n" - "float v2 = read_imagef(input, sampler, (float2) (xb, ya)).x; \n" - "float v3 = read_imagef(input, sampler, (float2) (xa, yb)).x; \n" - "float v4 = read_imagef(input, sampler, (float2) (xb, yb)).x; \n" - "float4 result = (float4) (v1, v2, v3, v4);" - "write_imagef(output, (int2) (x, y), result); }" , _context, _device); - - - s_zero_pass = new ProgramCL("zero_pass", - "__kernel void zero_pass(__write_only image2d_t output){\n" - "int2 coord = (int2)(get_global_id(0), get_global_id(1));\n" - "write_imagef(output, coord, (float4)(0.0));}", _context, _device); - - s_packup = new ProgramCL("packup", - "__kernel void packup(__global float* input, __write_only image2d_t output,\n" - " int twidth, int theight, int width){\n" - "int2 coord = (int2)(get_global_id(0), get_global_id(1));\n" - "if(coord.x >= twidth || coord.y >= theight) return;\n" - "int index0 = (coord.y + coord.y) * width; \n" - "int index1 = index0 + coord.x;\n" - "int x2 = min(width -1, coord.x); \n" - "float v1 = input[index1 + coord.x], v2 = input[index1 + x2]; \n" - "int index2 = index1 + width; \n" - "float v3 = input[index2 + coord.x], v4 = input[index2 + x2]; \n " - "write_imagef(output, coord, (float4) (v1, v2, v3, v4));}", _context, _device); - - s_dog_pass = new ProgramCL("dog_pass", - "__kernel void dog_pass(__read_only image2d_t tex, __read_only image2d_t texp,\n" - " __write_only image2d_t dog, int width, int height) {\n" - "sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE |\n" - " CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;\n" - "int2 coord = (int2)(get_global_id(0), get_global_id(1)); \n" - "if( coord.x >= width || coord.y >= height) return;\n" - "float4 cc = read_imagef(tex , sampler, coord); \n" - "float4 cp = read_imagef(texp, sampler, coord);\n" - "write_imagef(dog, coord, cc - cp); }\n", _context, _device); - - s_grad_pass = new ProgramCL("grad_pass", - "__kernel void grad_pass(__read_only image2d_t tex, __read_only image2d_t texp,\n" - " __write_only image2d_t dog, int width, int height,\n" - " __write_only image2d_t grad, __write_only image2d_t rot) {\n" - "sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE |\n" - " CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;\n" - "int x = get_global_id(0), y = get_global_id(1); \n" - "if( x >= width || y >= height) return;\n" - "int2 coord = (int2) (x, y);\n" - "float4 cc = read_imagef(tex , sampler, coord); \n" - "float4 cp = read_imagef(texp, sampler, coord);\n" - "float2 cl = read_imagef(tex, sampler, (int2)(x - 1, y)).yw;\n" - "float2 cr = read_imagef(tex, sampler, (int2)(x + 1, y)).xz;\n" - "float2 cd = read_imagef(tex, sampler, (int2)(x, y - 1)).zw;\n" - "float2 cu = read_imagef(tex, sampler, (int2)(x, y + 1)).xy;\n" - "write_imagef(dog, coord, cc - cp); \n" - "float4 dx = (float4)(cc.y - cl.x, cr.x - cc.x, cc.w - cl.y, cr.y - cc.z);\n" - "float4 dy = (float4)(cc.zw - cd.xy, cu.xy - cc.xy);\n" - "write_imagef(grad, coord, 0.5 * sqrt(dx*dx + dy * dy));\n" - "write_imagef(rot, coord, atan2(dy, dx + (float4) (FLT_MIN)));}\n", _context, _device); - - s_grad_pass2 = new ProgramCL("grad_pass2", - "#define BLOCK_DIMX 32\n" - "#define BLOCK_DIMY 14\n" - "#define BLOCK_SIZE (BLOCK_DIMX * BLOCK_DIMY)\n" - "__kernel void grad_pass2(__read_only image2d_t tex, __read_only image2d_t texp,\n" - " __write_only image2d_t dog, int width, int height,\n" - " __write_only image2d_t grd, __write_only image2d_t rot){\n"//, __local float* block) {\n" - "__local float block[BLOCK_SIZE * 4]; \n" - "sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE |\n" - " CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;\n" - "int2 coord = (int2) ( get_global_id(0) - get_group_id(0) * 2 - 1, \n" - " get_global_id(1) - get_group_id(1) * 2- 1); \n" - "int idx = mad24(get_local_id(1), BLOCK_DIMX, get_local_id(0));\n" - "float4 cc = read_imagef(tex, sampler, coord);\n" - "block[idx ] = cc.x;\n" - "block[idx + BLOCK_SIZE ] = cc.y;\n" - "block[idx + BLOCK_SIZE * 2] = cc.z;\n" - "block[idx + BLOCK_SIZE * 3] = cc.w;\n" - "barrier(CLK_LOCAL_MEM_FENCE);\n" - "if( get_local_id(0) == 0 || get_local_id(0) == BLOCK_DIMX - 1) return;\n" - "if( get_local_id(1) == 0 || get_local_id(1) == BLOCK_DIMY - 1) return;\n" - "if( coord.x >= width) return; \n" - "if( coord.y >= height) return;\n" - "float4 cp = read_imagef(texp, sampler, coord);\n" - "float4 dx = (float4)( cc.y - block[idx - 1 + BLOCK_SIZE], \n" - " block[idx + 1] - cc.x, \n" - " cc.w - block[idx - 1 + 3 * BLOCK_SIZE], \n" - " block[idx + 1 + 2 * BLOCK_SIZE] - cc.z);\n" - "float4 dy = (float4)( cc.z - block[idx - BLOCK_DIMX + 2 * BLOCK_SIZE], \n" - " cc.w - block[idx - BLOCK_DIMX + 3 * BLOCK_SIZE]," - //" cc.zw - block[idx - BLOCK_DIMX].zw, \n" - " block[idx + BLOCK_DIMX] - cc.x,\n " - " block[idx + BLOCK_DIMX + BLOCK_SIZE] - cc.y);\n" - //" block[idx + BLOCK_DIMX].xy - cc.xy);\n" - "write_imagef(dog, coord, cc - cp); \n" - "write_imagef(grd, coord, 0.5 * sqrt(dx*dx + dy * dy));\n" - "write_imagef(rot, coord, atan2(dy, dx + (float4) (FLT_MIN)));}\n", _context, _device); -} - -void ProgramBagCL::LoadDynamicShaders(SiftParam& param) -{ - LoadKeypointShader(); - LoadGenListShader(param._dog_level_num, 0); - CreateGaussianFilters(param); -} - - -void ProgramBagCL::SelectInitialSmoothingFilter(int octave_min, SiftParam&param) -{ - float sigma = param.GetInitialSmoothSigma(octave_min); - if(sigma == 0) - { - f_gaussian_skip0 = NULL; - }else - { - for(unsigned int i = 0; i < f_gaussian_skip0_v.size(); i++) - { - if(f_gaussian_skip0_v[i]->_id == octave_min) - { - f_gaussian_skip0 = f_gaussian_skip0_v[i]; - return ; - } - } - FilterCL * filter = CreateGaussianFilter(sigma); - filter->_id = octave_min; - f_gaussian_skip0_v.push_back(filter); - f_gaussian_skip0 = filter; - } - -} - -void ProgramBagCL::CreateGaussianFilters(SiftParam&param) -{ - if(param._sigma_skip0>0.0f) - { - f_gaussian_skip0 = CreateGaussianFilter(param._sigma_skip0); - f_gaussian_skip0->_id = GlobalUtil::_octave_min_default; - f_gaussian_skip0_v.push_back(f_gaussian_skip0); - } - if(param._sigma_skip1>0.0f) - { - f_gaussian_skip1 = CreateGaussianFilter(param._sigma_skip1); - } - - f_gaussian_step = new FilterCL*[param._sigma_num]; - for(int i = 0; i< param._sigma_num; i++) - { - f_gaussian_step[i] = CreateGaussianFilter(param._sigma[i]); - } - _gaussian_step_num = param._sigma_num; -} - - -FilterCL* ProgramBagCL::CreateGaussianFilter(float sigma) -{ - //pixel inside 3*sigma box - int sz = int( ceil( GlobalUtil::_FilterWidthFactor * sigma -0.5) ) ;// - int width = 2*sz + 1; - - //filter size truncation - if(GlobalUtil::_MaxFilterWidth >0 && width > GlobalUtil::_MaxFilterWidth) - { - std::cout<<"Filter size truncated from "<<width<<" to "<<GlobalUtil::_MaxFilterWidth<<endl; - sz = GlobalUtil::_MaxFilterWidth>>1; - width = 2 * sz + 1; - } - - int i; - float * kernel = new float[width]; - float rv = 1.0f/(sigma*sigma); - float v, ksum =0; - - // pre-compute filter - for( i = -sz ; i <= sz ; ++i) - { - kernel[i+sz] = v = exp(-0.5f * i * i *rv) ; - ksum += v; - } - - //normalize the kernel - rv = 1.0f / ksum; - for(i = 0; i< width ;i++) kernel[i]*=rv; - - FilterCL * filter = CreateFilter(kernel, width); - delete [] kernel; - if(GlobalUtil::_verbose && GlobalUtil::_timingL) std::cout<<"Filter: sigma = "<<sigma<<", size = "<<width<<"x"<<width<<endl; - return filter; -} - -FilterCL* ProgramBagCL::CreateFilter(float kernel[], int width) -{ - FilterCL * filter = new FilterCL; - filter->s_shader_h = CreateFilterH(kernel, width); - filter->s_shader_v = CreateFilterV(kernel, width); - filter->_size = width; - filter->_id = 0; - return filter; -} - -ProgramCL* ProgramBagCL::CreateFilterH(float kernel[], int width) -{ - int halfwidth = width >>1; - float * pf = kernel + halfwidth; - int nhpixel = (halfwidth+1)>>1; //how many neighbour pixels need to be looked up - int npixel = (nhpixel<<1)+1;// - float weight[3]; - - //////////////////////////// - char buffer[10240]; - ostrstream out(buffer, 10240); - out<<setprecision(8); - - - //CL_DEVICE_IMAGE2D_MAX_WIDTH - out<< - "const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;" - "__kernel void filter_h(__read_only image2d_t input, \n" - " __write_only image2d_t output, int width_, int height_) {\n" - "int x = get_global_id(0);\n" - "int y = get_global_id(1); \n" - "if( x > width_ || y > height_) return; \n" - "float4 pc; int2 coord; \n" - "float4 result = (float4)(0.0);\n"; - for(int i = 0 ; i < npixel ; i++) - { - out<<"coord = (int2)(x + ("<< (i - nhpixel) << "), y);\n"; - out<<"pc= read_imagef(input, sampler, coord);\n"; - if(GlobalUtil::_PreciseBorder) - out<<"if(coord.x < 0) pc = pc.xxzz; else if (coord.x > width_) pc = pc.yyww; \n"; - //for each sub-pixel j in center, the weight of sub-pixel k - int xw = (i - nhpixel)*2; - for(int j = 0; j < 3; j++) - { - int xwn = xw + j -1; - weight[j] = xwn < -halfwidth || xwn > halfwidth? 0 : pf[xwn]; - } - if(weight[1] == 0.0) - { - out<<"result += (float4)("<<weight[2]<<","<<weight[0]<<","<<weight[2]<<","<<weight[0]<<") * pc.yxwz;\n"; - } - else - { - out<<"result += (float4)("<<weight[1]<<", "<<weight[0]<<", "<<weight[1]<<", "<<weight[0]<<") * pc.xxzz;\n"; - out<<"result += (float4)("<<weight[2]<<", "<<weight[1]<<", "<<weight[2]<<", "<<weight[1]<<") * pc.yyww;\n"; - } - } - out << "write_imagef(output, (int2)(x, y), result); }\n" << '\0'; - return new ProgramCL("filter_h", buffer, _context, _device); -} - - - -ProgramCL* ProgramBagCL::CreateFilterV(float kernel[], int width) -{ - - int halfwidth = width >>1; - float * pf = kernel + halfwidth; - int nhpixel = (halfwidth+1)>>1; //how many neighbour pixels need to be looked up - int npixel = (nhpixel<<1)+1;// - float weight[3]; - - //////////////////////////// - char buffer[10240]; - ostrstream out(buffer, 10240); - out<<setprecision(8); - - - //CL_DEVICE_IMAGE2D_MAX_WIDTH - out<< - "const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;" - "__kernel void filter_v(__read_only image2d_t input, \n" - " __write_only image2d_t output, int width_, int height_) {\n" - "int x = get_global_id(0);\n" - "int y = get_global_id(1); \n" - "if( x > width_ || y >= height_) return; \n" - "float4 pc; int2 coord; \n" - "float4 result = (float4)(0.0);\n"; - for(int i = 0 ; i < npixel ; i++) - { - out<<"coord = (int2)(x, y + ("<< (i - nhpixel) << "));\n"; - out<<"pc= read_imagef(input, sampler, coord);\n"; - if(GlobalUtil::_PreciseBorder) - out<<"if(coord.y < 0) pc = pc.xyxy; else if (coord.y > height_) pc = pc.zwzw; \n"; - //for each sub-pixel j in center, the weight of sub-pixel k - int xw = (i - nhpixel)*2; - for(int j = 0; j < 3; j++) - { - int xwn = xw + j -1; - weight[j] = xwn < -halfwidth || xwn > halfwidth? 0 : pf[xwn]; - } - if(weight[1] == 0.0) - { - out<<"result += (float4)("<<weight[2]<<","<<weight[2]<<","<<weight[0]<<","<<weight[0]<<") * pc.zwxy;\n"; - } - else - { - out<<"result += (float4)("<<weight[1]<<", "<<weight[1]<<", "<<weight[0]<<", "<<weight[0]<<") * pc.xyxy;\n"; - out<<"result += (float4)("<<weight[2]<<", "<<weight[2]<<", "<<weight[1]<<", "<<weight[1]<<") * pc.zwzw;\n"; - } - } - out << "write_imagef(output, (int2)(x, y), result); }\n" << '\0'; - return new ProgramCL("filter_v", buffer, _context, _device); - -} - -void ProgramBagCL::FilterImage(FilterCL* filter, CLTexImage *dst, CLTexImage *src, CLTexImage*tmp) -{ - cl_kernel kernelh = filter->s_shader_h->_kernel; - cl_kernel kernelv = filter->s_shader_v->_kernel; - ////////////////////////////////////////////////////////////////// - - cl_int status, w = dst->GetImgWidth(), h = dst->GetImgHeight(); - cl_int w_ = w - 1, h_ = h - 1; - - size_t dim0 = 16, dim1 = 16; - size_t gsz[2] = {(w + dim0 - 1) / dim0 * dim0, (h + dim1 - 1) / dim1 * dim1}, lsz[2] = {dim0, dim1}; - - clSetKernelArg(kernelh, 0, sizeof(cl_mem), &src->_clData); - clSetKernelArg(kernelh, 1, sizeof(cl_mem), &tmp->_clData); - clSetKernelArg(kernelh, 2, sizeof(cl_int), &w_); - clSetKernelArg(kernelh, 3, sizeof(cl_int), &h_); - status = clEnqueueNDRangeKernel(_queue, kernelh, 2, NULL, gsz, lsz, 0, NULL, NULL); - CheckErrorCL(status, "ProgramBagCL::FilterImageH"); - if(status != CL_SUCCESS) return; - - clSetKernelArg(kernelv, 0, sizeof(cl_mem), &tmp->_clData); - clSetKernelArg(kernelv, 1, sizeof(cl_mem), &dst->_clData); - clSetKernelArg(kernelv, 2, sizeof(cl_int), &w_); - clSetKernelArg(kernelv, 3, sizeof(cl_int), &h_); - size_t gsz2[2] = {(w + dim1 - 1) / dim1 * dim1, (h + dim0 - 1) / dim0 * dim0}, lsz2[2] = {dim1, dim0}; - status = clEnqueueNDRangeKernel(_queue, kernelv, 2, NULL, gsz2, lsz2, 0, NULL, NULL); - CheckErrorCL(status, "ProgramBagCL::FilterImageV"); - //clReleaseEvent(event); -} - -void ProgramBagCL::SampleImageU(CLTexImage *dst, CLTexImage *src, int log_scale) -{ - cl_kernel kernel= s_sampling_u->_kernel; - float scale = 1.0f / (1 << log_scale); - float offset = scale * 0.5f; - cl_int w = dst->GetImgWidth(), h = dst->GetImgHeight(); - clSetKernelArg(kernel, 0, sizeof(cl_mem), &(src->_clData)); - clSetKernelArg(kernel, 1, sizeof(cl_mem), &(dst->_clData)); - clSetKernelArg(kernel, 2, sizeof(cl_int), &(w)); - clSetKernelArg(kernel, 3, sizeof(cl_int), &(h)); - clSetKernelArg(kernel, 4, sizeof(cl_float), &(scale)); - clSetKernelArg(kernel, 5, sizeof(cl_float), &(offset)); - - size_t dim0 = 16, dim1 = 16; - //while( w * h / dim0 / dim1 < 8 && dim1 > 1) dim1 /= 2; - size_t gsz[2] = {(w + dim0 - 1) / dim0 * dim0, (h + dim1 - 1) / dim1 * dim1}, lsz[2] = {dim0, dim1}; - cl_int status = clEnqueueNDRangeKernel(_queue, kernel, 2, NULL, gsz, lsz, 0, NULL, NULL); - CheckErrorCL(status, "ProgramBagCL::SampleImageU"); -} - -void ProgramBagCL::SampleImageD(CLTexImage *dst, CLTexImage *src, int log_scale) -{ - cl_kernel kernel; - cl_int w = dst->GetImgWidth(), h = dst->GetImgHeight(); - if(log_scale == 1) - { - kernel = s_sampling->_kernel; - clSetKernelArg(kernel, 0, sizeof(cl_mem), &(src->_clData)); - clSetKernelArg(kernel, 1, sizeof(cl_mem), &(dst->_clData)); - clSetKernelArg(kernel, 2, sizeof(cl_int), &(w)); - clSetKernelArg(kernel, 3, sizeof(cl_int), &(h)); - }else - { - cl_int fullstep = (1 << log_scale); - cl_int halfstep = fullstep >> 1; - kernel = s_sampling_k->_kernel; - clSetKernelArg(kernel, 0, sizeof(cl_mem), &(src->_clData)); - clSetKernelArg(kernel, 1, sizeof(cl_mem), &(dst->_clData)); - clSetKernelArg(kernel, 2, sizeof(cl_int), &(w)); - clSetKernelArg(kernel, 3, sizeof(cl_int), &(h)); - clSetKernelArg(kernel, 4, sizeof(cl_int), &(fullstep)); - clSetKernelArg(kernel, 5, sizeof(cl_int), &(halfstep)); - } - size_t dim0 = 128, dim1 = 1; - //while( w * h / dim0 / dim1 < 8 && dim1 > 1) dim1 /= 2; - size_t gsz[2] = {(w + dim0 - 1) / dim0 * dim0, (h + dim1 - 1) / dim1 * dim1}, lsz[2] = {dim0, dim1}; - cl_int status = clEnqueueNDRangeKernel(_queue, kernel, 2, NULL, gsz, lsz, 0, NULL, NULL); - CheckErrorCL(status, "ProgramBagCL::SampleImageD"); -} - -void ProgramBagCL::FilterInitialImage(CLTexImage* tex, CLTexImage* buf) -{ - if(f_gaussian_skip0) FilterImage(f_gaussian_skip0, tex, tex, buf); -} - -void ProgramBagCL::FilterSampledImage(CLTexImage* tex, CLTexImage* buf) -{ - if(f_gaussian_skip1) FilterImage(f_gaussian_skip1, tex, tex, buf); -} - -void ProgramBagCL::ComputeDOG(CLTexImage*tex, CLTexImage* texp, CLTexImage* dog, CLTexImage* grad, CLTexImage* rot) -{ - int margin = 0, use_gm2 = 1; - bool both_grad_dog = rot->_clData && grad->_clData; - cl_int w = tex->GetImgWidth(), h = tex->GetImgHeight(); - cl_kernel kernel ; size_t dim0, dim1; - if(!both_grad_dog) {kernel = s_dog_pass->_kernel; dim0 = 16; dim1 = 12; } - else if(use_gm2) {kernel = s_grad_pass2->_kernel; dim0 = 32; dim1 = 14; margin = 2; } - else {kernel = s_grad_pass->_kernel; dim0 = 16; dim1 = 20; } - size_t gsz[2] = { (w + dim0 - 1 - margin) / (dim0 - margin) * dim0, - (h + dim1 - 1 - margin) / (dim1 - margin) * dim1}; - size_t lsz[2] = {dim0, dim1}; - clSetKernelArg(kernel, 0, sizeof(cl_mem), &(tex->_clData)); - clSetKernelArg(kernel, 1, sizeof(cl_mem), &(texp->_clData)); - clSetKernelArg(kernel, 2, sizeof(cl_mem), &(dog->_clData)); - clSetKernelArg(kernel, 3, sizeof(cl_int), &(w)); - clSetKernelArg(kernel, 4, sizeof(cl_int), &(h)); - if(both_grad_dog) - { - clSetKernelArg(kernel, 5, sizeof(cl_mem), &(grad->_clData)); - clSetKernelArg(kernel, 6, sizeof(cl_mem), &(rot->_clData)); - } - /////////////////////////////////////////////////////// - cl_int status = clEnqueueNDRangeKernel(_queue, kernel, 2, NULL, gsz, lsz, 0, NULL, NULL); - CheckErrorCL(status, "ProgramBagCL::ComputeDOG"); -} - - -void ProgramBagCL::ComputeKEY(CLTexImage*dog, CLTexImage* key, float Tdog, float Tedge) -{ - cl_kernel kernel = s_keypoint->_kernel; - cl_int w = key->GetImgWidth(), h = key->GetImgHeight(); - float threshold0 = Tdog* (GlobalUtil::_SubpixelLocalization?0.8f:1.0f); - float threshold1 = Tdog; - float threshold2 = (Tedge+1)*(Tedge+1)/Tedge; - - clSetKernelArg(kernel, 0, sizeof(cl_mem), &(dog->_clData)); - clSetKernelArg(kernel, 1, sizeof(cl_mem), &((dog + 1)->_clData)); - clSetKernelArg(kernel, 2, sizeof(cl_mem), &((dog - 1)->_clData)); - clSetKernelArg(kernel, 3, sizeof(cl_mem), &(key->_clData)); - clSetKernelArg(kernel, 4, sizeof(cl_float), &(threshold0)); - clSetKernelArg(kernel, 5, sizeof(cl_float), &(threshold1)); - clSetKernelArg(kernel, 6, sizeof(cl_float), &(threshold2)); - clSetKernelArg(kernel, 7, sizeof(cl_int), &(w)); - clSetKernelArg(kernel, 8, sizeof(cl_int), &(h)); - - size_t dim0 = 8, dim1 = 8; - //if( w * h / dim0 / dim1 < 16) dim1 /= 2; - size_t gsz[2] = {(w + dim0 - 1) / dim0 * dim0, (h + dim1 - 1) / dim1 * dim1}, lsz[2] = {dim0, dim1}; - cl_int status = clEnqueueNDRangeKernel(_queue, kernel, 2, NULL, gsz, lsz, 0, NULL, NULL); - CheckErrorCL(status, "ProgramBagCL::ComputeKEY"); -} - -void ProgramBagCL::UnpackImage(CLTexImage*src, CLTexImage* dst) -{ - cl_kernel kernel = s_unpack->_kernel; - cl_int w = dst->GetImgWidth(), h = dst->GetImgHeight(); - clSetKernelArg(kernel, 0, sizeof(cl_mem), &(src->_clData)); - clSetKernelArg(kernel, 1, sizeof(cl_mem), &(dst->_clData)); - clSetKernelArg(kernel, 2, sizeof(cl_int), &(w)); - clSetKernelArg(kernel, 3, sizeof(cl_int), &(h)); - const size_t dim0 = 16, dim1 = 16; - size_t gsz[2] = {(w + dim0 - 1) / dim0 * dim0, (h + dim1 - 1) / dim1 * dim1}, lsz[2] = {dim0, dim1}; - cl_int status = clEnqueueNDRangeKernel(_queue, kernel, 2, NULL, gsz, lsz, 0, NULL, NULL); - - CheckErrorCL(status, "ProgramBagCL::UnpackImage"); - FinishCL(); - -} - -void ProgramBagCL::UnpackImageDOG(CLTexImage*src, CLTexImage* dst) -{ - if(s_unpack_dog == NULL) return; - cl_kernel kernel = s_unpack_dog->_kernel; - cl_int w = dst->GetImgWidth(), h = dst->GetImgHeight(); - clSetKernelArg(kernel, 0, sizeof(cl_mem), &(src->_clData)); - clSetKernelArg(kernel, 1, sizeof(cl_mem), &(dst->_clData)); - clSetKernelArg(kernel, 2, sizeof(cl_int), &(w)); - clSetKernelArg(kernel, 3, sizeof(cl_int), &(h)); - const size_t dim0 = 16, dim1 = 16; - size_t gsz[2] = {(w + dim0 - 1) / dim0 * dim0, (h + dim1 - 1) / dim1 * dim1}, lsz[2] = {dim0, dim1}; - cl_int status = clEnqueueNDRangeKernel(_queue, kernel, 2, NULL, gsz, lsz, 0, NULL, NULL); - - CheckErrorCL(status, "ProgramBagCL::UnpackImage"); - FinishCL(); -} - -void ProgramBagCL::UnpackImageGRD(CLTexImage*src, CLTexImage* dst) -{ - if(s_unpack_grd == NULL) return; - cl_kernel kernel = s_unpack_grd->_kernel; - cl_int w = dst->GetImgWidth(), h = dst->GetImgHeight(); - clSetKernelArg(kernel, 0, sizeof(cl_mem), &(src->_clData)); - clSetKernelArg(kernel, 1, sizeof(cl_mem), &(dst->_clData)); - clSetKernelArg(kernel, 2, sizeof(cl_int), &(w)); - clSetKernelArg(kernel, 3, sizeof(cl_int), &(h)); - const size_t dim0 = 16, dim1 = 16; - size_t gsz[2] = {(w + dim0 - 1) / dim0 * dim0, (h + dim1 - 1) / dim1 * dim1}, lsz[2] = {dim0, dim1}; - cl_int status = clEnqueueNDRangeKernel(_queue, kernel, 2, NULL, gsz, lsz, 0, NULL, NULL); - - CheckErrorCL(status, "ProgramBagCL::UnpackImage"); - FinishCL(); -} -void ProgramBagCL::UnpackImageKEY(CLTexImage*src, CLTexImage* dog, CLTexImage* dst) -{ - if(s_unpack_key == NULL) return; - cl_kernel kernel = s_unpack_key->_kernel; - cl_int w = dst->GetImgWidth(), h = dst->GetImgHeight(); - clSetKernelArg(kernel, 0, sizeof(cl_mem), &(dog->_clData)); - clSetKernelArg(kernel, 1, sizeof(cl_mem), &(src->_clData)); - clSetKernelArg(kernel, 2, sizeof(cl_mem), &(dst->_clData)); - clSetKernelArg(kernel, 3, sizeof(cl_int), &(w)); - clSetKernelArg(kernel, 4, sizeof(cl_int), &(h)); - const size_t dim0 = 16, dim1 = 16; - size_t gsz[2] = {(w + dim0 - 1) / dim0 * dim0, (h + dim1 - 1) / dim1 * dim1}, lsz[2] = {dim0, dim1}; - cl_int status = clEnqueueNDRangeKernel(_queue, kernel, 2, NULL, gsz, lsz, 0, NULL, NULL); - - CheckErrorCL(status, "ProgramBagCL::UnpackImageKEY"); - FinishCL(); -} -void ProgramBagCL::LoadDescriptorShader() -{ - GlobalUtil::_DescriptorPPT = 16; - LoadDescriptorShaderF2(); -} - -void ProgramBagCL::LoadDescriptorShaderF2() -{ - -} - -void ProgramBagCL::LoadOrientationShader(void) -{ - -} - -void ProgramBagCL::LoadGenListShader(int ndoglev,int nlev) -{ - -} - -void ProgramBagCL::LoadKeypointShader() -{ - int i; char buffer[20240]; - ostrstream out(buffer, 20240); - streampos pos; - - //tex(X)(Y) - //X: (CLR) (CENTER 0, LEFT -1, RIGHT +1) - //Y: (CDU) (CENTER 0, DOWN -1, UP +1) - out<< - "__kernel void keypoint(__read_only image2d_t tex, __read_only image2d_t texU,\n" - " __read_only image2d_t texD, __write_only image2d_t texK,\n" - " float THRESHOLD0, float THRESHOLD1, \n" - " float THRESHOLD2, int width, int height)\n" - "{\n" - " sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | \n" - " CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;" - " int x = get_global_id(0), y = get_global_id(1);\n" - " if(x >= width || y >= height) return; \n" - " int xp = x - 1, xn = x + 1;\n" - " int yp = y - 1, yn = y + 1;\n" - " int2 coord0 = (int2) (x, y); \n" - " int2 coord1 = (int2) (xp, y); \n" - " int2 coord2 = (int2) (xn, y); \n" - " int2 coord3 = (int2) (x, yp); \n" - " int2 coord4 = (int2) (x, yn); \n" - " int2 coord5 = (int2) (xp, yp); \n" - " int2 coord6 = (int2) (xp, yn); \n" - " int2 coord7 = (int2) (xn, yp); \n" - " int2 coord8 = (int2) (xn, yn); \n" - " float4 ccc = read_imagef(tex, sampler,coord0);\n" - " float4 clc = read_imagef(tex, sampler,coord1);\n" - " float4 crc = read_imagef(tex, sampler,coord2);\n" - " float4 ccd = read_imagef(tex, sampler,coord3);\n" - " float4 ccu = read_imagef(tex, sampler,coord4);\n" - " float4 cld = read_imagef(tex, sampler,coord5);\n" - " float4 clu = read_imagef(tex, sampler,coord6);\n" - " float4 crd = read_imagef(tex, sampler,coord7);\n" - " float4 cru = read_imagef(tex, sampler,coord8);\n" - " float4 cc = ccc;\n" - " float4 v1[4], v2[4];\n" - " v1[0] = (float4)(clc.y, ccc.y, ccd.z, ccc.z);\n" - " v1[1] = (float4)(ccc.x, crc.x, ccd.w, ccc.w);\n" - " v1[2] = (float4)(clc.w, ccc.w, ccc.x, ccu.x);\n" - " v1[3] = (float4)(ccc.z, crc.z, ccc.y, ccu.y);\n" - " v2[0] = (float4)(cld.w, clc.w, ccd.w, ccc.w);\n" - " v2[1] = (float4)(ccd.z, ccc.z, crd.z, crc.z);\n" - " v2[2] = (float4)(clc.y, clu.y, ccc.y, ccu.y);\n" - " v2[3] = (float4)(ccc.x, ccu.x, crc.x, cru.x);\n" - " float4 key4 = (float4)(0); \n"; - //test against 8 neighbours - //use variable to identify type of extremum - //1.0 for local maximum and -1.0 for minimum - for(i = 0; i < 4; ++i) - out<< - " if(cc.s"<<i<<" > THRESHOLD0){ \n" - " if(all(isgreater((float4)(cc.s"<<i<<"), max(v1["<<i<<"], v2["<<i<<"]))))key4.s"<<i<<" = 1.0;\n" - " }else if(cc.s"<<i<<" < -THRESHOLD0){ \n" - " if(all(isless((float4)(cc.s"<<i<<"), min(v1["<<i<<"], v2["<<i<<"]))))key4.s"<<i<<" = -1.0;\n" - " }"; - - out<< - " if(x ==0) {key4.x = key4.z= 0; }\n" - " else if(x + 1 == width) {key4.y = key4.w = 0;}\n" - " if(y ==0) {key4.x = key4.y = 0; }\n" - " else if(y + 1 == height) {key4.z = key4.w = 0;}\n" - " float4 ak = fabs(key4); \n" - " float keysum = ak.x + ak.y + ak.z + ak.w; \n" - " float4 result = (float4)(0.0);\n" - " if(keysum == 1.0) {\n" - " float fxx[4], fyy[4], fxy[4], fx[4], fy[4];\n"; - - //do edge supression first.. - //vector v1 is < (-1, 0), (1, 0), (0,-1), (0, 1)> - //vector v2 is < (-1,-1), (-1,1), (1,-1), (1, 1)> - for(i = 0; i < 4; ++i) - out << - " if(key4.s"<<i<<" != 0)\n" - " {\n" - " float4 D2 = v1["<<i<<"].xyzw - cc.s"<<i<<";\n" - " float2 D4 = v2["<<i<<"].xw - v2["<<i<<"].yz;\n" - " float2 D5 = 0.5*(v1["<<i<<"].yw-v1["<<i<<"].xz); \n" - " fx["<<i<<"] = D5.x; fy["<<i<<"] = D5.y ;\n" - " fxx["<<i<<"] = D2.x + D2.y;\n" - " fyy["<<i<<"] = D2.z + D2.w;\n" - " fxy["<<i<<"] = 0.25*(D4.x + D4.y);\n" - " float fxx_plus_fyy = fxx["<<i<<"] + fyy["<<i<<"];\n" - " float score_up = fxx_plus_fyy*fxx_plus_fyy; \n" - " float score_down = (fxx["<<i<<"]*fyy["<<i<<"] - fxy["<<i<<"]*fxy["<<i<<"]);\n" - " if( score_down <= 0 || score_up > THRESHOLD2 * score_down)keysum = 0;\n" - " }\n"; - - out << - " if(keysum == 1) {\n"; - //////////////////////////////////////////////// - //read 9 pixels of upper/lower level - out<< - " float4 v4[4], v5[4], v6[4];\n" - " ccc = read_imagef(texU, sampler,coord0);\n" - " clc = read_imagef(texU, sampler,coord1);\n" - " crc = read_imagef(texU, sampler,coord2);\n" - " ccd = read_imagef(texU, sampler,coord3);\n" - " ccu = read_imagef(texU, sampler,coord4);\n" - " cld = read_imagef(texU, sampler,coord5);\n" - " clu = read_imagef(texU, sampler,coord6);\n" - " crd = read_imagef(texU, sampler,coord7);\n" - " cru = read_imagef(texU, sampler,coord8);\n" - " float4 cu = ccc;\n" - " v4[0] = (float4)(clc.y, ccc.y, ccd.z, ccc.z);\n" - " v4[1] = (float4)(ccc.x, crc.x, ccd.w, ccc.w);\n" - " v4[2] = (float4)(clc.w, ccc.w, ccc.x, ccu.x);\n" - " v4[3] = (float4)(ccc.z, crc.z, ccc.y, ccu.y);\n" - " v6[0] = (float4)(cld.w, clc.w, ccd.w, ccc.w);\n" - " v6[1] = (float4)(ccd.z, ccc.z, crd.z, crc.z);\n" - " v6[2] = (float4)(clc.y, clu.y, ccc.y, ccu.y);\n" - " v6[3] = (float4)(ccc.x, ccu.x, crc.x, cru.x);\n"; - - for(i = 0; i < 4; ++i) - out << - " if(key4.s"<<i<<" == 1.0)\n" - " {\n" - " if(cc.s"<<i<<" < cu.s"<<i<<" || \n" - " any(isless((float4)(cc.s"<<i<<"), max(v4["<<i<<"], v6["<<i<<"]))))keysum = 0; \n" - " }else if(key4.s"<<i<<" == -1.0)\n" - " {\n" - " if(cc.s"<<i<<" > cu.s"<<i<<" || \n" - " any(isgreater((float4)(cc.s"<<i<<"), min(v4["<<i<<"], v6["<<i<<"]))) )keysum = 0; \n" - " }\n"; - - out << - " if(keysum == 1.0) { \n"; - out << - " ccc = read_imagef(texD, sampler,coord0);\n" - " clc = read_imagef(texD, sampler,coord1);\n" - " crc = read_imagef(texD, sampler,coord2);\n" - " ccd = read_imagef(texD, sampler,coord3);\n" - " ccu = read_imagef(texD, sampler,coord4);\n" - " cld = read_imagef(texD, sampler,coord5);\n" - " clu = read_imagef(texD, sampler,coord6);\n" - " crd = read_imagef(texD, sampler,coord7);\n" - " cru = read_imagef(texD, sampler,coord8);\n" - " float4 cd = ccc;\n" - " v5[0] = (float4)(clc.y, ccc.y, ccd.z, ccc.z);\n" - " v5[1] = (float4)(ccc.x, crc.x, ccd.w, ccc.w);\n" - " v5[2] = (float4)(clc.w, ccc.w, ccc.x, ccu.x);\n" - " v5[3] = (float4)(ccc.z, crc.z, ccc.y, ccu.y);\n" - " v6[0] = (float4)(cld.w, clc.w, ccd.w, ccc.w);\n" - " v6[1] = (float4)(ccd.z, ccc.z, crd.z, crc.z);\n" - " v6[2] = (float4)(clc.y, clu.y, ccc.y, ccu.y);\n" - " v6[3] = (float4)(ccc.x, ccu.x, crc.x, cru.x);\n"; - for(i = 0; i < 4; ++i) - out << - " if(key4.s"<<i<<" == 1.0)\n" - " {\n" - " if(cc.s"<<i<<" < cd.s"<<i<<" ||\n" - " any(isless((float4)(cc.s"<<i<<"), max(v5["<<i<<"], v6["<<i<<"]))))keysum = 0; \n" - " }else if(key4.s"<<i<<" == -1.0)\n" - " {\n" - " if(cc.s"<<i<<" > cd.s"<<i<<" ||\n" - " any(isgreater((float4)(cc.s"<<i<<"), min(v5["<<i<<"], v6["<<i<<"]))))keysum = 0; \n" - " }\n"; - - out << - " if(keysum==1.0) {\n"; - ////////////////////////////////////////////////////////////////////// - if(GlobalUtil::_SubpixelLocalization) - { - out << - " float4 offset = (float4)(0); \n"; - for(i = 1; i < 4; ++i) - out << - " if(key4.s"<<i<<" != 0) \n" - " {\n" - " cu.s0 = cu.s"<<i<<"; cd.s0 = cd.s"<<i<<"; cc.s0 = cc.s"<<i<<"; \n" - " v4[0] = v4["<<i<<"]; v5[0] = v5["<<i<<"]; \n" - " fxy[0] = fxy["<<i<<"]; fxx[0] = fxx["<<i<<"]; fyy[0] = fyy["<<i<<"]; \n" - " fx[0] = fx["<<i<<"]; fy[0] = fy["<<i<<"]; \n" - " }\n"; - - out << - " float fs = 0.5*( cu.s0 - cd.s0 ); \n" - " float fss = cu.s0 + cd.s0 - cc.s0 - cc.s0;\n" - " float fxs = 0.25 * (v4[0].y + v5[0].x - v4[0].x - v5[0].y);\n" - " float fys = 0.25 * (v4[0].w + v5[0].z - v4[0].z - v5[0].w);\n" - " float4 A0, A1, A2 ; \n" - " A0 = (float4)(fxx[0], fxy[0], fxs, -fx[0]); \n" - " A1 = (float4)(fxy[0], fyy[0], fys, -fy[0]); \n" - " A2 = (float4)(fxs, fys, fss, -fs); \n" - " float4 x3 = fabs((float4)(fxx[0], fxy[0], fxs, 0)); \n" - " float maxa = max(max(x3.x, x3.y), x3.z); \n" - " if(maxa >= 1e-10 ) \n" - " { \n" - " if(x3.y ==maxa ) \n" - " { \n" - " float4 TEMP = A1; A1 = A0; A0 = TEMP; \n" - " }else if( x3.z == maxa ) \n" - " { \n" - " float4 TEMP = A2; A2 = A0; A0 = TEMP; \n" - " } \n" - " A0 /= A0.x; \n" - " A1 -= A1.x * A0; \n" - " A2 -= A2.x * A0; \n" - " float2 x2 = fabs((float2)(A1.y, A2.y)); \n" - " if( x2.y > x2.x ) \n" - " { \n" - " float4 TEMP = A2.yzwx; \n" - " A2.yzw = A1.yzw; \n" - " A1.yzw = TEMP.xyz; \n" - " x2.x = x2.y; \n" - " } \n" - " if(x2.x >= 1e-10) { \n" - " A1.yzw /= A1.y; \n" - " A2.yzw -= A2.y * A1.yzw; \n" - " if(fabs(A2.z) >= 1e-10) {\n" - " offset.z = A2.w /A2.z; \n" - " offset.y = A1.w - offset.z*A1.z; \n" - " offset.x = A0.w - offset.z*A0.z - offset.y*A0.y; \n" - " if(fabs(cc.s0 + 0.5*dot((float4)(fx[0], fy[0], fs, 0), offset ))<=THRESHOLD1\n" - " || any( isgreater(fabs(offset), (float4)(1.0)))) key4 = (float4)(0.0);\n" - " }\n" - " }\n" - " }\n" - <<"\n" - " float keyv = dot(key4, (float4)(1.0, 2.0, 3.0, 4.0));\n" - " result = (float4)(keyv, offset.xyz);\n" - " }}}}\n" - " write_imagef(texK, coord0, result);\n " - "}\n" <<'\0'; - } - else - { - out << "\n" - " float keyv = dot(key4, (float4)(1.0, 2.0, 3.0, 4.0));\n" - " result = (float4)(keyv, 0, 0, 0);\n" - " }}}}\n" - " write_imagef(texK, coord0, result);\n " - "}\n" <<'\0'; - } - - s_keypoint = new ProgramCL("keypoint", buffer, _context, _device); -} - -void ProgramBagCL::LoadDisplayShaders() -{ - //"uniform sampler2DRect tex; void main(){\n" - //"vec4 pc = texture2DRect(tex, gl_TexCoord[0].xy); bvec2 ff = lessThan(fract(gl_TexCoord[0].xy), vec2(0.5));\n" - //"float v = ff.y?(ff.x? pc.r : pc.g):(ff.x?pc.b:pc.a); gl_FragColor = vec4(vec3(v), 1.0);}"); - s_unpack = new ProgramCL("main", - "__kernel void main(__read_only image2d_t input, __write_only image2d_t output,\n" - " int width, int height) {\n" - "sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;\n" - "int x = get_global_id(0), y = get_global_id(1); \n" - "if(x >= width || y >= height) return;\n" - "int xx = x / 2, yy = y / 2; \n" - "float4 vv = read_imagef(input, sampler, (int2) (xx, yy)); \n" - "float v1 = (x & 1 ? vv.w : vv.z); \n" - "float v2 = (x & 1 ? vv.y : vv.x);\n" - "float v = y & 1 ? v1 : v2;\n" - "float4 result = (float4) (v, v, v, 1);" - "write_imagef(output, (int2) (x, y), result); }" , _context, _device); - - s_unpack_dog = new ProgramCL("main", - "__kernel void main(__read_only image2d_t input, __write_only image2d_t output,\n" - " int width, int height) {\n" - "sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;\n" - "int x = get_global_id(0), y = get_global_id(1); \n" - "if(x >= width || y >= height) return;\n" - "int xx = x / 2, yy = y / 2; \n" - "float4 vv = read_imagef(input, sampler, (int2) (xx, yy)); \n" - "float v1 = (x & 1 ? vv.w : vv.z); \n" - "float v2 = (x & 1 ? vv.y : vv.x);\n" - "float v0 = y & 1 ? v1 : v2;\n" - "float v = 0.5 + 20.0 * v0;\n " - "float4 result = (float4) (v, v, v, 1);" - "write_imagef(output, (int2) (x, y), result); }" , _context, _device); - - s_unpack_grd = new ProgramCL("main", - "__kernel void main(__read_only image2d_t input, __write_only image2d_t output,\n" - " int width, int height) {\n" - "sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;\n" - "int x = get_global_id(0), y = get_global_id(1); \n" - "if(x >= width || y >= height) return;\n" - "int xx = x / 2, yy = y / 2; \n" - "float4 vv = read_imagef(input, sampler, (int2) (xx, yy)); \n" - "float v1 = (x & 1 ? vv.w : vv.z); \n" - "float v2 = (x & 1 ? vv.y : vv.x);\n" - "float v0 = y & 1 ? v1 : v2;\n" - "float v = 5.0 * v0;\n " - "float4 result = (float4) (v, v, v, 1);" - "write_imagef(output, (int2) (x, y), result); }" , _context, _device); - - s_unpack_key = new ProgramCL("main", - "__kernel void main(__read_only image2d_t dog,\n" - " __read_only image2d_t key,\n" - " __write_only image2d_t output,\n" - " int width, int height) {\n" - "sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;\n" - "int x = get_global_id(0), y = get_global_id(1); \n" - "if(x >= width || y >= height) return;\n" - "int xx = x / 2, yy = y / 2; \n" - "float4 kk = read_imagef(key, sampler, (int2) (xx, yy));\n" - "int4 cc = isequal(fabs(kk.xxxx), (float4)(1.0, 2.0, 3.0, 4.0));\n" - "int k1 = (x & 1 ? cc.w : cc.z); \n" - "int k2 = (x & 1 ? cc.y : cc.x);\n" - "int k0 = y & 1 ? k1 : k2;\n" - "float4 result;\n" - "if(k0 != 0){\n" - " //result = kk.x > 0 ? ((float4)(1.0, 0, 0, 1.0)) : ((float4) (0.0, 1.0, 0.0, 1.0)); \n" - " result = kk.x < 0 ? ((float4)(0, 1.0, 0, 1.0)) : ((float4) (1.0, 0.0, 0.0, 1.0)); \n" - "}else{" - "float4 vv = read_imagef(dog, sampler, (int2) (xx, yy));\n" - "float v1 = (x & 1 ? vv.w : vv.z); \n" - "float v2 = (x & 1 ? vv.y : vv.x);\n" - "float v0 = y & 1 ? v1 : v2;\n" - "float v = 0.5 + 20.0 * v0;\n " - "result = (float4) (v, v, v, 1);" - "}\n" - "write_imagef(output, (int2) (x, y), result); }" , _context, _device); -} - - -void ProgramBagCL::SetMarginCopyParam(int xmax, int ymax) -{ - -} - -void ProgramBagCL::SetGradPassParam(int texP) -{ - -} - -void ProgramBagCL::SetGenListEndParam(int ktex) -{ - -} - -void ProgramBagCL::SetDogTexParam(int texU, int texD) -{ - -} - -void ProgramBagCL::SetGenListInitParam(int w, int h) -{ - float bbox[4] = {(w -1.0f) * 0.5f +0.25f, (w-1.0f) * 0.5f - 0.25f, (h - 1.0f) * 0.5f + 0.25f, (h-1.0f) * 0.5f - 0.25f}; - -} - - -void ProgramBagCL::SetGenListStartParam(float width, int tex0) -{ - -} - - - -void ProgramBagCL::SetGenListStepParam(int tex, int tex0) -{ - -} - -void ProgramBagCL::SetGenVBOParam(float width, float fwidth, float size) -{ - -} - -void ProgramBagCL::SetSimpleOrientationInput(int oTex, float sigma, float sigma_step) -{ - -} - - -void ProgramBagCL::SetFeatureOrientationParam(int gtex, int width, int height, float sigma, int otex, float step) -{ - - -} - -void ProgramBagCL::SetFeatureDescirptorParam(int gtex, int otex, float dwidth, float fwidth, float width, float height, float sigma) -{ - -} - - - -const char* ProgramBagCL::GetErrorString(cl_int error) -{ - static const char* errorString[] = { - "CL_SUCCESS", - "CL_DEVICE_NOT_FOUND", - "CL_DEVICE_NOT_AVAILABLE", - "CL_COMPILER_NOT_AVAILABLE", - "CL_MEM_OBJECT_ALLOCATION_FAILURE", - "CL_OUT_OF_RESOURCES", - "CL_OUT_OF_HOST_MEMORY", - "CL_PROFILING_INFO_NOT_AVAILABLE", - "CL_MEM_COPY_OVERLAP", - "CL_IMAGE_FORMAT_MISMATCH", - "CL_IMAGE_FORMAT_NOT_SUPPORTED", - "CL_BUILD_PROGRAM_FAILURE", - "CL_MAP_FAILURE", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "CL_INVALID_VALUE", - "CL_INVALID_DEVICE_TYPE", - "CL_INVALID_PLATFORM", - "CL_INVALID_DEVICE", - "CL_INVALID_CONTEXT", - "CL_INVALID_QUEUE_PROPERTIES", - "CL_INVALID_COMMAND_QUEUE", - "CL_INVALID_HOST_PTR", - "CL_INVALID_MEM_OBJECT", - "CL_INVALID_IMAGE_FORMAT_DESCRIPTOR", - "CL_INVALID_IMAGE_SIZE", - "CL_INVALID_SAMPLER", - "CL_INVALID_BINARY", - "CL_INVALID_BUILD_OPTIONS", - "CL_INVALID_PROGRAM", - "CL_INVALID_PROGRAM_EXECUTABLE", - "CL_INVALID_KERNEL_NAME", - "CL_INVALID_KERNEL_DEFINITION", - "CL_INVALID_KERNEL", - "CL_INVALID_ARG_INDEX", - "CL_INVALID_ARG_VALUE", - "CL_INVALID_ARG_SIZE", - "CL_INVALID_KERNEL_ARGS", - "CL_INVALID_WORK_DIMENSION", - "CL_INVALID_WORK_GROUP_SIZE", - "CL_INVALID_WORK_ITEM_SIZE", - "CL_INVALID_GLOBAL_OFFSET", - "CL_INVALID_EVENT_WAIT_LIST", - "CL_INVALID_EVENT", - "CL_INVALID_OPERATION", - "CL_INVALID_GL_OBJECT", - "CL_INVALID_BUFFER_SIZE", - "CL_INVALID_MIP_LEVEL", - "CL_INVALID_GLOBAL_WORK_SIZE", - }; - - const int errorCount = sizeof(errorString) / sizeof(errorString[0]); - - const int index = -error; - - return (index >= 0 && index < errorCount) ? errorString[index] : ""; -} - -bool ProgramBagCL::CheckErrorCL(cl_int error, const char* location) -{ - if(error == CL_SUCCESS) return true; - const char *errstr = GetErrorString(error); - if(errstr && errstr[0]) std::cerr << errstr; - else std::cerr << "Error " << error; - if(location) std::cerr << " at " << location; - std::cerr << "\n"; - exit(0); - return false; - -} - - -//////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////// - -void ProgramBagCLN::LoadFixedShaders() -{ - s_sampling = new ProgramCL("sampling", - "const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;\n" - "__kernel void sampling(__read_only image2d_t input, __write_only image2d_t output, " - " int width, int height) {\n" - "int2 coord = (int2)(get_global_id(0), get_global_id(1)); \n" - "if( coord.x >= width || coord.y >= height) return;\n" - "write_imagef(output, coord, read_imagef(input, sampler, coord << 1)); }" , _context, _device); - - s_sampling_k = new ProgramCL("sampling_k", - "const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;\n" - "__kernel void sampling_k(__read_only image2d_t input, __write_only image2d_t output, " - " int width, int height, int step) {\n" - "int x = get_global_id(0), y = get_global_id(1); \n" - "if( x >= width || y >= height) return;\n" - "int xa = x * step, ya = y *step; \n" - "float4 v1 = read_imagef(input, sampler, (int2) (xa, ya)); \n" - "write_imagef(output, (int2) (x, y), v1); }" , _context, _device); - - - s_sampling_u = new ProgramCL("sampling_u", - "const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_LINEAR;\n" - "__kernel void sampling_u(__read_only image2d_t input, \n" - " __write_only image2d_t output,\n" - " int width, int height, float step) {\n" - "int x = get_global_id(0), y = get_global_id(1); \n" - "if( x >= width || y >= height) return;\n" - "float xa = x * step, ya = y *step; \n" - "float v1 = read_imagef(input, sampler, (float2) (xa, ya)).x; \n" - "write_imagef(output, (int2) (x, y), (float4)(v1)); }" , _context, _device); - - s_dog_pass = new ProgramCL("dog_pass", - "const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;\n" - "__kernel void dog_pass(__read_only image2d_t tex, __read_only image2d_t texp,\n" - " __write_only image2d_t dog, int width, int height) {\n" - "int2 coord = (int2)(get_global_id(0), get_global_id(1)); \n" - "if( coord.x >= width || coord.y >= height) return;\n" - "float cc = read_imagef(tex , sampler, coord).x; \n" - "float cp = read_imagef(texp, sampler, coord).x;\n" - "write_imagef(dog, coord, (float4)(cc - cp)); }\n", _context, _device); - - s_grad_pass = new ProgramCL("grad_pass", - "const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;\n" - "__kernel void grad_pass(__read_only image2d_t tex, __read_only image2d_t texp,\n" - " __write_only image2d_t dog, int width, int height, \n" - " __write_only image2d_t grad, __write_only image2d_t rot) {\n" - "int x = get_global_id(0), y = get_global_id(1); \n" - "if( x >= width || y >= height) return;\n" - "int2 coord = (int2) (x, y);\n" - "float cl = read_imagef(tex, sampler, (int2)(x - 1, y)).x;\n" - "float cc = read_imagef(tex , sampler, coord).x; \n" - "float cr = read_imagef(tex, sampler, (int2)(x + 1, y)).x;\n" - "float cp = read_imagef(texp, sampler, coord).x;\n" - "write_imagef(dog, coord, (float4)(cc - cp)); \n" - "float cd = read_imagef(tex, sampler, (int2)(x, y - 1)).x;\n" - "float cu = read_imagef(tex, sampler, (int2)(x, y + 1)).x;\n" - "float dx = cr - cl, dy = cu - cd; \n" - "float gg = 0.5 * sqrt(dx*dx + dy * dy);\n" - "write_imagef(grad, coord, (float4)(gg));\n" - "float oo = atan2(dy, dx + FLT_MIN);\n" - "write_imagef(rot, coord, (float4)(oo));}\n", _context, _device); - - s_grad_pass2 = new ProgramCL("grad_pass2", - "#define BLOCK_DIMX 32\n" - "#define BLOCK_DIMY 14\n" - "#define BLOCK_SIZE (BLOCK_DIMX * BLOCK_DIMY)\n" - "__kernel void grad_pass2(__read_only image2d_t tex, __read_only image2d_t texp,\n" - " __write_only image2d_t dog, int width, int height,\n" - " __write_only image2d_t grd, __write_only image2d_t rot){\n"//, __local float* block) {\n" - "__local float block[BLOCK_SIZE]; \n" - "sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE |\n" - " CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;\n" - "int2 coord = (int2) ( get_global_id(0) - get_group_id(0) * 2 - 1, \n" - " get_global_id(1) - get_group_id(1) * 2 - 1); \n" - "int idx = mad24(get_local_id(1), BLOCK_DIMX, get_local_id(0));\n" - "float cc = read_imagef(tex, sampler, coord).x;\n" - "block[idx] = cc;\n" - "barrier(CLK_LOCAL_MEM_FENCE);\n" - "if( get_local_id(0) == 0 || get_local_id(0) == BLOCK_DIMX - 1) return;\n" - "if( get_local_id(1) == 0 || get_local_id(1) == BLOCK_DIMY - 1) return;\n" - "if( coord.x >= width) return; \n" - "if( coord.y >= height) return;\n" - "float cp = read_imagef(texp, sampler, coord).x;\n" - "float dx = block[idx + 1] - block[idx - 1];\n" - "float dy = block[idx + BLOCK_DIMX ] - block[idx - BLOCK_DIMX];\n" - "write_imagef(dog, coord, (float4)(cc - cp)); \n" - "write_imagef(grd, coord, (float4)(0.5 * sqrt(dx*dx + dy * dy)));\n" - "write_imagef(rot, coord, (float4)(atan2(dy, dx + FLT_MIN)));}\n", _context, _device); -} - -void ProgramBagCLN::LoadDisplayShaders() -{ - s_unpack = new ProgramCL("main", - "const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;\n" - "__kernel void main(__read_only image2d_t input, __write_only image2d_t output,\n" - " int width, int height) {\n" - "int x = get_global_id(0), y = get_global_id(1); \n" - "if(x >= width || y >= height) return;\n" - "float v = read_imagef(input, sampler, (int2) (x, y)).x; \n" - "float4 result = (float4) (v, v, v, 1);" - "write_imagef(output, (int2) (x, y), result); }" , _context, _device); - - s_unpack_grd = new ProgramCL("main", - "const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE |\n" - " CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;\n" - "__kernel void main(__read_only image2d_t input, __write_only image2d_t output,\n" - " int width, int height) {\n" - "int x = get_global_id(0), y = get_global_id(1); \n" - "if(x >= width || y >= height) return;\n" - "float v0 = read_imagef(input, sampler, (int2) (x, y)).x; \n" - "float v = 5.0 * v0; float4 result = (float4) (v, v, v, 1);" - "write_imagef(output, (int2) (x, y), result); }" , _context, _device); - - s_unpack_dog = new ProgramCL("main", - "const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;\n" - "__kernel void main(__read_only image2d_t input, __write_only image2d_t output,\n" - " int width, int height) {\n" - "int x = get_global_id(0), y = get_global_id(1); \n" - "if(x >= width || y >= height) return;\n" - "float v0 = read_imagef(input, sampler, (int2) (x, y)).x; \n" - "float v = 0.5 + 20.0 * v0; float4 result = (float4) (v, v, v, 1);" - "write_imagef(output, (int2) (x, y), result); }" , _context, _device); -} - -ProgramCL* ProgramBagCLN::CreateFilterH(float kernel[], int width) -{ - //////////////////////////// - char buffer[10240]; - ostrstream out(buffer, 10240); - out << "#define KERNEL_WIDTH " << width << "\n" - << "#define KERNEL_HALF_WIDTH " << (width / 2) << "\n" - "#define BLOCK_WIDTH 128\n" - "#define BLOCK_HEIGHT 1\n" - "#define CACHE_WIDTH (BLOCK_WIDTH + KERNEL_WIDTH - 1)\n" - "#define CACHE_WIDTH_ALIGNED ((CACHE_WIDTH + 15) / 16 * 16)\n" - "#define CACHE_COUNT (2 + (CACHE_WIDTH - 2) / BLOCK_WIDTH)\n" - "const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;\n" - "__kernel void filter_h(__read_only image2d_t input, \n" - " __write_only image2d_t output, int width_, int height_, \n" - " __constant float* weight) {\n" - "__local float data[CACHE_WIDTH]; \n" - "int x = get_global_id(0), y = get_global_id(1);\n" - "#pragma unroll\n" - "for(int j = 0; j < CACHE_COUNT; ++j)\n" - "{\n" - " if(get_local_id(0) + j * BLOCK_WIDTH < CACHE_WIDTH)\n" - " {\n" - " int fetch_index = min(x + j * BLOCK_WIDTH - KERNEL_HALF_WIDTH, width_);\n" - " data[get_local_id(0) + j * BLOCK_WIDTH] = read_imagef(input, sampler, (int2)(fetch_index, y)).x;\n" - " }\n" - "}\n" - "barrier(CLK_LOCAL_MEM_FENCE); \n" - "if( x > width_ || y > height_) return; \n" - "float result = 0; \n" - "#pragma unroll\n" - "for(int i = 0; i < KERNEL_WIDTH; ++i)\n" - "{\n" - " result += data[get_local_id(0) + i] * weight[i];\n" - "}\n" - << "write_imagef(output, (int2)(x, y), (float4)(result)); }\n" << '\0'; - return new ProgramCL("filter_h", buffer, _context, _device); -} - - - -ProgramCL* ProgramBagCLN::CreateFilterV(float kernel[], int width) -{ - //////////////////////////// - char buffer[10240]; - ostrstream out(buffer, 10240); - out << "#define KERNEL_WIDTH " << width << "\n" - << "#define KERNEL_HALF_WIDTH " << (width / 2) << "\n" - "#define BLOCK_WIDTH 128\n" - "#define CACHE_WIDTH (BLOCK_WIDTH + KERNEL_WIDTH - 1)\n" - "#define CACHE_WIDTH_ALIGNED ((CACHE_WIDTH + 15) / 16 * 16)\n" - "#define CACHE_COUNT (2 + (CACHE_WIDTH - 2) / BLOCK_WIDTH)\n" - "const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | \n" - " CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;\n" - "__kernel void filter_v(__read_only image2d_t input, \n" - " __write_only image2d_t output, int width_, int height_, \n" - " __constant float* weight) {\n" - "__local float data[CACHE_WIDTH]; \n" - "int x = get_global_id(0), y = get_global_id(1);\n" - "#pragma unroll\n" - "for(int j = 0; j < CACHE_COUNT; ++j)\n" - "{\n" - " if(get_local_id(1) + j * BLOCK_WIDTH < CACHE_WIDTH)\n" - " {\n" - " int fetch_index = min(y + j * BLOCK_WIDTH - KERNEL_HALF_WIDTH, height_);\n" - " data[get_local_id(1) + j * BLOCK_WIDTH ] = read_imagef(input, sampler, (int2)(x, fetch_index)).x;\n" - " }\n" - "}\n" - "barrier(CLK_LOCAL_MEM_FENCE); \n" - "if( x > width_ || y > height_) return; \n" - "float result = 0; \n" - "#pragma unroll\n" - "for(int i = 0; i < KERNEL_WIDTH; ++i)\n" - "{\n" - " result += data[get_local_id(1) + i] * weight[i];\n" - "}\n" - << "write_imagef(output, (int2)(x, y), (float4)(result)); }\n" << '\0'; - - return new ProgramCL("filter_v", buffer, _context, _device); -} - -FilterCL* ProgramBagCLN::CreateFilter(float kernel[], int width) -{ - FilterCL * filter = new FilterCL; - filter->s_shader_h = CreateFilterH(kernel, width); - filter->s_shader_v = CreateFilterV(kernel, width); - filter->_weight = new CLTexImage(_context, _queue); - filter->_weight->InitBufferTex(width, 1, 1); - filter->_weight->CopyFromHost(kernel); - filter->_size = width; - return filter; -} - - -void ProgramBagCLN::FilterImage(FilterCL* filter, CLTexImage *dst, CLTexImage *src, CLTexImage*tmp) -{ - cl_kernel kernelh = filter->s_shader_h->_kernel; - cl_kernel kernelv = filter->s_shader_v->_kernel; - ////////////////////////////////////////////////////////////////// - - cl_int status, w = dst->GetImgWidth(), h = dst->GetImgHeight(); - cl_mem weight = (cl_mem) filter->_weight->_clData; - cl_int w_ = w - 1, h_ = h - 1; - - - clSetKernelArg(kernelh, 0, sizeof(cl_mem), &src->_clData); - clSetKernelArg(kernelh, 1, sizeof(cl_mem), &tmp->_clData); - clSetKernelArg(kernelh, 2, sizeof(cl_int), &w_); - clSetKernelArg(kernelh, 3, sizeof(cl_int), &h_); - clSetKernelArg(kernelh, 4, sizeof(cl_mem), &weight); - - size_t dim00 = 128, dim01 = 1; - size_t gsz1[2] = {(w + dim00 - 1) / dim00 * dim00, (h + dim01 - 1) / dim01 * dim01}, lsz1[2] = {dim00, dim01}; - status = clEnqueueNDRangeKernel(_queue, kernelh, 2, NULL, gsz1, lsz1, 0, NULL, NULL); - CheckErrorCL(status, "ProgramBagCLN::FilterImageH"); - if(status != CL_SUCCESS) return; - - - clSetKernelArg(kernelv, 0, sizeof(cl_mem), &tmp->_clData); - clSetKernelArg(kernelv, 1, sizeof(cl_mem), &dst->_clData); - clSetKernelArg(kernelv, 2, sizeof(cl_int), &w_); - clSetKernelArg(kernelv, 3, sizeof(cl_int), &h_); - clSetKernelArg(kernelv, 4, sizeof(cl_mem), &weight); - - size_t dim10 = 1, dim11 = 128; - size_t gsz2[2] = {(w + dim10 - 1) / dim10 * dim10, (h + dim11 - 1) / dim11 * dim11}, lsz2[2] = {dim10, dim11}; - status = clEnqueueNDRangeKernel(_queue, kernelv, 2, NULL, gsz2, lsz2, 0, NULL, NULL); - CheckErrorCL(status, "ProgramBagCLN::FilterImageV"); - //clReleaseEvent(event); -} - -void ProgramBagCLN::SampleImageD(CLTexImage *dst, CLTexImage *src, int log_scale) -{ - cl_kernel kernel; - cl_int w = dst->GetImgWidth(), h = dst->GetImgHeight(); - - cl_int fullstep = (1 << log_scale); - kernel = log_scale == 1? s_sampling->_kernel : s_sampling_k->_kernel; - clSetKernelArg(kernel, 0, sizeof(cl_mem), &(src->_clData)); - clSetKernelArg(kernel, 1, sizeof(cl_mem), &(dst->_clData)); - clSetKernelArg(kernel, 2, sizeof(cl_int), &(w)); - clSetKernelArg(kernel, 3, sizeof(cl_int), &(h)); - if(log_scale > 1) clSetKernelArg(kernel, 4, sizeof(cl_int), &(fullstep)); - - size_t dim0 = 128, dim1 = 1; - //while( w * h / dim0 / dim1 < 8 && dim1 > 1) dim1 /= 2; - size_t gsz[2] = {(w + dim0 - 1) / dim0 * dim0, (h + dim1 - 1) / dim1 * dim1}, lsz[2] = {dim0, dim1}; - cl_int status = clEnqueueNDRangeKernel(_queue, kernel, 2, NULL, gsz, lsz, 0, NULL, NULL); - CheckErrorCL(status, "ProgramBagCLN::SampleImageD"); -} - - -#endif - diff --git a/3rdparty/SiftGPU/src/SiftGPU/ProgramCL.h b/3rdparty/SiftGPU/src/SiftGPU/ProgramCL.h deleted file mode 100644 index e134992b..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/ProgramCL.h +++ /dev/null @@ -1,164 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: ProgramCL.h -// Author: Changchang Wu -// Description : interface for the ProgramCL classes. -// ProgramCL: Cg programs -// ShaderBagCG: All Cg shaders for Sift in a bag -// FilterCL: Cg Gaussian Filters -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - - -#if defined(CL_SIFTGPU_ENABLED) - -#ifndef _PROGRAM_CL_H -#define _PROGRAM_CL_H - -#include "ProgramGPU.h" - -class ProgramCL: public ProgramGPU -{ - cl_program _program; - cl_kernel _kernel; - int _valid; -public: - int IsValidProgram(){return _program && _valid;} - ProgramCL(const char* name, const char * code, cl_context contex, cl_device_id device); - ProgramCL(); - void PrintBuildLog(cl_device_id device, int all); - virtual ~ProgramCL(); - virtual int UseProgram(){return 1;} - virtual void * GetProgramID() {return _kernel;} - friend class ProgramBagCL; - friend class ProgramBagCLN; -}; - -class CLTexImage; -class FilterCL -{ -public: - ProgramCL* s_shader_h; - ProgramCL* s_shader_v; - int _size; - int _id; - CLTexImage * _weight; -public: - FilterCL() : s_shader_h(NULL), s_shader_v(NULL), _size(0), _id(0), _weight(NULL) {} - ~FilterCL() {if(s_shader_h) delete s_shader_h; if(s_shader_v) delete s_shader_v; if(_weight) delete _weight; } -}; - -class SiftParam; - -class ProgramBagCL -{ -protected: - cl_platform_id _platform; - cl_device_id _device; - cl_context _context; - cl_command_queue _queue; -protected: - ProgramCL * s_gray; - ProgramCL * s_sampling; - ProgramCL * s_sampling_k; - ProgramCL * s_sampling_u; - ProgramCL * s_zero_pass; - ProgramCL * s_packup; - ProgramCL * s_unpack; - ProgramCL * s_unpack_dog; - ProgramCL * s_unpack_grd; - ProgramCL * s_unpack_key; - ProgramCL * s_dog_pass; - ProgramCL * s_grad_pass; - ProgramCL * s_grad_pass2; - ProgramCL * s_gray_pack; - ProgramCL * s_keypoint; -public: - FilterCL * f_gaussian_skip0; - vector<FilterCL*> f_gaussian_skip0_v; - FilterCL * f_gaussian_skip1; - FilterCL ** f_gaussian_step; - int _gaussian_step_num; -public: - ProgramBagCL(); - bool InitializeContext(); - virtual ~ProgramBagCL(); - void FinishCL(); - cl_context GetContextCL() {return _context;} - cl_command_queue GetCommandQueue() {return _queue;} - static const char* GetErrorString(cl_int error); - static bool CheckErrorCL(cl_int error, const char* location = NULL); -public: - FilterCL * CreateGaussianFilter(float sigma); - void CreateGaussianFilters(SiftParam&param); - void SelectInitialSmoothingFilter(int octave_min, SiftParam&param); - void FilterInitialImage(CLTexImage* tex, CLTexImage* buf); - void FilterSampledImage(CLTexImage* tex, CLTexImage* buf); - void UnpackImage(CLTexImage*src, CLTexImage* dst); - void UnpackImageDOG(CLTexImage*src, CLTexImage* dst); - void UnpackImageGRD(CLTexImage*src, CLTexImage* dst); - void UnpackImageKEY(CLTexImage*src, CLTexImage* dog, CLTexImage* dst); - void ComputeDOG(CLTexImage*tex, CLTexImage* texp, CLTexImage* dog, CLTexImage* grad, CLTexImage* rot); - void ComputeKEY(CLTexImage*dog, CLTexImage* key, float Tdog, float Tedge); -public: - virtual void SampleImageU(CLTexImage *dst, CLTexImage *src, int log_scale); - virtual void SampleImageD(CLTexImage *dst, CLTexImage *src, int log_scale = 1); - virtual void FilterImage(FilterCL* filter, CLTexImage *dst, CLTexImage *src, CLTexImage*tmp); - virtual ProgramCL* CreateFilterH(float kernel[], int width); - virtual ProgramCL* CreateFilterV(float kernel[], int width); - virtual FilterCL* CreateFilter(float kernel[], int width); -public: - virtual void InitProgramBag(SiftParam&param); - virtual void LoadDescriptorShader(); - virtual void LoadDescriptorShaderF2(); - virtual void LoadOrientationShader(); - virtual void LoadGenListShader(int ndoglev, int nlev); - virtual void UnloadProgram() ; - virtual void LoadKeypointShader(); - virtual void LoadFixedShaders(); - virtual void LoadDisplayShaders(); - virtual void LoadDynamicShaders(SiftParam& param); -public: - //parameters - virtual void SetGradPassParam(int texP); - virtual void SetGenListEndParam(int ktex); - virtual void SetGenListStartParam(float width, int tex0); - virtual void SetGenListInitParam(int w, int h); - virtual void SetMarginCopyParam(int xmax, int ymax); - virtual void SetDogTexParam(int texU, int texD); - virtual void SetGenListStepParam(int tex, int tex0); - virtual void SetGenVBOParam( float width, float fwidth, float size); - virtual void SetFeatureDescirptorParam(int gtex, int otex, float dwidth, float fwidth, float width, float height, float sigma); - virtual void SetFeatureOrientationParam(int gtex, int width, int height, float sigma, int stex, float step); - virtual void SetSimpleOrientationInput(int oTex, float sigma, float sigma_step); - -}; - -class CLTexImage ; -class ProgramBagCLN: public ProgramBagCL -{ -public: - virtual void SampleImageD(CLTexImage *dst, CLTexImage *src, int log_scale = 1); - virtual FilterCL* CreateFilter(float kernel[], int width); - virtual ProgramCL* CreateFilterH(float kernel[], int width); - virtual ProgramCL* CreateFilterV(float kernel[], int width); - virtual void FilterImage(FilterCL* filter, CLTexImage *dst, CLTexImage *src, CLTexImage*tmp); - virtual void LoadFixedShaders(); - virtual void LoadDisplayShaders(); -}; -#endif -#endif - diff --git a/3rdparty/SiftGPU/src/SiftGPU/ProgramCU.cu b/3rdparty/SiftGPU/src/SiftGPU/ProgramCU.cu deleted file mode 100644 index 257dda08..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/ProgramCU.cu +++ /dev/null @@ -1,1795 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: ProgramCU.cu -// Author: Changchang Wu -// Description : implementation of ProgramCU and all CUDA kernels -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - -#if defined(CUDA_SIFTGPU_ENABLED) - -#include "GL/glew.h" -#include "stdio.h" - -#include "CuTexImage.h" -#include "ProgramCU.h" -#include "GlobalUtil.h" - -//---------------------------------------------------------------- -//Begin SiftGPU setting section. -////////////////////////////////////////////////////////// -#define IMUL(X,Y) __mul24(X,Y) -//#define FDIV(X,Y) ((X)/(Y)) -#define FDIV(X,Y) __fdividef(X,Y) - -///////////////////////////////////////////////////////// -//filter kernel width range (don't change this) -#define KERNEL_MAX_WIDTH 33 -#define KERNEL_MIN_WIDTH 5 - -////////////////////////////////////////////////////////// -//horizontal filter block size (32, 64, 128, 256, 512) -#define FILTERH_TILE_WIDTH 128 -//thread block for vertical filter. FILTERV_BLOCK_WIDTH can be (4, 8 or 16) -#define FILTERV_BLOCK_WIDTH 16 -#define FILTERV_BLOCK_HEIGHT 32 -//The corresponding image patch for a thread block -#define FILTERV_PIXEL_PER_THREAD 4 -#define FILTERV_TILE_WIDTH FILTERV_BLOCK_WIDTH -#define FILTERV_TILE_HEIGHT (FILTERV_PIXEL_PER_THREAD * FILTERV_BLOCK_HEIGHT) - - -////////////////////////////////////////////////////////// -//thread block size for computing Difference of Gaussian -#define DOG_BLOCK_LOG_DIMX 7 -#define DOG_BLOCK_LOG_DIMY 0 -#define DOG_BLOCK_DIMX (1 << DOG_BLOCK_LOG_DIMX) -#define DOG_BLOCK_DIMY (1 << DOG_BLOCK_LOG_DIMY) - -////////////////////////////////////////////////////////// -//thread block size for keypoint detection -#define KEY_BLOCK_LOG_DIMX 3 -#define KEY_BLOCK_LOG_DIMY 3 -#define KEY_BLOCK_DIMX (1<<KEY_BLOCK_LOG_DIMX) -#define KEY_BLOCK_DIMY (1<<KEY_BLOCK_LOG_DIMY) -//#define KEY_OFFSET_ONE -//make KEY_BLOCK_LOG_DIMX 4 will make the write coalesced.. -//but it seems uncoalesced writes don't affect the speed - -////////////////////////////////////////////////////////// -//thread block size for initializing list generation (64, 128, 256, 512 ...) -#define HIST_INIT_WIDTH 128 -//thread block size for generating feature list (32, 64, 128, 256, 512, ...) -#define LISTGEN_BLOCK_DIM 128 - - -///////////////////////////////////////////////////////// -//how many keypoint orientations to compute in a block -#define ORIENTATION_COMPUTE_PER_BLOCK 64 -//how many keypoint descriptor to compute in a block (2, 4, 8, 16, 32) -#define DESCRIPTOR_COMPUTE_PER_BLOCK 4 -#define DESCRIPTOR_COMPUTE_BLOCK_SIZE (16 * DESCRIPTOR_COMPUTE_PER_BLOCK) -//how many keypoint descriptor to normalized in a block (32, ...) -#define DESCRIPTOR_NORMALIZ_PER_BLOCK 32 - - - -/////////////////////////////////////////// -//Thread block size for visualization -//(This doesn't affect the speed of computation) -#define BLOCK_LOG_DIM 4 -#define BLOCK_DIM (1 << BLOCK_LOG_DIM) - -//End SiftGPU setting section. -//---------------------------------------------------------------- - - -__device__ __constant__ float d_kernel[KERNEL_MAX_WIDTH]; -texture<float, 1, cudaReadModeElementType> texData; -texture<unsigned char, 1, cudaReadModeNormalizedFloat> texDataB; -texture<float2, 2, cudaReadModeElementType> texDataF2; -texture<float4, 1, cudaReadModeElementType> texDataF4; -texture<int4, 1, cudaReadModeElementType> texDataI4; -texture<int4, 1, cudaReadModeElementType> texDataList; - -//template<int i> __device__ float Conv(float *data) { return Conv<i-1>(data) + data[i]*d_kernel[i];} -//template<> __device__ float Conv<0>(float *data) { return data[0] * d_kernel[0]; } - - -////////////////////////////////////////////////////////////// -template<int FW> __global__ void FilterH( float* d_result, int width) -{ - - const int HALF_WIDTH = FW >> 1; - const int CACHE_WIDTH = FILTERH_TILE_WIDTH + FW -1; - const int CACHE_COUNT = 2 + (CACHE_WIDTH - 2)/ FILTERH_TILE_WIDTH; - __shared__ float data[CACHE_WIDTH]; - const int bcol = IMUL(blockIdx.x, FILTERH_TILE_WIDTH); - const int col = bcol + threadIdx.x; - const int index_min = IMUL(blockIdx.y, width); - const int index_max = index_min + width - 1; - int src_index = index_min + bcol - HALF_WIDTH + threadIdx.x; - int cache_index = threadIdx.x; - float value = 0; -#pragma unroll - for(int j = 0; j < CACHE_COUNT; ++j) - { - if(cache_index < CACHE_WIDTH) - { - int fetch_index = src_index < index_min? index_min : (src_index > index_max ? index_max : src_index); - data[cache_index] = tex1Dfetch(texData,fetch_index); - src_index += FILTERH_TILE_WIDTH; - cache_index += FILTERH_TILE_WIDTH; - } - } - __syncthreads(); - if(col >= width) return; -#pragma unroll - for(int i = 0; i < FW; ++i) - { - value += (data[threadIdx.x + i]* d_kernel[i]); - } -// value = Conv<FW-1>(data + threadIdx.x); - d_result[index_min + col] = value; -} - - - -//////////////////////////////////////////////////////////////////// -template<int FW> __global__ void FilterV(float* d_result, int width, int height) -{ - const int HALF_WIDTH = FW >> 1; - const int CACHE_WIDTH = FW + FILTERV_TILE_HEIGHT - 1; - const int TEMP = CACHE_WIDTH & 0xf; -//add some extra space to avoid bank conflict -#if FILTERV_TILE_WIDTH == 16 - //make the stride 16 * n +/- 1 - const int EXTRA = (TEMP == 1 || TEMP == 0) ? 1 - TEMP : 15 - TEMP; -#elif FILTERV_TILE_WIDTH == 8 - //make the stride 16 * n +/- 2 - const int EXTRA = (TEMP == 2 || TEMP == 1 || TEMP == 0) ? 2 - TEMP : (TEMP == 15? 3 : 14 - TEMP); -#elif FILTERV_TILE_WIDTH == 4 - //make the stride 16 * n +/- 4 - const int EXTRA = (TEMP >=0 && TEMP <=4) ? 4 - TEMP : (TEMP > 12? 20 - TEMP : 12 - TEMP); -#else -#error -#endif - const int CACHE_TRUE_WIDTH = CACHE_WIDTH + EXTRA; - const int CACHE_COUNT = (CACHE_WIDTH + FILTERV_BLOCK_HEIGHT - 1) / FILTERV_BLOCK_HEIGHT; - const int WRITE_COUNT = (FILTERV_TILE_HEIGHT + FILTERV_BLOCK_HEIGHT -1) / FILTERV_BLOCK_HEIGHT; - __shared__ float data[CACHE_TRUE_WIDTH * FILTERV_TILE_WIDTH]; - const int row_block_first = IMUL(blockIdx.y, FILTERV_TILE_HEIGHT); - const int col = IMUL(blockIdx.x, FILTERV_TILE_WIDTH) + threadIdx.x; - const int row_first = row_block_first - HALF_WIDTH; - const int data_index_max = IMUL(height - 1, width) + col; - const int cache_col_start = threadIdx.y; - const int cache_row_start = IMUL(threadIdx.x, CACHE_TRUE_WIDTH); - int cache_index = cache_col_start + cache_row_start; - int data_index = IMUL(row_first + cache_col_start, width) + col; - - if(col < width) - { -#pragma unroll - for(int i = 0; i < CACHE_COUNT; ++i) - { - if(cache_col_start < CACHE_WIDTH - i * FILTERV_BLOCK_HEIGHT) - { - int fetch_index = data_index < col ? col : (data_index > data_index_max? data_index_max : data_index); - data[cache_index + i * FILTERV_BLOCK_HEIGHT] = tex1Dfetch(texData,fetch_index); - data_index += IMUL(FILTERV_BLOCK_HEIGHT, width); - } - } - } - __syncthreads(); - - if(col >= width) return; - - int row = row_block_first + threadIdx.y; - int index_start = cache_row_start + threadIdx.y; -#pragma unroll - for(int i = 0; i < WRITE_COUNT; ++i, - row += FILTERV_BLOCK_HEIGHT, index_start += FILTERV_BLOCK_HEIGHT) - { - if(row < height) - { - int index_dest = IMUL(row, width) + col; - float value = 0; -#pragma unroll - for(int i = 0; i < FW; ++i) - { - value += (data[index_start + i] * d_kernel[i]); - } - d_result[index_dest] = value; - } - } -} - - -template<int LOG_SCALE> __global__ void UpsampleKernel(float* d_result, int width) -{ - const int SCALE = (1 << LOG_SCALE), SCALE_MASK = (SCALE - 1); - const float INV_SCALE = 1.0f / (float(SCALE)); - int col = IMUL(blockIdx.x, FILTERH_TILE_WIDTH) + threadIdx.x; - if(col >= width) return; - - int row = blockIdx.y >> LOG_SCALE; - int index = row * width + col; - int dst_row = blockIdx.y; - int dst_idx= (width * dst_row + col) * SCALE; - int helper = blockIdx.y & SCALE_MASK; - if (helper) - { - float v11 = tex1Dfetch(texData, index); - float v12 = tex1Dfetch(texData, index + 1); - index += width; - float v21 = tex1Dfetch(texData, index); - float v22 = tex1Dfetch(texData, index + 1); - float w1 = INV_SCALE * helper, w2 = 1.0 - w1; - float v1 = (v21 * w1 + w2 * v11); - float v2 = (v22 * w1 + w2 * v12); - d_result[dst_idx] = v1; -#pragma unroll - for(int i = 1; i < SCALE; ++i) - { - const float r2 = i * INV_SCALE; - const float r1 = 1.0f - r2; - d_result[dst_idx +i] = v1 * r1 + v2 * r2; - } - }else - { - float v1 = tex1Dfetch(texData, index); - float v2 = tex1Dfetch(texData, index + 1); - d_result[dst_idx] = v1; -#pragma unroll - for(int i = 1; i < SCALE; ++i) - { - const float r2 = i * INV_SCALE; - const float r1 = 1.0f - r2; - d_result[dst_idx +i] = v1 * r1 + v2 * r2; - } - } - -} - -//////////////////////////////////////////////////////////////////////////////////////// -void ProgramCU::SampleImageU(CuTexImage *dst, CuTexImage *src, int log_scale) -{ - int width = src->GetImgWidth(), height = src->GetImgHeight(); - src->BindTexture(texData); - dim3 grid((width + FILTERH_TILE_WIDTH - 1)/ FILTERH_TILE_WIDTH, height << log_scale); - dim3 block(FILTERH_TILE_WIDTH); - switch(log_scale) - { - case 1 : UpsampleKernel<1> <<< grid, block>>> ((float*) dst->_cuData, width); break; - case 2 : UpsampleKernel<2> <<< grid, block>>> ((float*) dst->_cuData, width); break; - case 3 : UpsampleKernel<3> <<< grid, block>>> ((float*) dst->_cuData, width); break; - default: break; - } -} - -template<int LOG_SCALE> __global__ void DownsampleKernel(float* d_result, int src_width, int dst_width) -{ - const int dst_col = IMUL(blockIdx.x, FILTERH_TILE_WIDTH) + threadIdx.x; - if(dst_col >= dst_width) return; - const int src_col = min((dst_col << LOG_SCALE), (src_width - 1)); - const int dst_row = blockIdx.y; - const int src_row = blockIdx.y << LOG_SCALE; - const int src_idx = IMUL(src_row, src_width) + src_col; - const int dst_idx = IMUL(dst_width, dst_row) + dst_col; - d_result[dst_idx] = tex1Dfetch(texData, src_idx); - -} - -__global__ void DownsampleKernel(float* d_result, int src_width, int dst_width, const int log_scale) -{ - const int dst_col = IMUL(blockIdx.x, FILTERH_TILE_WIDTH) + threadIdx.x; - if(dst_col >= dst_width) return; - const int src_col = min((dst_col << log_scale), (src_width - 1)); - const int dst_row = blockIdx.y; - const int src_row = blockIdx.y << log_scale; - const int src_idx = IMUL(src_row, src_width) + src_col; - const int dst_idx = IMUL(dst_width, dst_row) + dst_col; - d_result[dst_idx] = tex1Dfetch(texData, src_idx); - -} - -void ProgramCU::SampleImageD(CuTexImage *dst, CuTexImage *src, int log_scale) -{ - int src_width = src->GetImgWidth(), dst_width = dst->GetImgWidth() ; - - src->BindTexture(texData); - dim3 grid((dst_width + FILTERH_TILE_WIDTH - 1)/ FILTERH_TILE_WIDTH, dst->GetImgHeight()); - dim3 block(FILTERH_TILE_WIDTH); - switch(log_scale) - { - case 1 : DownsampleKernel<1> <<< grid, block>>> ((float*) dst->_cuData, src_width, dst_width); break; - case 2 : DownsampleKernel<2> <<< grid, block>>> ((float*) dst->_cuData, src_width, dst_width); break; - case 3 : DownsampleKernel<3> <<< grid, block>>> ((float*) dst->_cuData, src_width, dst_width); break; - default: DownsampleKernel <<< grid, block>>> ((float*) dst->_cuData, src_width, dst_width, log_scale); - } -} - -__global__ void ChannelReduce_Kernel(float* d_result) -{ - int index = IMUL(blockIdx.x, FILTERH_TILE_WIDTH) + threadIdx.x; - d_result[index] = tex1Dfetch(texData, index*4); -} - -__global__ void ChannelReduce_Convert_Kernel(float* d_result) -{ - int index = IMUL(blockIdx.x, FILTERH_TILE_WIDTH) + threadIdx.x; - float4 rgba = tex1Dfetch(texDataF4, index); - d_result[index] = 0.299f * rgba.x + 0.587f* rgba.y + 0.114f * rgba.z; -} - -void ProgramCU::ReduceToSingleChannel(CuTexImage* dst, CuTexImage* src, int convert_rgb) -{ - int width = src->GetImgWidth(), height = dst->GetImgHeight() ; - - dim3 grid((width * height + FILTERH_TILE_WIDTH - 1)/ FILTERH_TILE_WIDTH); - dim3 block(FILTERH_TILE_WIDTH); - if(convert_rgb) - { - src->BindTexture(texDataF4); - ChannelReduce_Convert_Kernel<<<grid, block>>>((float*)dst->_cuData); - }else - { - src->BindTexture(texData); - ChannelReduce_Kernel<<<grid, block>>>((float*)dst->_cuData); - } -} - -__global__ void ConvertByteToFloat_Kernel(float* d_result) -{ - int index = IMUL(blockIdx.x, FILTERH_TILE_WIDTH) + threadIdx.x; - d_result[index] = tex1Dfetch(texDataB, index); -} - -void ProgramCU::ConvertByteToFloat(CuTexImage*src, CuTexImage* dst) -{ - int width = src->GetImgWidth(), height = dst->GetImgHeight() ; - dim3 grid((width * height + FILTERH_TILE_WIDTH - 1)/ FILTERH_TILE_WIDTH); - dim3 block(FILTERH_TILE_WIDTH); - src->BindTexture(texDataB); - ConvertByteToFloat_Kernel<<<grid, block>>>((float*)dst->_cuData); -} - -void ProgramCU::CreateFilterKernel(float sigma, float* kernel, int& width) -{ - int i, sz = int( ceil( GlobalUtil::_FilterWidthFactor * sigma -0.5) ) ;// - width = 2*sz + 1; - - if(width > KERNEL_MAX_WIDTH) - { - //filter size truncation - sz = KERNEL_MAX_WIDTH >> 1; - width =KERNEL_MAX_WIDTH; - }else if(width < KERNEL_MIN_WIDTH) - { - sz = KERNEL_MIN_WIDTH >> 1; - width =KERNEL_MIN_WIDTH; - } - - float rv = 1.0f/(sigma*sigma), v, ksum =0; - - // pre-compute filter - for( i = -sz ; i <= sz ; ++i) - { - kernel[i+sz] = v = exp(-0.5f * i * i *rv) ; - ksum += v; - } - - //normalize the kernel - rv = 1.0f/ksum; - for(i = 0; i< width ;i++) kernel[i]*=rv; -} - - -template<int FW> void ProgramCU::FilterImage(CuTexImage *dst, CuTexImage *src, CuTexImage* buf) -{ - int width = src->GetImgWidth(), height = src->GetImgHeight(); - - //horizontal filtering - src->BindTexture(texData); - dim3 gridh((width + FILTERH_TILE_WIDTH - 1)/ FILTERH_TILE_WIDTH, height); - dim3 blockh(FILTERH_TILE_WIDTH); - FilterH<FW><<<gridh, blockh>>>((float*)buf->_cuData, width); - CheckErrorCUDA("FilterH"); - - ///vertical filtering - buf->BindTexture(texData); - dim3 gridv((width + FILTERV_TILE_WIDTH - 1)/ FILTERV_TILE_WIDTH, (height + FILTERV_TILE_HEIGHT - 1)/FILTERV_TILE_HEIGHT); - dim3 blockv(FILTERV_TILE_WIDTH, FILTERV_BLOCK_HEIGHT); - FilterV<FW><<<gridv, blockv>>>((float*)dst->_cuData, width, height); - CheckErrorCUDA("FilterV"); -} - -////////////////////////////////////////////////////////////////////// -// tested on 2048x1500 image, the time on pyramid construction is -// OpenGL version : 18ms -// CUDA version: 28 ms -void ProgramCU::FilterImage(CuTexImage *dst, CuTexImage *src, CuTexImage* buf, float sigma) -{ - float filter_kernel[KERNEL_MAX_WIDTH]; int width; - CreateFilterKernel(sigma, filter_kernel, width); - cudaMemcpyToSymbol(d_kernel, filter_kernel, width * sizeof(float), 0, cudaMemcpyHostToDevice); - - switch(width) - { - case 5: FilterImage< 5>(dst, src, buf); break; - case 7: FilterImage< 7>(dst, src, buf); break; - case 9: FilterImage< 9>(dst, src, buf); break; - case 11: FilterImage<11>(dst, src, buf); break; - case 13: FilterImage<13>(dst, src, buf); break; - case 15: FilterImage<15>(dst, src, buf); break; - case 17: FilterImage<17>(dst, src, buf); break; - case 19: FilterImage<19>(dst, src, buf); break; - case 21: FilterImage<21>(dst, src, buf); break; - case 23: FilterImage<23>(dst, src, buf); break; - case 25: FilterImage<25>(dst, src, buf); break; - case 27: FilterImage<27>(dst, src, buf); break; - case 29: FilterImage<29>(dst, src, buf); break; - case 31: FilterImage<31>(dst, src, buf); break; - case 33: FilterImage<33>(dst, src, buf); break; - default: break; - } - -} - - -texture<float, 1, cudaReadModeElementType> texC; -texture<float, 1, cudaReadModeElementType> texP; -texture<float, 1, cudaReadModeElementType> texN; - -void __global__ ComputeDOG_Kernel(float* d_dog, float2* d_got, int width, int height) -{ - int row = (blockIdx.y << DOG_BLOCK_LOG_DIMY) + threadIdx.y; - int col = (blockIdx.x << DOG_BLOCK_LOG_DIMX) + threadIdx.x; - if(col < width && row < height) - { - int index = IMUL(row, width) + col; - float vp = tex1Dfetch(texP, index); - float v = tex1Dfetch(texC, index); - d_dog[index] = v - vp; - float vxn = tex1Dfetch(texC, index + 1); - float vxp = tex1Dfetch(texC, index - 1); - float vyp = tex1Dfetch(texC, index - width); - float vyn = tex1Dfetch(texC, index + width); - float dx = vxn - vxp, dy = vyn - vyp; - float grd = 0.5f * sqrt(dx * dx + dy * dy); - float rot = (grd == 0.0f? 0.0f : atan2(dy, dx)); - d_got[index] = make_float2(grd, rot); - } -} - -void __global__ ComputeDOG_Kernel(float* d_dog, int width, int height) -{ - int row = (blockIdx.y << DOG_BLOCK_LOG_DIMY) + threadIdx.y; - int col = (blockIdx.x << DOG_BLOCK_LOG_DIMX) + threadIdx.x; - if(col < width && row < height) - { - int index = IMUL(row, width) + col; - float vp = tex1Dfetch(texP, index); - float v = tex1Dfetch(texC, index); - d_dog[index] = v - vp; - } -} - -void ProgramCU::ComputeDOG(CuTexImage* gus, CuTexImage* dog, CuTexImage* got) -{ - int width = gus->GetImgWidth(), height = gus->GetImgHeight(); - dim3 grid((width + DOG_BLOCK_DIMX - 1)/ DOG_BLOCK_DIMX, (height + DOG_BLOCK_DIMY - 1)/DOG_BLOCK_DIMY); - dim3 block(DOG_BLOCK_DIMX, DOG_BLOCK_DIMY); - gus->BindTexture(texC); - (gus -1)->BindTexture(texP); - if(got->_cuData) - ComputeDOG_Kernel<<<grid, block>>>((float*) dog->_cuData, (float2*) got->_cuData, width, height); - else - ComputeDOG_Kernel<<<grid, block>>>((float*) dog->_cuData, width, height); -} - - -#define READ_CMP_DOG_DATA(datai, tex, idx) \ - datai[0] = tex1Dfetch(tex, idx - 1);\ - datai[1] = tex1Dfetch(tex, idx);\ - datai[2] = tex1Dfetch(tex, idx + 1);\ - if(v > nmax)\ - {\ - nmax = max(nmax, datai[0]);\ - nmax = max(nmax, datai[1]);\ - nmax = max(nmax, datai[2]);\ - if(v < nmax) goto key_finish;\ - }else\ - {\ - nmin = min(nmin, datai[0]);\ - nmin = min(nmin, datai[1]);\ - nmin = min(nmin, datai[2]);\ - if(v > nmin) goto key_finish;\ - } - - -void __global__ ComputeKEY_Kernel(float4* d_key, int width, int colmax, int rowmax, - float dog_threshold0, float dog_threshold, float edge_threshold, int subpixel_localization) -{ - float data[3][3], v; - float datap[3][3], datan[3][3]; -#ifdef KEY_OFFSET_ONE - int row = (blockIdx.y << KEY_BLOCK_LOG_DIMY) + threadIdx.y + 1; - int col = (blockIdx.x << KEY_BLOCK_LOG_DIMX) + threadIdx.x + 1; -#else - int row = (blockIdx.y << KEY_BLOCK_LOG_DIMY) + threadIdx.y; - int col = (blockIdx.x << KEY_BLOCK_LOG_DIMX) + threadIdx.x; -#endif - int index = IMUL(row, width) + col; - int idx[3] ={index - width, index, index + width}; - int in_image =0; - float nmax, nmin, result = 0.0f; - float dx = 0, dy = 0, ds = 0; - bool offset_test_passed = true; -#ifdef KEY_OFFSET_ONE - if(row < rowmax && col < colmax) -#else - if(row > 0 && col > 0 && row < rowmax && col < colmax) -#endif - { - in_image = 1; - data[1][1] = v = tex1Dfetch(texC, idx[1]); - if(fabs(v) <= dog_threshold0) goto key_finish; - - data[1][0] = tex1Dfetch(texC, idx[1] - 1); - data[1][2] = tex1Dfetch(texC, idx[1] + 1); - nmax = max(data[1][0], data[1][2]); - nmin = min(data[1][0], data[1][2]); - - if(v <=nmax && v >= nmin) goto key_finish; - //if((v > nmax && v < 0 )|| (v < nmin && v > 0)) goto key_finish; - READ_CMP_DOG_DATA(data[0], texC, idx[0]); - READ_CMP_DOG_DATA(data[2], texC, idx[2]); - - //edge supression - float vx2 = v * 2.0f; - float fxx = data[1][0] + data[1][2] - vx2; - float fyy = data[0][1] + data[2][1] - vx2; - float fxy = 0.25f * (data[2][2] + data[0][0] - data[2][0] - data[0][2]); - float temp1 = fxx * fyy - fxy * fxy; - float temp2 = (fxx + fyy) * (fxx + fyy); - if(temp1 <=0 || temp2 > edge_threshold * temp1) goto key_finish; - - - //read the previous level - READ_CMP_DOG_DATA(datap[0], texP, idx[0]); - READ_CMP_DOG_DATA(datap[1], texP, idx[1]); - READ_CMP_DOG_DATA(datap[2], texP, idx[2]); - - - //read the next level - READ_CMP_DOG_DATA(datan[0], texN, idx[0]); - READ_CMP_DOG_DATA(datan[1], texN, idx[1]); - READ_CMP_DOG_DATA(datan[2], texN, idx[2]); - - if(subpixel_localization) - { - //subpixel localization - float fx = 0.5f * (data[1][2] - data[1][0]); - float fy = 0.5f * (data[2][1] - data[0][1]); - float fs = 0.5f * (datan[1][1] - datap[1][1]); - - float fss = (datan[1][1] + datap[1][1] - vx2); - float fxs = 0.25f* (datan[1][2] + datap[1][0] - datan[1][0] - datap[1][2]); - float fys = 0.25f* (datan[2][1] + datap[0][1] - datan[0][1] - datap[2][1]); - - //need to solve dx, dy, ds; - // |-fx| | fxx fxy fxs | |dx| - // |-fy| = | fxy fyy fys | * |dy| - // |-fs| | fxs fys fss | |ds| - float4 A0 = fxx > 0? make_float4(fxx, fxy, fxs, -fx) : make_float4(-fxx, -fxy, -fxs, fx); - float4 A1 = fxy > 0? make_float4(fxy, fyy, fys, -fy) : make_float4(-fxy, -fyy, -fys, fy); - float4 A2 = fxs > 0? make_float4(fxs, fys, fss, -fs) : make_float4(-fxs, -fys, -fss, fs); - float maxa = max(max(A0.x, A1.x), A2.x); - if(maxa >= 1e-10) - { - if(maxa == A1.x) - { - float4 TEMP = A1; A1 = A0; A0 = TEMP; - }else if(maxa == A2.x) - { - float4 TEMP = A2; A2 = A0; A0 = TEMP; - } - A0.y /= A0.x; A0.z /= A0.x; A0.w/= A0.x; - A1.y -= A1.x * A0.y; A1.z -= A1.x * A0.z; A1.w -= A1.x * A0.w; - A2.y -= A2.x * A0.y; A2.z -= A2.x * A0.z; A2.w -= A2.x * A0.w; - if(abs(A2.y) > abs(A1.y)) - { - float4 TEMP = A2; A2 = A1; A1 = TEMP; - } - if(abs(A1.y) >= 1e-10) - { - A1.z /= A1.y; A1.w /= A1.y; - A2.z -= A2.y * A1.z; A2.w -= A2.y * A1.w; - if(abs(A2.z) >= 1e-10) - { - ds = A2.w / A2.z; - dy = A1.w - ds * A1.z; - dx = A0.w - ds * A0.z - dy * A0.y; - - offset_test_passed = - fabs(data[1][1] + 0.5f * (dx * fx + dy * fy + ds * fs)) > dog_threshold - &&fabs(ds) < 1.0f && fabs(dx) < 1.0f && fabs(dy) < 1.0f; - } - } - } - } - if(offset_test_passed) result = v > nmax ? 1.0 : -1.0; - } -key_finish: - if(in_image) d_key[index] = make_float4(result, dx, dy, ds); -} - - -void ProgramCU::ComputeKEY(CuTexImage* dog, CuTexImage* key, float Tdog, float Tedge) -{ - int width = dog->GetImgWidth(), height = dog->GetImgHeight(); - float Tdog1 = (GlobalUtil::_SubpixelLocalization? 0.8f : 1.0f) * Tdog; - CuTexImage* dogp = dog - 1; - CuTexImage* dogn = dog + 1; -#ifdef KEY_OFFSET_ONE - dim3 grid((width - 1 + KEY_BLOCK_DIMX - 1)/ KEY_BLOCK_DIMX, (height - 1 + KEY_BLOCK_DIMY - 1)/KEY_BLOCK_DIMY); -#else - dim3 grid((width + KEY_BLOCK_DIMX - 1)/ KEY_BLOCK_DIMX, (height + KEY_BLOCK_DIMY - 1)/KEY_BLOCK_DIMY); -#endif - dim3 block(KEY_BLOCK_DIMX, KEY_BLOCK_DIMY); - dogp->BindTexture(texP); - dog ->BindTexture(texC); - dogn->BindTexture(texN); - Tedge = (Tedge+1)*(Tedge+1)/Tedge; - ComputeKEY_Kernel<<<grid, block>>>((float4*) key->_cuData, width, - width -1, height -1, Tdog1, Tdog, Tedge, GlobalUtil::_SubpixelLocalization); - -} - - - -void __global__ InitHist_Kernel(int4* hist, int ws, int wd, int height) -{ - int row = IMUL(blockIdx.y, blockDim.y) + threadIdx.y; - int col = IMUL(blockIdx.x, blockDim.x) + threadIdx.x; - if(row < height && col < wd) - { - int hidx = IMUL(row, wd) + col; - int scol = col << 2; - int sidx = IMUL(row, ws) + scol; - int v[4] = {0, 0, 0, 0}; - if(row > 0 && row < height -1) - { -#pragma unroll - for(int i = 0; i < 4 ; ++i, ++scol) - { - float4 temp = tex1Dfetch(texDataF4, sidx +i); - v[i] = (scol < ws -1 && scol > 0 && temp.x!=0) ? 1 : 0; - } - } - hist[hidx] = make_int4(v[0], v[1], v[2], v[3]); - - } -} - - - -void ProgramCU::InitHistogram(CuTexImage* key, CuTexImage* hist) -{ - int ws = key->GetImgWidth(), hs = key->GetImgHeight(); - int wd = hist->GetImgWidth(), hd = hist->GetImgHeight(); - dim3 grid((wd + HIST_INIT_WIDTH - 1)/ HIST_INIT_WIDTH, hd); - dim3 block(HIST_INIT_WIDTH, 1); - key->BindTexture(texDataF4); - InitHist_Kernel<<<grid, block>>>((int4*) hist->_cuData, ws, wd, hd); -} - - - -void __global__ ReduceHist_Kernel(int4* d_hist, int ws, int wd, int height) -{ - int row = IMUL(blockIdx.y, blockDim.y) + threadIdx.y; - int col = IMUL(blockIdx.x, blockDim.x) + threadIdx.x; - if(row < height && col < wd) - { - int hidx = IMUL(row, wd) + col; - int scol = col << 2; - int sidx = IMUL(row, ws) + scol; - int v[4] = {0, 0, 0, 0}; -#pragma unroll - for(int i = 0; i < 4 && scol < ws; ++i, ++scol) - { - int4 temp = tex1Dfetch(texDataI4, sidx + i); - v[i] = temp.x + temp.y + temp.z + temp.w; - } - d_hist[hidx] = make_int4(v[0], v[1], v[2], v[3]); - } -} - -void ProgramCU::ReduceHistogram(CuTexImage*hist1, CuTexImage* hist2) -{ - int ws = hist1->GetImgWidth(), hs = hist1->GetImgHeight(); - int wd = hist2->GetImgWidth(), hd = hist2->GetImgHeight(); - int temp = (int)floor(logf(float(wd * 2/ 3)) / logf(2.0f)); - const int wi = min(7, max(temp , 0)); - hist1->BindTexture(texDataI4); - - const int BW = 1 << wi, BH = 1 << (7 - wi); - dim3 grid((wd + BW - 1)/ BW, (hd + BH -1) / BH); - dim3 block(BW, BH); - ReduceHist_Kernel<<<grid, block>>>((int4*)hist2->_cuData, ws, wd, hd); -} - - -void __global__ ListGen_Kernel(int4* d_list, int width) -{ - int idx1 = IMUL(blockIdx.x, blockDim.x) + threadIdx.x; - int4 pos = tex1Dfetch(texDataList, idx1); - int idx2 = IMUL(pos.y, width) + pos.x; - int4 temp = tex1Dfetch(texDataI4, idx2); - int sum1 = temp.x + temp.y; - int sum2 = sum1 + temp.z; - pos.x <<= 2; - if(pos.z >= sum2) - { - pos.x += 3; - pos.z -= sum2; - }else if(pos.z >= sum1) - { - pos.x += 2; - pos.z -= sum1; - }else if(pos.z >= temp.x) - { - pos.x += 1; - pos.z -= temp.x; - } - d_list[idx1] = pos; -} - -//input list (x, y) (x, y) .... -void ProgramCU::GenerateList(CuTexImage* list, CuTexImage* hist) -{ - int len = list->GetImgWidth(); - list->BindTexture(texDataList); - hist->BindTexture(texDataI4); - dim3 grid((len + LISTGEN_BLOCK_DIM -1) /LISTGEN_BLOCK_DIM); - dim3 block(LISTGEN_BLOCK_DIM); - ListGen_Kernel<<<grid, block>>>((int4*) list->_cuData, hist->GetImgWidth()); -} - -void __global__ ComputeOrientation_Kernel(float4* d_list, - int list_len, - int width, int height, - float sigma, float sigma_step, - float gaussian_factor, float sample_factor, - int num_orientation, - int existing_keypoint, - int subpixel, - int keepsign) -{ - const float ten_degree_per_radius = 5.7295779513082320876798154814105; - const float radius_per_ten_degrees = 1.0 / 5.7295779513082320876798154814105; - int idx = IMUL(blockDim.x, blockIdx.x) + threadIdx.x; - if(idx >= list_len) return; - float4 key; - if(existing_keypoint) - { - key = tex1Dfetch(texDataF4, idx); - }else - { - int4 ikey = tex1Dfetch(texDataList, idx); - key.x = ikey.x + 0.5f; - key.y = ikey.y + 0.5f; - key.z = sigma; - if(subpixel || keepsign) - { - float4 offset = tex1Dfetch(texDataF4, IMUL(width, ikey.y) + ikey.x); - if(subpixel) - { - key.x += offset.y; - key.y += offset.z; - key.z *= pow(sigma_step, offset.w); - } - if(keepsign) key.z *= offset.x; - } - } - if(num_orientation == 0) - { - key.w = 0; - d_list[idx] = key; - return; - } - float vote[37]; - float gsigma = key.z * gaussian_factor; - float win = fabs(key.z) * sample_factor; - float dist_threshold = win * win + 0.5; - float factor = -0.5f / (gsigma * gsigma); - float xmin = max(1.5f, floor(key.x - win) + 0.5f); - float ymin = max(1.5f, floor(key.y - win) + 0.5f); - float xmax = min(width - 1.5f, floor(key.x + win) + 0.5f); - float ymax = min(height -1.5f, floor(key.y + win) + 0.5f); -#pragma unroll - for(int i = 0; i < 36; ++i) vote[i] = 0.0f; - for(float y = ymin; y <= ymax; y += 1.0f) - { - for(float x = xmin; x <= xmax; x += 1.0f) - { - float dx = x - key.x; - float dy = y - key.y; - float sq_dist = dx * dx + dy * dy; - if(sq_dist >= dist_threshold) continue; - float2 got = tex2D(texDataF2, x, y); - float weight = got.x * exp(sq_dist * factor); - float fidx = floor(got.y * ten_degree_per_radius); - int oidx = fidx; - if(oidx < 0) oidx += 36; - vote[oidx] += weight; - } - } - - //filter the vote - - const float one_third = 1.0 /3.0; -#pragma unroll - for(int i = 0; i < 6; ++i) - { - vote[36] = vote[0]; - float pre = vote[35]; -#pragma unroll - for(int j = 0; j < 36; ++j) - { - float temp = one_third * (pre + vote[j] + vote[j + 1]); - pre = vote[j]; vote[j] = temp; - } - } - - vote[36] = vote[0]; - if(num_orientation == 1 || existing_keypoint) - { - int index_max = 0; - float max_vote = vote[0]; -#pragma unroll - for(int i = 1; i < 36; ++i) - { - index_max = vote[i] > max_vote? i : index_max; - max_vote = max(max_vote, vote[i]); - } - float pre = vote[index_max == 0? 35 : index_max -1]; - float next = vote[index_max + 1]; - float weight = max_vote; - float off = 0.5f * FDIV(next - pre, weight + weight - next - pre); - key.w = radius_per_ten_degrees * (index_max + 0.5f + off); - d_list[idx] = key; - - }else - { - float max_vote = vote[0]; -#pragma unroll - for(int i = 1; i < 36; ++i) max_vote = max(max_vote, vote[i]); - - float vote_threshold = max_vote * 0.8f; - float pre = vote[35]; - float max_rot[2], max_vot[2] = {0, 0}; - int ocount = 0; -#pragma unroll - for(int i =0; i < 36; ++i) - { - float next = vote[i + 1]; - if(vote[i] > vote_threshold && vote[i] > pre && vote[i] > next) - { - float di = 0.5f * FDIV(next - pre, vote[i] + vote[i] - next - pre); - float rot = i + di + 0.5f; - float weight = vote[i]; - /// - if(weight > max_vot[1]) - { - if(weight > max_vot[0]) - { - max_vot[1] = max_vot[0]; - max_rot[1] = max_rot[0]; - max_vot[0] = weight; - max_rot[0] = rot; - } - else - { - max_vot[1] = weight; - max_rot[1] = rot; - } - ocount ++; - } - } - pre = vote[i]; - } - float fr1 = max_rot[0] / 36.0f; - if(fr1 < 0) fr1 += 1.0f; - unsigned short us1 = ocount == 0? 65535 : ((unsigned short )floor(fr1 * 65535.0f)); - unsigned short us2 = 65535; - if(ocount > 1) - { - float fr2 = max_rot[1] / 36.0f; - if(fr2 < 0) fr2 += 1.0f; - us2 = (unsigned short ) floor(fr2 * 65535.0f); - } - unsigned int uspack = (us2 << 16) | us1; - key.w = __int_as_float(uspack); - d_list[idx] = key; - } - -} - - - - -void ProgramCU::ComputeOrientation(CuTexImage* list, CuTexImage* got, CuTexImage*key, - float sigma, float sigma_step, int existing_keypoint) -{ - int len = list->GetImgWidth(); - if(len <= 0) return; - int width = got->GetImgWidth(), height = got->GetImgHeight(); - if(existing_keypoint) - { - list->BindTexture(texDataF4); - }else - { - list->BindTexture(texDataList); - if(GlobalUtil::_SubpixelLocalization) key->BindTexture(texDataF4); - } - got->BindTexture2D(texDataF2); - - const int block_width = len < ORIENTATION_COMPUTE_PER_BLOCK ? 16 : ORIENTATION_COMPUTE_PER_BLOCK; - dim3 grid((len + block_width -1) / block_width); - dim3 block(block_width); - - ComputeOrientation_Kernel<<<grid, block>>>((float4*) list->_cuData, - len, width, height, sigma, sigma_step, - GlobalUtil::_OrientationGaussianFactor, - GlobalUtil::_OrientationGaussianFactor * GlobalUtil::_OrientationWindowFactor, - GlobalUtil::_FixedOrientation? 0 : GlobalUtil::_MaxOrientation, - existing_keypoint, GlobalUtil::_SubpixelLocalization, GlobalUtil::_KeepExtremumSign); - - ProgramCU::CheckErrorCUDA("ComputeOrientation"); -} - -template <bool DYNAMIC_INDEXING> void __global__ ComputeDescriptor_Kernel(float4* d_des, int num, - int width, int height, float window_factor) -{ - const float rpi = 4.0/ 3.14159265358979323846; - int idx = IMUL(blockIdx.x, blockDim.x) + threadIdx.x; - int fidx = idx >> 4; - if(fidx >= num) return; - float4 key = tex1Dfetch(texDataF4, fidx); - int bidx = idx& 0xf, ix = bidx & 0x3, iy = bidx >> 2; - float spt = fabs(key.z * window_factor); - float s, c; __sincosf(key.w, &s, &c); - float anglef = key.w > 3.14159265358979323846? key.w - (2.0 * 3.14159265358979323846) : key.w ; - float cspt = c * spt, sspt = s * spt; - float crspt = c / spt, srspt = s / spt; - float2 offsetpt, pt; - float xmin, ymin, xmax, ymax, bsz; - offsetpt.x = ix - 1.5f; - offsetpt.y = iy - 1.5f; - pt.x = cspt * offsetpt.x - sspt * offsetpt.y + key.x; - pt.y = cspt * offsetpt.y + sspt * offsetpt.x + key.y; - bsz = fabs(cspt) + fabs(sspt); - xmin = max(1.5f, floor(pt.x - bsz) + 0.5f); - ymin = max(1.5f, floor(pt.y - bsz) + 0.5f); - xmax = min(width - 1.5f, floor(pt.x + bsz) + 0.5f); - ymax = min(height - 1.5f, floor(pt.y + bsz) + 0.5f); - float des[9]; -#pragma unroll - for(int i =0; i < 9; ++i) des[i] = 0.0f; - for(float y = ymin; y <= ymax; y += 1.0f) - { - for(float x = xmin; x <= xmax; x += 1.0f) - { - float dx = x - pt.x; - float dy = y - pt.y; - float nx = crspt * dx + srspt * dy; - float ny = crspt * dy - srspt * dx; - float nxn = fabs(nx); - float nyn = fabs(ny); - if(nxn < 1.0f && nyn < 1.0f) - { - float2 cc = tex2D(texDataF2, x, y); - float dnx = nx + offsetpt.x; - float dny = ny + offsetpt.y; - float ww = exp(-0.125f * (dnx * dnx + dny * dny)); - float wx = 1.0 - nxn; - float wy = 1.0 - nyn; - float weight = ww * wx * wy * cc.x; - float theta = (anglef - cc.y) * rpi; - if(theta < 0) theta += 8.0f; - float fo = floor(theta); - int fidx = fo; - float weight1 = fo + 1.0f - theta; - float weight2 = theta - fo; - if(DYNAMIC_INDEXING) - { - des[fidx] += (weight1 * weight); - des[fidx + 1] += (weight2 * weight); - //this dynamic indexing part might be slow - }else - { - #pragma unroll - for(int k = 0; k < 8; ++k) - { - if(k == fidx) - { - des[k] += (weight1 * weight); - des[k+1] += (weight2 * weight); - } - } - } - } - } - } - des[0] += des[8]; - - int didx = idx << 1; - d_des[didx] = make_float4(des[0], des[1], des[2], des[3]); - d_des[didx+1] = make_float4(des[4], des[5], des[6], des[7]); -} - - -template <bool DYNAMIC_INDEXING> void __global__ ComputeDescriptorRECT_Kernel(float4* d_des, int num, - int width, int height, float window_factor) -{ - const float rpi = 4.0/ 3.14159265358979323846; - int idx = IMUL(blockIdx.x, blockDim.x) + threadIdx.x; - int fidx = idx >> 4; - if(fidx >= num) return; - float4 key = tex1Dfetch(texDataF4, fidx); - int bidx = idx& 0xf, ix = bidx & 0x3, iy = bidx >> 2; - //float aspect_ratio = key.w / key.z; - //float aspect_sq = aspect_ratio * aspect_ratio; - float sptx = key.z * 0.25, spty = key.w * 0.25; - float xmin, ymin, xmax, ymax; float2 pt; - pt.x = sptx * (ix + 0.5f) + key.x; - pt.y = spty * (iy + 0.5f) + key.y; - xmin = max(1.5f, floor(pt.x - sptx) + 0.5f); - ymin = max(1.5f, floor(pt.y - spty) + 0.5f); - xmax = min(width - 1.5f, floor(pt.x + sptx) + 0.5f); - ymax = min(height - 1.5f, floor(pt.y + spty) + 0.5f); - float des[9]; -#pragma unroll - for(int i =0; i < 9; ++i) des[i] = 0.0f; - for(float y = ymin; y <= ymax; y += 1.0f) - { - for(float x = xmin; x <= xmax; x += 1.0f) - { - float nx = (x - pt.x) / sptx; - float ny = (y - pt.y) / spty; - float nxn = fabs(nx); - float nyn = fabs(ny); - if(nxn < 1.0f && nyn < 1.0f) - { - float2 cc = tex2D(texDataF2, x, y); - float wx = 1.0 - nxn; - float wy = 1.0 - nyn; - float weight = wx * wy * cc.x; - float theta = (- cc.y) * rpi; - if(theta < 0) theta += 8.0f; - float fo = floor(theta); - int fidx = fo; - float weight1 = fo + 1.0f - theta; - float weight2 = theta - fo; - if(DYNAMIC_INDEXING) - { - des[fidx] += (weight1 * weight); - des[fidx + 1] += (weight2 * weight); - //this dynamic indexing part might be slow - }else - { - #pragma unroll - for(int k = 0; k < 8; ++k) - { - if(k == fidx) - { - des[k] += (weight1 * weight); - des[k+1] += (weight2 * weight); - } - } - } - } - } - } - des[0] += des[8]; - - int didx = idx << 1; - d_des[didx] = make_float4(des[0], des[1], des[2], des[3]); - d_des[didx+1] = make_float4(des[4], des[5], des[6], des[7]); -} - -void __global__ NormalizeDescriptor_Kernel(float4* d_des, int num) -{ - float4 temp[32]; - int idx = IMUL(blockIdx.x, blockDim.x) + threadIdx.x; - if(idx >= num) return; - int sidx = idx << 5; - float norm1 = 0, norm2 = 0; -#pragma unroll - for(int i = 0; i < 32; ++i) - { - temp[i] = tex1Dfetch(texDataF4, sidx +i); - norm1 += (temp[i].x * temp[i].x + temp[i].y * temp[i].y + - temp[i].z * temp[i].z + temp[i].w * temp[i].w); - } - norm1 = rsqrt(norm1); - -#pragma unroll - for(int i = 0; i < 32; ++i) - { - temp[i].x = min(0.2f, temp[i].x * norm1); - temp[i].y = min(0.2f, temp[i].y * norm1); - temp[i].z = min(0.2f, temp[i].z * norm1); - temp[i].w = min(0.2f, temp[i].w * norm1); - norm2 += (temp[i].x * temp[i].x + temp[i].y * temp[i].y + - temp[i].z * temp[i].z + temp[i].w * temp[i].w); - } - - norm2 = rsqrt(norm2); -#pragma unroll - for(int i = 0; i < 32; ++i) - { - temp[i].x *= norm2; temp[i].y *= norm2; - temp[i].z *= norm2; temp[i].w *= norm2; - d_des[sidx + i] = temp[i]; - } -} - -void ProgramCU::ComputeDescriptor(CuTexImage*list, CuTexImage* got, CuTexImage* dtex, int rect, int stream) -{ - int num = list->GetImgWidth(); - int width = got->GetImgWidth(); - int height = got->GetImgHeight(); - - dtex->InitTexture(num * 128, 1, 1); - got->BindTexture2D(texDataF2); - list->BindTexture(texDataF4); - int block_width = DESCRIPTOR_COMPUTE_BLOCK_SIZE; - dim3 grid((num * 16 + block_width -1) / block_width); - dim3 block(block_width); - - if(rect) - { - if(GlobalUtil::_UseDynamicIndexing) - ComputeDescriptorRECT_Kernel<true><<<grid, block>>>((float4*) dtex->_cuData, num, width, height, GlobalUtil::_DescriptorWindowFactor); - else - ComputeDescriptorRECT_Kernel<false><<<grid, block>>>((float4*) dtex->_cuData, num, width, height, GlobalUtil::_DescriptorWindowFactor); - - }else - { - if(GlobalUtil::_UseDynamicIndexing) - ComputeDescriptor_Kernel<true><<<grid, block>>>((float4*) dtex->_cuData, num, width, height, GlobalUtil::_DescriptorWindowFactor); - else - ComputeDescriptor_Kernel<false><<<grid, block>>>((float4*) dtex->_cuData, num, width, height, GlobalUtil::_DescriptorWindowFactor); - } - if(GlobalUtil::_NormalizedSIFT) - { - dtex->BindTexture(texDataF4); - const int block_width = DESCRIPTOR_NORMALIZ_PER_BLOCK; - dim3 grid((num + block_width -1) / block_width); - dim3 block(block_width); - NormalizeDescriptor_Kernel<<<grid, block>>>((float4*) dtex->_cuData, num); - } - CheckErrorCUDA("ComputeDescriptor"); -} - -////////////////////////////////////////////////////// -void ProgramCU::FinishCUDA() -{ - cudaThreadSynchronize(); -} - -int ProgramCU::CheckErrorCUDA(const char* location) -{ - cudaError_t e = cudaGetLastError(); - if(e) - { - if(location) fprintf(stderr, "%s:\t", location); - fprintf(stderr, "%s\n", cudaGetErrorString(e)); - //assert(0); - return 1; - }else - { - return 0; - } -} - -void __global__ ConvertDOG_Kernel(float* d_result, int width, int height) -{ - int row = (blockIdx.y << BLOCK_LOG_DIM) + threadIdx.y; - int col = (blockIdx.x << BLOCK_LOG_DIM) + threadIdx.x; - if(col < width && row < height) - { - int index = row * width + col; - float v = tex1Dfetch(texData, index); - d_result[index] = (col == 0 || row == 0 || col == width -1 || row == height -1)? - 0.5 : saturate(0.5+20.0*v); - } -} -/// -void ProgramCU::DisplayConvertDOG(CuTexImage* dog, CuTexImage* out) -{ - if(out->_cuData == NULL) return; - int width = dog->GetImgWidth(), height = dog ->GetImgHeight(); - dog->BindTexture(texData); - dim3 grid((width + BLOCK_DIM - 1)/ BLOCK_DIM, (height + BLOCK_DIM - 1)/BLOCK_DIM); - dim3 block(BLOCK_DIM, BLOCK_DIM); - ConvertDOG_Kernel<<<grid, block>>>((float*) out->_cuData, width, height); - ProgramCU::CheckErrorCUDA("DisplayConvertDOG"); -} - -void __global__ ConvertGRD_Kernel(float* d_result, int width, int height) -{ - int row = (blockIdx.y << BLOCK_LOG_DIM) + threadIdx.y; - int col = (blockIdx.x << BLOCK_LOG_DIM) + threadIdx.x; - if(col < width && row < height) - { - int index = row * width + col; - float v = tex1Dfetch(texData, index << 1); - d_result[index] = (col == 0 || row == 0 || col == width -1 || row == height -1)? - 0 : saturate(5 * v); - - } -} - - -void ProgramCU::DisplayConvertGRD(CuTexImage* got, CuTexImage* out) -{ - if(out->_cuData == NULL) return; - int width = got->GetImgWidth(), height = got ->GetImgHeight(); - got->BindTexture(texData); - dim3 grid((width + BLOCK_DIM - 1)/ BLOCK_DIM, (height + BLOCK_DIM - 1)/BLOCK_DIM); - dim3 block(BLOCK_DIM, BLOCK_DIM); - ConvertGRD_Kernel<<<grid, block>>>((float*) out->_cuData, width, height); - ProgramCU::CheckErrorCUDA("DisplayConvertGRD"); -} - -void __global__ ConvertKEY_Kernel(float4* d_result, int width, int height) -{ - - int row = (blockIdx.y << BLOCK_LOG_DIM) + threadIdx.y; - int col = (blockIdx.x << BLOCK_LOG_DIM) + threadIdx.x; - if(col < width && row < height) - { - int index = row * width + col; - float4 keyv = tex1Dfetch(texDataF4, index); - int is_key = (keyv.x == 1.0f || keyv.x == -1.0f); - int inside = col > 0 && row > 0 && row < height -1 && col < width - 1; - float v = inside? saturate(0.5 + 20 * tex1Dfetch(texData, index)) : 0.5; - d_result[index] = is_key && inside ? - (keyv.x > 0? make_float4(1.0f, 0, 0, 1.0f) : make_float4(0.0f, 1.0f, 0.0f, 1.0f)): - make_float4(v, v, v, 1.0f) ; - } -} -void ProgramCU::DisplayConvertKEY(CuTexImage* key, CuTexImage* dog, CuTexImage* out) -{ - if(out->_cuData == NULL) return; - int width = key->GetImgWidth(), height = key ->GetImgHeight(); - dog->BindTexture(texData); - key->BindTexture(texDataF4); - dim3 grid((width + BLOCK_DIM - 1)/ BLOCK_DIM, (height + BLOCK_DIM - 1)/BLOCK_DIM); - dim3 block(BLOCK_DIM, BLOCK_DIM); - ConvertKEY_Kernel<<<grid, block>>>((float4*) out->_cuData, width, height); -} - - -void __global__ DisplayKeyPoint_Kernel(float4 * d_result, int num) -{ - int idx = IMUL(blockIdx.x, blockDim.x) + threadIdx.x; - if(idx >= num) return; - float4 v = tex1Dfetch(texDataF4, idx); - d_result[idx] = make_float4(v.x, v.y, 0, 1.0f); -} - -void ProgramCU::DisplayKeyPoint(CuTexImage* ftex, CuTexImage* out) -{ - int num = ftex->GetImgWidth(); - int block_width = 64; - dim3 grid((num + block_width -1) /block_width); - dim3 block(block_width); - ftex->BindTexture(texDataF4); - DisplayKeyPoint_Kernel<<<grid, block>>>((float4*) out->_cuData, num); - ProgramCU::CheckErrorCUDA("DisplayKeyPoint"); -} - -void __global__ DisplayKeyBox_Kernel(float4* d_result, int num) -{ - int idx = IMUL(blockIdx.x, blockDim.x) + threadIdx.x; - if(idx >= num) return; - int kidx = idx / 10, vidx = idx - IMUL(kidx , 10); - float4 v = tex1Dfetch(texDataF4, kidx); - float sz = fabs(v.z * 3.0f); - /////////////////////// - float s, c; __sincosf(v.w, &s, &c); - /////////////////////// - float dx = vidx == 0? 0 : ((vidx <= 4 || vidx >= 9)? sz : -sz); - float dy = vidx <= 1? 0 : ((vidx <= 2 || vidx >= 7)? -sz : sz); - float4 pos; - pos.x = v.x + c * dx - s * dy; - pos.y = v.y + c * dy + s * dx; - pos.z = 0; pos.w = 1.0f; - d_result[idx] = pos; -} - -void ProgramCU::DisplayKeyBox(CuTexImage* ftex, CuTexImage* out) -{ - int len = ftex->GetImgWidth(); - int block_width = 32; - dim3 grid((len * 10 + block_width -1) / block_width); - dim3 block(block_width); - ftex->BindTexture(texDataF4); - DisplayKeyBox_Kernel<<<grid, block>>>((float4*) out->_cuData, len * 10); -} -/////////////////////////////////////////////////////////////////// -inline void CuTexImage:: BindTexture(textureReference& texRef) -{ - cudaBindTexture(NULL, &texRef, _cuData, &texRef.channelDesc, _numBytes); -} - -inline void CuTexImage::BindTexture2D(textureReference& texRef) -{ -#if defined(SIFTGPU_ENABLE_LINEAR_TEX2D) - cudaBindTexture2D(0, &texRef, _cuData, &texRef.channelDesc, _imgWidth, _imgHeight, _imgWidth* _numChannel* sizeof(float)); -#else - cudaChannelFormatDesc desc; - cudaGetChannelDesc(&desc, _cuData2D); - cudaBindTextureToArray(&texRef, _cuData2D, &desc); -#endif -} - -int ProgramCU::CheckCudaDevice(int device) -{ - int count = 0, device_used; - if(cudaGetDeviceCount(&count) != cudaSuccess || count <= 0) - { - ProgramCU::CheckErrorCUDA("CheckCudaDevice"); - return 0; - }else if(count == 1) - { - cudaDeviceProp deviceProp; - if ( cudaGetDeviceProperties(&deviceProp, 0) != cudaSuccess || - (deviceProp.major == 9999 && deviceProp.minor == 9999)) - { - fprintf(stderr, "CheckCudaDevice: no device supporting CUDA.\n"); - return 0; - }else - { - GlobalUtil::_MemCapGPU = deviceProp.totalGlobalMem / 1024; - GlobalUtil::_texMaxDimGL = 32768; - if(GlobalUtil::_verbose) - fprintf(stdout, "NOTE: changing maximum texture dimension to %d\n", GlobalUtil::_texMaxDimGL); - - } - } - if(device >0 && device < count) - { - cudaSetDevice(device); - CheckErrorCUDA("cudaSetDevice\n"); - } - cudaGetDevice(&device_used); - if(device != device_used) - fprintf(stderr, "\nERROR: Cannot set device to %d\n" - "\nWARNING: Use # %d device instead (out of %d)\n", device, device_used, count); - return 1; -} - -//////////////////////////////////////////////////////////////////////////////////////// -// siftmatch funtions -////////////////////////////////////////////////////////////////////////////////////////// - -#define MULT_TBLOCK_DIMX 128 -#define MULT_TBLOCK_DIMY 1 -#define MULT_BLOCK_DIMX (MULT_TBLOCK_DIMX) -#define MULT_BLOCK_DIMY (8 * MULT_TBLOCK_DIMY) - - -texture<uint4, 1, cudaReadModeElementType> texDes1; -texture<uint4, 1, cudaReadModeElementType> texDes2; - -void __global__ MultiplyDescriptor_Kernel(int* d_result, int num1, int num2, int3* d_temp) -{ - int idx01 = (blockIdx.y * MULT_BLOCK_DIMY), idx02 = (blockIdx.x * MULT_BLOCK_DIMX); - - int idx1 = idx01 + threadIdx.y, idx2 = idx02 + threadIdx.x; - __shared__ int data1[17 * 2 * MULT_BLOCK_DIMY]; - int read_idx1 = idx01 * 8 + threadIdx.x, read_idx2 = idx2 * 8; - int col4 = threadIdx.x & 0x3, row4 = threadIdx.x >> 2; - int cache_idx1 = IMUL(row4, 17) + (col4 << 2); - - /////////////////////////////////////////////////////////////// - //Load feature descriptors - /////////////////////////////////////////////////////////////// -#if MULT_BLOCK_DIMY == 16 - uint4 v = tex1Dfetch(texDes1, read_idx1); - data1[cache_idx1] = v.x; data1[cache_idx1+1] = v.y; - data1[cache_idx1+2] = v.z; data1[cache_idx1+3] = v.w; -#elif MULT_BLOCK_DIMY == 8 - if(threadIdx.x < 64) - { - uint4 v = tex1Dfetch(texDes1, read_idx1); - data1[cache_idx1] = v.x; data1[cache_idx1+1] = v.y; - data1[cache_idx1+2] = v.z; data1[cache_idx1+3] = v.w; - } -#else -#error -#endif - __syncthreads(); - - /// - if(idx2 >= num2) return; - /////////////////////////////////////////////////////////////////////////// - //compare descriptors - - int results[MULT_BLOCK_DIMY]; -#pragma unroll - for(int i = 0; i < MULT_BLOCK_DIMY; ++i) results[i] = 0; - -#pragma unroll - for(int i = 0; i < 8; ++i) - { - uint4 v = tex1Dfetch(texDes2, read_idx2 + i); - unsigned char* p2 = (unsigned char*)(&v); -#pragma unroll - for(int k = 0; k < MULT_BLOCK_DIMY; ++k) - { - unsigned char* p1 = (unsigned char*) (data1 + k * 34 + i * 4 + (i/4)); - results[k] += ( IMUL(p1[0], p2[0]) + IMUL(p1[1], p2[1]) - + IMUL(p1[2], p2[2]) + IMUL(p1[3], p2[3]) - + IMUL(p1[4], p2[4]) + IMUL(p1[5], p2[5]) - + IMUL(p1[6], p2[6]) + IMUL(p1[7], p2[7]) - + IMUL(p1[8], p2[8]) + IMUL(p1[9], p2[9]) - + IMUL(p1[10], p2[10]) + IMUL(p1[11], p2[11]) - + IMUL(p1[12], p2[12]) + IMUL(p1[13], p2[13]) - + IMUL(p1[14], p2[14]) + IMUL(p1[15], p2[15])); - } - } - - int dst_idx = IMUL(idx1, num2) + idx2; - if(d_temp) - { - int3 cmp_result = make_int3(0, -1, 0); - -#pragma unroll - for(int i = 0; i < MULT_BLOCK_DIMY; ++i) - { - if(idx1 + i < num1) - { - cmp_result = results[i] > cmp_result.x? - make_int3(results[i], idx1 + i, cmp_result.x) : - make_int3(cmp_result.x, cmp_result.y, max(cmp_result.z, results[i])); - d_result[dst_idx + IMUL(i, num2)] = results[i]; - } - } - d_temp[ IMUL(blockIdx.y, num2) + idx2] = cmp_result; - }else - { -#pragma unroll - for(int i = 0; i < MULT_BLOCK_DIMY; ++i) - { - if(idx1 + i < num1) d_result[dst_idx + IMUL(i, num2)] = results[i]; - } - } - -} - - -void ProgramCU::MultiplyDescriptor(CuTexImage* des1, CuTexImage* des2, CuTexImage* texDot, CuTexImage* texCRT) -{ - int num1 = des1->GetImgWidth() / 8; - int num2 = des2->GetImgWidth() / 8; - dim3 grid( (num2 + MULT_BLOCK_DIMX - 1)/ MULT_BLOCK_DIMX, - (num1 + MULT_BLOCK_DIMY - 1)/MULT_BLOCK_DIMY); - dim3 block(MULT_TBLOCK_DIMX, MULT_TBLOCK_DIMY); - texDot->InitTexture( num2,num1); - if(texCRT) texCRT->InitTexture(num2, (num1 + MULT_BLOCK_DIMY - 1)/MULT_BLOCK_DIMY, 32); - des1->BindTexture(texDes1); - des2->BindTexture(texDes2); - - MultiplyDescriptor_Kernel<<<grid, block>>>((int*)texDot->_cuData, num1, num2, - (texCRT? (int3*)texCRT->_cuData : NULL)); - ProgramCU::CheckErrorCUDA("MultiplyDescriptor"); -} - -texture<float, 1, cudaReadModeElementType> texLoc1; -texture<float2, 1, cudaReadModeElementType> texLoc2; -struct Matrix33{float mat[3][3];}; - - - -void __global__ MultiplyDescriptorG_Kernel(int* d_result, int num1, int num2, int3* d_temp, - Matrix33 H, float hdistmax, Matrix33 F, float fdistmax) -{ - int idx01 = (blockIdx.y * MULT_BLOCK_DIMY); - int idx02 = (blockIdx.x * MULT_BLOCK_DIMX); - - int idx1 = idx01 + threadIdx.y; - int idx2 = idx02 + threadIdx.x; - __shared__ int data1[17 * 2 * MULT_BLOCK_DIMY]; - __shared__ float loc1[MULT_BLOCK_DIMY * 2]; - int read_idx1 = idx01 * 8 + threadIdx.x ; - int read_idx2 = idx2 * 8; - int col4 = threadIdx.x & 0x3, row4 = threadIdx.x >> 2; - int cache_idx1 = IMUL(row4, 17) + (col4 << 2); -#if MULT_BLOCK_DIMY == 16 - uint4 v = tex1Dfetch(texDes1, read_idx1); - data1[cache_idx1] = v.x; - data1[cache_idx1+1] = v.y; - data1[cache_idx1+2] = v.z; - data1[cache_idx1+3] = v.w; -#elif MULT_BLOCK_DIMY == 8 - if(threadIdx.x < 64) - { - uint4 v = tex1Dfetch(texDes1, read_idx1); - data1[cache_idx1] = v.x; - data1[cache_idx1+1] = v.y; - data1[cache_idx1+2] = v.z; - data1[cache_idx1+3] = v.w; - } -#else -#error -#endif - __syncthreads(); - if(threadIdx.x < MULT_BLOCK_DIMY * 2) - { - loc1[threadIdx.x] = tex1Dfetch(texLoc1, 2 * idx01 + threadIdx.x); - } - __syncthreads(); - if(idx2 >= num2) return; - int results[MULT_BLOCK_DIMY]; - ///////////////////////////////////////////////////////////////////////////////////////////// - //geometric verification - ///////////////////////////////////////////////////////////////////////////////////////////// - int good_count = 0; - float2 loc2 = tex1Dfetch(texLoc2, idx2); -#pragma unroll - for(int i = 0; i < MULT_BLOCK_DIMY; ++i) - { - - if(idx1 + i < num1) - { - float* loci = loc1 + i * 2; - float locx = loci[0], locy = loci[1]; - //homography - float x[3], diff[2]; - x[0] = H.mat[0][0] * locx + H.mat[0][1] * locy + H.mat[0][2]; - x[1] = H.mat[1][0] * locx + H.mat[1][1] * locy + H.mat[1][2]; - x[2] = H.mat[2][0] * locx + H.mat[2][1] * locy + H.mat[2][2]; - diff[0] = fabs(FDIV(x[0], x[2]) - loc2.x); - diff[1] = fabs(FDIV(x[1], x[2]) - loc2.y); - if(diff[0] < hdistmax && diff[1] < hdistmax) - { - //check fundamental matrix - float fx1[3], ftx2[3], x2fx1, se; - fx1[0] = F.mat[0][0] * locx + F.mat[0][1] * locy + F.mat[0][2]; - fx1[1] = F.mat[1][0] * locx + F.mat[1][1] * locy + F.mat[1][2]; - fx1[2] = F.mat[2][0] * locx + F.mat[2][1] * locy + F.mat[2][2]; - - ftx2[0] = F.mat[0][0] * loc2.x + F.mat[1][0] * loc2.y + F.mat[2][0]; - ftx2[1] = F.mat[0][1] * loc2.x + F.mat[1][1] * loc2.y + F.mat[2][1]; - //ftx2[2] = F.mat[0][2] * loc2.x + F.mat[1][2] * loc2.y + F.mat[2][2]; - - x2fx1 = loc2.x * fx1[0] + loc2.y * fx1[1] + fx1[2]; - se = FDIV(x2fx1 * x2fx1, fx1[0] * fx1[0] + fx1[1] * fx1[1] + ftx2[0] * ftx2[0] + ftx2[1] * ftx2[1]); - results[i] = se < fdistmax? 0: -262144; - }else - { - results[i] = -262144; - } - }else - { - results[i] = -262144; - } - good_count += (results[i] >=0); - } - ///////////////////////////////////////////////////////////////////////////////////////////// - ///compare feature descriptors anyway - ///////////////////////////////////////////////////////////////////////////////////////////// - if(good_count > 0) - { -#pragma unroll - for(int i = 0; i < 8; ++i) - { - uint4 v = tex1Dfetch(texDes2, read_idx2 + i); - unsigned char* p2 = (unsigned char*)(&v); -#pragma unroll - for(int k = 0; k < MULT_BLOCK_DIMY; ++k) - { - unsigned char* p1 = (unsigned char*) (data1 + k * 34 + i * 4 + (i/4)); - results[k] += ( IMUL(p1[0], p2[0]) + IMUL(p1[1], p2[1]) - + IMUL(p1[2], p2[2]) + IMUL(p1[3], p2[3]) - + IMUL(p1[4], p2[4]) + IMUL(p1[5], p2[5]) - + IMUL(p1[6], p2[6]) + IMUL(p1[7], p2[7]) - + IMUL(p1[8], p2[8]) + IMUL(p1[9], p2[9]) - + IMUL(p1[10], p2[10]) + IMUL(p1[11], p2[11]) - + IMUL(p1[12], p2[12]) + IMUL(p1[13], p2[13]) - + IMUL(p1[14], p2[14]) + IMUL(p1[15], p2[15])); - } - } - } - int dst_idx = IMUL(idx1, num2) + idx2; - if(d_temp) - { - int3 cmp_result = make_int3(0, -1, 0); -#pragma unroll - for(int i= 0; i < MULT_BLOCK_DIMY; ++i) - { - if(idx1 + i < num1) - { - cmp_result = results[i] > cmp_result.x? - make_int3(results[i], idx1 + i, cmp_result.x) : - make_int3(cmp_result.x, cmp_result.y, max(cmp_result.z, results[i])); - d_result[dst_idx + IMUL(i, num2)] = max(results[i], 0); - }else - { - break; - } - } - d_temp[ IMUL(blockIdx.y, num2) + idx2] = cmp_result; - }else - { -#pragma unroll - for(int i = 0; i < MULT_BLOCK_DIMY; ++i) - { - if(idx1 + i < num1) d_result[dst_idx + IMUL(i, num2)] = max(results[i], 0); - else break; - } - } - -} - - -void ProgramCU::MultiplyDescriptorG(CuTexImage* des1, CuTexImage* des2, - CuTexImage* loc1, CuTexImage* loc2, CuTexImage* texDot, CuTexImage* texCRT, - float H[3][3], float hdistmax, float F[3][3], float fdistmax) -{ - int num1 = des1->GetImgWidth() / 8; - int num2 = des2->GetImgWidth() / 8; - Matrix33 MatF, MatH; - //copy the matrix - memcpy(MatF.mat, F, 9 * sizeof(float)); - memcpy(MatH.mat, H, 9 * sizeof(float)); - //thread blocks - dim3 grid( (num2 + MULT_BLOCK_DIMX - 1)/ MULT_BLOCK_DIMX, - (num1 + MULT_BLOCK_DIMY - 1)/MULT_BLOCK_DIMY); - dim3 block(MULT_TBLOCK_DIMX, MULT_TBLOCK_DIMY); - //intermediate results - texDot->InitTexture( num2,num1); - if(texCRT) texCRT->InitTexture( num2, (num1 + MULT_BLOCK_DIMY - 1)/MULT_BLOCK_DIMY, 3); - loc1->BindTexture(texLoc1); - loc2->BindTexture(texLoc2); - des1->BindTexture(texDes1); - des2->BindTexture(texDes2); - MultiplyDescriptorG_Kernel<<<grid, block>>>((int*)texDot->_cuData, num1, num2, - (texCRT? (int3*)texCRT->_cuData : NULL), - MatH, hdistmax, MatF, fdistmax); -} - - -texture<int, 1, cudaReadModeElementType> texDOT; - -#define ROWMATCH_BLOCK_WIDTH 32 -#define ROWMATCH_BLOCK_HEIGHT 1 - -void __global__ RowMatch_Kernel(int*d_dot, int* d_result, int num2, float distmax, float ratiomax) -{ -#if ROWMATCH_BLOCK_HEIGHT == 1 - __shared__ int dotmax[ROWMATCH_BLOCK_WIDTH]; - __shared__ int dotnxt[ROWMATCH_BLOCK_WIDTH]; - __shared__ int dotidx[ROWMATCH_BLOCK_WIDTH]; - int row = blockIdx.y; -#else - __shared__ int x_dotmax[ROWMATCH_BLOCK_HEIGHT][ROWMATCH_BLOCK_WIDTH]; - __shared__ int x_dotnxt[ROWMATCH_BLOCK_HEIGHT][ROWMATCH_BLOCK_WIDTH]; - __shared__ int x_dotidx[ROWMATCH_BLOCK_HEIGHT][ROWMATCH_BLOCK_WIDTH]; - int* dotmax = x_dotmax[threadIdx.y]; - int* dotnxt = x_dotnxt[threadIdx.y]; - int* dotidx = x_dotidx[threadIdx.y]; - int row = IMUL(blockIdx.y, ROWMATCH_BLOCK_HEIGHT) + threadIdx.y; -#endif - - int base_address = IMUL(row , num2); - int t_dotmax = 0, t_dotnxt = 0, t_dotidx = -1; - for(int i = 0; i < num2; i += ROWMATCH_BLOCK_WIDTH) - { - if(threadIdx.x + i < num2) - { - int v = tex1Dfetch(texDOT, base_address + threadIdx.x + i);//d_dot[base_address + threadIdx.x + i];// - bool test = v > t_dotmax; - t_dotnxt = test? t_dotmax : max(t_dotnxt, v); - t_dotidx = test? (threadIdx.x + i) : t_dotidx; - t_dotmax = test? v: t_dotmax; - } - __syncthreads(); - } - dotmax[threadIdx.x] = t_dotmax; - dotnxt[threadIdx.x] = t_dotnxt; - dotidx[threadIdx.x] = t_dotidx; - __syncthreads(); - -#pragma unroll - for(int step = ROWMATCH_BLOCK_WIDTH/2; step >0; step /= 2) - { - if(threadIdx.x < step) - { - int v1 = dotmax[threadIdx.x], v2 = dotmax[threadIdx.x + step]; - bool test = v2 > v1; - dotnxt[threadIdx.x] = test? max(v1, dotnxt[threadIdx.x + step]) :max(dotnxt[threadIdx.x], v2); - dotidx[threadIdx.x] = test? dotidx[threadIdx.x + step] : dotidx[threadIdx.x]; - dotmax[threadIdx.x] = test? v2 : v1; - } - __syncthreads(); - } - if(threadIdx.x == 0) - { - float dist = acos(min(dotmax[0] * 0.000003814697265625f, 1.0)); - float distn = acos(min(dotnxt[0] * 0.000003814697265625f, 1.0)); - //float ratio = dist / distn; - d_result[row] = (dist < distmax) && (dist < distn * ratiomax) ? dotidx[0] : -1;//? : -1; - } - -} - - -void ProgramCU::GetRowMatch(CuTexImage* texDot, CuTexImage* texMatch, float distmax, float ratiomax) -{ - int num1 = texDot->GetImgHeight(); - int num2 = texDot->GetImgWidth(); - dim3 grid(1, num1/ROWMATCH_BLOCK_HEIGHT); - dim3 block(ROWMATCH_BLOCK_WIDTH, ROWMATCH_BLOCK_HEIGHT); - texDot->BindTexture(texDOT); - RowMatch_Kernel<<<grid, block>>>((int*)texDot->_cuData, - (int*)texMatch->_cuData, num2, distmax, ratiomax); -} - -#define COLMATCH_BLOCK_WIDTH 32 - -//texture<int3, 1, cudaReadModeElementType> texCT; - -void __global__ ColMatch_Kernel(int3*d_crt, int* d_result, int height, int num2, float distmax, float ratiomax) -{ - int col = COLMATCH_BLOCK_WIDTH * blockIdx.x + threadIdx.x; - if(col >= num2) return; - int3 result = d_crt[col];//tex1Dfetch(texCT, col); - int read_idx = col + num2; - for(int i = 1; i < height; ++i, read_idx += num2) - { - int3 temp = d_crt[read_idx];//tex1Dfetch(texCT, read_idx); - result = result.x < temp.x? - make_int3(temp.x, temp.y, max(result.x, temp.z)) : - make_int3(result.x, result.y, max(result.z, temp.x)); - } - - float dist = acos(min(result.x * 0.000003814697265625f, 1.0)); - float distn = acos(min(result.z * 0.000003814697265625f, 1.0)); - //float ratio = dist / distn; - d_result[col] = (dist < distmax) && (dist < distn * ratiomax) ? result.y : -1;//? : -1; - -} - -void ProgramCU::GetColMatch(CuTexImage* texCRT, CuTexImage* texMatch, float distmax, float ratiomax) -{ - int height = texCRT->GetImgHeight(); - int num2 = texCRT->GetImgWidth(); - //texCRT->BindTexture(texCT); - dim3 grid((num2 + COLMATCH_BLOCK_WIDTH -1) / COLMATCH_BLOCK_WIDTH); - dim3 block(COLMATCH_BLOCK_WIDTH); - ColMatch_Kernel<<<grid, block>>>((int3*)texCRT->_cuData, (int*) texMatch->_cuData, height, num2, distmax, ratiomax); -} - -#endif diff --git a/3rdparty/SiftGPU/src/SiftGPU/ProgramCU.h b/3rdparty/SiftGPU/src/SiftGPU/ProgramCU.h deleted file mode 100644 index e261b844..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/ProgramCU.h +++ /dev/null @@ -1,74 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: ProgramCU.h -// Author: Changchang Wu -// Description : interface for the ProgramCU classes. -// It is basically a wrapper around all the CUDA kernels -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - -#ifndef _PROGRAM_CU_H -#define _PROGRAM_CU_H -#if defined(CUDA_SIFTGPU_ENABLED) - -class CuTexImage; - -class ProgramCU -{ -public: - //GPU FUNCTIONS - static void FinishCUDA(); - static int CheckErrorCUDA(const char* location); - static int CheckCudaDevice(int device); -public: - ////SIFTGPU FUNCTIONS - static void CreateFilterKernel(float sigma, float* kernel, int& width); - template<int KWIDTH> static void FilterImage(CuTexImage *dst, CuTexImage *src, CuTexImage* buf); - static void FilterImage(CuTexImage *dst, CuTexImage *src, CuTexImage* buf, float sigma); - static void ComputeDOG(CuTexImage* gus, CuTexImage* dog, CuTexImage* got); - static void ComputeKEY(CuTexImage* dog, CuTexImage* key, float Tdog, float Tedge); - static void InitHistogram(CuTexImage* key, CuTexImage* hist); - static void ReduceHistogram(CuTexImage*hist1, CuTexImage* hist2); - static void GenerateList(CuTexImage* list, CuTexImage* hist); - static void ComputeOrientation(CuTexImage*list, CuTexImage* got, CuTexImage*key, - float sigma, float sigma_step, int existing_keypoint); - static void ComputeDescriptor(CuTexImage*list, CuTexImage* got, CuTexImage* dtex, int rect = 0, int stream = 0); - - //data conversion - static void SampleImageU(CuTexImage *dst, CuTexImage *src, int log_scale); - static void SampleImageD(CuTexImage *dst, CuTexImage *src, int log_scale = 1); - static void ReduceToSingleChannel(CuTexImage* dst, CuTexImage* src, int convert_rgb); - static void ConvertByteToFloat(CuTexImage*src, CuTexImage* dst); - - //visualization - static void DisplayConvertDOG(CuTexImage* dog, CuTexImage* out); - static void DisplayConvertGRD(CuTexImage* got, CuTexImage* out); - static void DisplayConvertKEY(CuTexImage* key, CuTexImage* dog, CuTexImage* out); - static void DisplayKeyPoint(CuTexImage* ftex, CuTexImage* out); - static void DisplayKeyBox(CuTexImage* ftex, CuTexImage* out); - - //SIFTMATCH FUNCTIONS - static void MultiplyDescriptor(CuTexImage* tex1, CuTexImage* tex2, CuTexImage* texDot, CuTexImage* texCRT); - static void MultiplyDescriptorG(CuTexImage* texDes1, CuTexImage* texDes2, - CuTexImage* texLoc1, CuTexImage* texLoc2, CuTexImage* texDot, CuTexImage* texCRT, - float H[3][3], float hdistmax, float F[3][3], float fdistmax); - static void GetRowMatch(CuTexImage* texDot, CuTexImage* texMatch, float distmax, float ratiomax); - static void GetColMatch(CuTexImage* texCRT, CuTexImage* texMatch, float distmax, float ratiomax); -}; - -#endif -#endif - diff --git a/3rdparty/SiftGPU/src/SiftGPU/ProgramGLSL.cpp b/3rdparty/SiftGPU/src/SiftGPU/ProgramGLSL.cpp deleted file mode 100644 index e95199dc..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/ProgramGLSL.cpp +++ /dev/null @@ -1,2690 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: ProgramGLSL.cpp -// Author: Changchang Wu -// Description : GLSL related classes -// class ProgramGLSL A simple wrapper of GLSL programs -// class ShaderBagGLSL GLSL shaders for SIFT -// class FilterGLSL GLSL gaussian filters for SIFT -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - - -#include "GL/glew.h" -#include <string.h> -#include <stdio.h> -#include <iomanip> -#include <iostream> -#include <sstream> -#include <vector> -#include <algorithm> -#include <math.h> -using namespace std; - -#include "GlobalUtil.h" -#include "ProgramGLSL.h" -#include "GLTexImage.h" -#include "ShaderMan.h" -#include "SiftGPU.h" - -ProgramGLSL::ShaderObject::ShaderObject(int shadertype, const char * source, int filesource) -{ - - - _type = shadertype; - _compiled = 0; - - - _shaderID = glCreateShader(shadertype); - if(_shaderID == 0) return; - - if(source) - { - - GLint code_length; - if(filesource ==0) - { - const char* code = source; - code_length = (GLint) strlen(code); - glShaderSource(_shaderID, 1, (const char **) &code, &code_length); - }else - { - char * code; - if((code_length= ReadShaderFile(source, code)) ==0) return; - glShaderSource(_shaderID, 1, (const char **) &code, &code_length); - delete code; - } - - glCompileShader(_shaderID); - - CheckCompileLog(); - - if(!_compiled) std::cout << source; - } - - - - -} - -int ProgramGLSL::ShaderObject::ReadShaderFile(const char *sourcefile, char*& code ) -{ - code = NULL; - FILE * file; - int len=0; - - if(sourcefile == NULL) return 0; - - file = fopen(sourcefile,"rt"); - if(file == NULL) return 0; - - - fseek(file, 0, SEEK_END); - len = ftell(file); - rewind(file); - if(len >1) - { - code = new char[len+1]; - fread(code, sizeof( char), len, file); - code[len] = 0; - }else - { - len = 0; - } - - fclose(file); - - return len; - -} - -void ProgramGLSL::ShaderObject::CheckCompileLog() -{ - - GLint status; - glGetShaderiv(_shaderID, GL_COMPILE_STATUS, &status); - _compiled = (status ==GL_TRUE); - - if(_compiled == 0) PrintCompileLog(std::cout); - - -} - -ProgramGLSL::ShaderObject::~ShaderObject() -{ - if(_shaderID) glDeleteShader(_shaderID); - -} - -int ProgramGLSL::ShaderObject::IsValidFragmentShader() -{ - return _type == GL_FRAGMENT_SHADER && _shaderID && _compiled; -} - -int ProgramGLSL::ShaderObject::IsValidVertexShader() -{ - return _type == GL_VERTEX_SHADER && _shaderID && _compiled; -} - - -void ProgramGLSL::ShaderObject::PrintCompileLog(ostream&os) -{ - GLint len = 0; - - glGetShaderiv(_shaderID, GL_INFO_LOG_LENGTH , &len); - if(len <=1) return; - - char * compileLog = new char[len+1]; - if(compileLog == NULL) return; - - glGetShaderInfoLog(_shaderID, len, &len, compileLog); - - - os<<"Compile Log\n"<<compileLog<<"\n"; - - delete[] compileLog; -} - - -ProgramGLSL::ProgramGLSL() -{ - _linked = 0; - _TextureParam0 = -1; - _programID = glCreateProgram(); -} -ProgramGLSL::~ProgramGLSL() -{ - if(_programID)glDeleteProgram(_programID); -} -void ProgramGLSL::AttachShaderObject(ShaderObject &shader) -{ - if(_programID && shader.IsValidShaderObject()) - glAttachShader(_programID, shader.GetShaderID()); -} -void ProgramGLSL::DetachShaderObject(ShaderObject &shader) -{ - if(_programID && shader.IsValidShaderObject()) - glDetachShader(_programID, shader.GetShaderID()); -} -int ProgramGLSL::LinkProgram() -{ - _linked = 0; - - if(_programID==0) return 0; - - glLinkProgram(_programID); - - CheckLinkLog(); - -// GlobalUtil::StartTimer("100 link test"); -// for(int i = 0; i<100; i++) glLinkProgram(_programID); -// GlobalUtil::StopTimer(); - - return _linked; -} - -void ProgramGLSL::CheckLinkLog() -{ - GLint status; - glGetProgramiv(_programID, GL_LINK_STATUS, &status); - - _linked = (status == GL_TRUE); - -} - - -int ProgramGLSL::ValidateProgram() -{ - if(_programID && _linked) - { -/// GLint status; -// glValidateProgram(_programID); -// glGetProgramiv(_programID, GL_VALIDATE_STATUS, &status); -// return status == GL_TRUE; - return 1; - } - else - return 0; -} - -void ProgramGLSL::PrintLinkLog(std::ostream &os) -{ - GLint len = 0; - - glGetProgramiv(_programID, GL_INFO_LOG_LENGTH , &len); - if(len <=1) return; - - char* linkLog = new char[len+1]; - if(linkLog == NULL) return; - - glGetProgramInfoLog(_programID, len, &len, linkLog); - - linkLog[len] = 0; - - if(strstr(linkLog, "failed")) - { - os<<linkLog + (linkLog[0] == ' '? 1:0)<<"\n"; - _linked = 0; - } - - delete[] linkLog; -} - -int ProgramGLSL::UseProgram() -{ - if(ValidateProgram()) - { - glUseProgram(_programID); - if (_TextureParam0 >= 0) glUniform1i(_TextureParam0, 0); - return true; - } - else - { - return false; - } -} - - -ProgramGLSL::ProgramGLSL(const char *frag_source) -{ - _linked = 0; - _programID = glCreateProgram(); - _TextureParam0 = -1; - ShaderObject shader(GL_FRAGMENT_SHADER, frag_source); - - if(shader.IsValidFragmentShader()) - { - AttachShaderObject(shader); - LinkProgram(); - - if(!_linked) - { - //shader.PrintCompileLog(std::cout); - PrintLinkLog(std::cout); - } else - { - _TextureParam0 = glGetUniformLocation(_programID, "tex"); - } - }else - { - _linked = 0; - } - -} - -/* -ProgramGLSL::ProgramGLSL(char*frag_source, char * vert_source) -{ - _used = 0; - _linked = 0; - _programID = glCreateProgram(); - ShaderObject shader(GL_FRAGMENT_SHADER, frag_source); - ShaderObject vertex_shader(GL_VERTEX_SHADER, vert_source); - AttachShaderObject(shader); - AttachShaderObject(vertex_shader); - LinkProgram(); - if(!_linked) - { - shader.PrintCompileLog(std::cout); - vertex_shader.PrintCompileLog(std::cout); - PrintLinkLog(std::cout); - std::cout<<vert_source; - std::cout<<frag_source; - } - -} -*/ - - - -void ProgramGLSL::ReLink() -{ - glLinkProgram(_programID); -} - -int ProgramGLSL::IsNative() -{ - return _linked; -} - -FilterGLSL::FilterGLSL(float sigma) -{ - //pixel inside 3*sigma box - int sz = int( ceil( GlobalUtil::_FilterWidthFactor * sigma -0.5) ) ;// - int width = 2*sz + 1; - - //filter size truncation - if(GlobalUtil::_MaxFilterWidth >0 && width > GlobalUtil::_MaxFilterWidth) - { - std::cout<<"Filter size truncated from "<<width<<" to "<<GlobalUtil::_MaxFilterWidth<<endl; - sz = GlobalUtil::_MaxFilterWidth>>1; - width = 2 * sz + 1; - } - - int i; - float * kernel = new float[width]; - float rv = 1.0f/(sigma*sigma); - float v, ksum =0; - - // pre-compute filter - for( i = -sz ; i <= sz ; ++i) - { - kernel[i+sz] = v = exp(-0.5f * i * i *rv) ; - ksum += v; - } - - //normalize the kernel - rv = 1.0f / ksum; - for(i = 0; i< width ;i++) kernel[i]*=rv; - // - - MakeFilterProgram(kernel, width); - - _size = sz; - - delete[] kernel; - if(GlobalUtil::_verbose && GlobalUtil::_timingL) std::cout<<"Filter: sigma = "<<sigma<<", size = "<<width<<"x"<<width<<endl; -} - - -void FilterGLSL::MakeFilterProgram(float kernel[], int width) -{ - if(GlobalUtil::_usePackedTex) - { - s_shader_h = CreateFilterHPK(kernel, width); - s_shader_v = CreateFilterVPK(kernel, width); - }else - { - s_shader_h = CreateFilterH(kernel, width); - s_shader_v = CreateFilterV(kernel, width); - } -} - -ProgramGPU* FilterGLSL::CreateFilterH(float kernel[], int width) -{ - ostringstream out; - out<<setprecision(8); - - out<< "uniform sampler2DRect tex;"; - out<< "\nvoid main(void){ float intensity = 0.0 ; vec2 pos;\n"; - - int half_width = width / 2; - for(int i = 0; i< width; i++) - { - if(i == half_width) - { - - out<<"float or = texture2DRect(tex, gl_TexCoord[0].st).r;\n"; - out<<"intensity+= or * "<<kernel[i]<<";\n"; - }else - { - out<<"pos = gl_TexCoord[0].st + vec2(float("<< (i - half_width) <<") , 0);\n"; - out<<"intensity+= "<<kernel[i]<<"*texture2DRect(tex, pos).r;\n"; - } - } - - //copy original data to red channel - out<<"gl_FragColor.r = or;\n"; - out<<"gl_FragColor.b = intensity;}\n"<<'\0'; - - return new ProgramGLSL(out.str().c_str()); -} - - -ProgramGPU* FilterGLSL::CreateFilterV(float kernel[], int height) -{ - ostringstream out; - out<<setprecision(8); - - out<< "uniform sampler2DRect tex;"; - out<< "\nvoid main(void){ float intensity = 0.0;vec2 pos; \n"; - int half_height = height / 2; - for(int i = 0; i< height; i++) - { - - if(i == half_height) - { - out<<"vec2 orb = texture2DRect(tex, gl_TexCoord[0].st).rb;\n"; - out<<"intensity+= orb.y * "<<kernel[i]<<";\n"; - - }else - { - out<<"pos = gl_TexCoord[0].st + vec2(0, float("<<(i - half_height) <<") );\n"; - out<<"intensity+= texture2DRect(tex, pos).b * "<<kernel[i]<<";\n"; - } - - } - - out<<"gl_FragColor.b = orb.y;\n"; - out<<"gl_FragColor.g = intensity - orb.x;\n"; // difference of gaussian.. - out<<"gl_FragColor.r = intensity;}\n"<<'\0'; - -// std::cout<<buffer<<endl; - return new ProgramGLSL(out.str().c_str()); -} - - - -ProgramGPU* FilterGLSL::CreateFilterHPK(float kernel[], int width) -{ - //both h and v are packed... - int i, j , xw, xwn; - - int halfwidth = width >>1; - float * pf = kernel + halfwidth; - int nhpixel = (halfwidth+1)>>1; //how many neighbour pixels need to be looked up - int npixel = (nhpixel<<1)+1;// - float weight[3]; - ostringstream out;; - out<<setprecision(8); - - out<< "uniform sampler2DRect tex;"; - out<< "\nvoid main(void){ vec4 result = vec4(0, 0, 0, 0);\n"; - ///use multi texture coordinate because nhpixels can be at most 3 - out<<"vec4 pc; vec2 coord; \n"; - for( i = 0 ; i < npixel ; i++) - { - out<<"coord = gl_TexCoord[0].xy + vec2(float("<<i-nhpixel<<"),0);\n"; - out<<"pc=texture2DRect(tex, coord);\n"; - if(GlobalUtil::_PreciseBorder) out<<"if(coord.x < 0.0) pc = pc.rrbb;\n"; - //for each sub-pixel j in center, the weight of sub-pixel k - xw = (i - nhpixel)*2; - for( j = 0; j < 3; j++) - { - xwn = xw + j -1; - weight[j] = xwn < -halfwidth || xwn > halfwidth? 0 : pf[xwn]; - } - if(weight[1] == 0.0) - { - out<<"result += vec4("<<weight[2]<<","<<weight[0]<<","<<weight[2]<<","<<weight[0]<<")*pc.grab;\n"; - } - else - { - out<<"result += vec4("<<weight[1]<<", "<<weight[0]<<", "<<weight[1]<<", "<<weight[0]<<")*pc.rrbb;\n"; - out<<"result += vec4("<<weight[2]<<", "<<weight[1]<<", "<<weight[2]<<", "<<weight[1]<<")*pc.ggaa;\n"; - } - - } - out<<"gl_FragColor = result;}\n"<<'\0'; - - return new ProgramGLSL(out.str().c_str()); - - -} - - -ProgramGPU* FilterGLSL::CreateFilterVPK(float kernel[], int height) -{ - - //both h and v are packed... - int i, j, yw, ywn; - - int halfh = height >>1; - float * pf = kernel + halfh; - int nhpixel = (halfh+1)>>1; //how many neighbour pixels need to be looked up - int npixel = (nhpixel<<1)+1;// - float weight[3]; - ostringstream out;; - out<<setprecision(8); - - out<< "uniform sampler2DRect tex;"; - out<< "\nvoid main(void){ vec4 result = vec4(0, 0, 0, 0);\n"; - ///use multi texture coordinate because nhpixels can be at most 3 - out<<"vec4 pc; vec2 coord;\n"; - for( i = 0 ; i < npixel ; i++) - { - out<<"coord = gl_TexCoord[0].xy + vec2(0, float("<<i-nhpixel<<"));\n"; - out<<"pc=texture2DRect(tex, coord);\n"; - if(GlobalUtil::_PreciseBorder) out<<"if(coord.y < 0.0) pc = pc.rgrg;\n"; - - //for each sub-pixel j in center, the weight of sub-pixel k - yw = (i - nhpixel)*2; - for( j = 0; j < 3; j++) - { - ywn = yw + j -1; - weight[j] = ywn < -halfh || ywn > halfh? 0 : pf[ywn]; - } - if(weight[1] == 0.0) - { - out<<"result += vec4("<<weight[2]<<","<<weight[2]<<","<<weight[0]<<","<<weight[0]<<")*pc.barg;\n"; - }else - { - out<<"result += vec4("<<weight[1]<<","<<weight[1]<<","<<weight[0]<<","<<weight[0]<<")*pc.rgrg;\n"; - out<<"result += vec4("<<weight[2]<<","<<weight[2]<<","<<weight[1]<<","<<weight[1]<<")*pc.baba;\n"; - } - } - out<<"gl_FragColor = result;}\n"<<'\0'; - - return new ProgramGLSL(out.str().c_str()); -} - - - -ShaderBag::ShaderBag() -{ - s_debug = 0; - s_orientation = 0; - s_display_gaussian = 0; - s_display_dog = 0; - s_display_grad = 0; - s_display_keys = 0; - s_sampling = 0; - s_grad_pass = 0; - s_dog_pass = 0; - s_keypoint = 0; - s_genlist_init_tight = 0; - s_genlist_init_ex = 0; - s_genlist_histo = 0; - s_genlist_start = 0; - s_genlist_step = 0; - s_genlist_end = 0; - s_vertex_list = 0; - s_descriptor_fp = 0; - s_margin_copy = 0; - //////////// - f_gaussian_skip0 = NULL; - f_gaussian_skip1 = NULL; - f_gaussian_step = NULL; - _gaussian_step_num = 0; - -} - -ShaderBag::~ShaderBag() -{ - if(s_debug)delete s_debug; - if(s_orientation)delete s_orientation; - if(s_display_gaussian)delete s_display_gaussian; - if(s_display_dog)delete s_display_dog; - if(s_display_grad)delete s_display_grad; - if(s_display_keys)delete s_display_keys; - if(s_sampling)delete s_sampling; - if(s_grad_pass)delete s_grad_pass; - if(s_dog_pass) delete s_dog_pass; - if(s_keypoint)delete s_keypoint; - if(s_genlist_init_tight)delete s_genlist_init_tight; - if(s_genlist_init_ex)delete s_genlist_init_ex; - if(s_genlist_histo)delete s_genlist_histo; - if(s_genlist_start)delete s_genlist_start; - if(s_genlist_step)delete s_genlist_step; - if(s_genlist_end)delete s_genlist_end; - if(s_vertex_list)delete s_vertex_list; - if(s_descriptor_fp)delete s_descriptor_fp; - if(s_margin_copy) delete s_margin_copy; - - ////////////////////////////////////////////// - if(f_gaussian_skip1) delete f_gaussian_skip1; - - for(unsigned int i = 0; i < f_gaussian_skip0_v.size(); i++) - { - if(f_gaussian_skip0_v[i]) delete f_gaussian_skip0_v[i]; - } - if(f_gaussian_step && _gaussian_step_num > 0) - { - for(int i = 0; i< _gaussian_step_num; i++) - { - delete f_gaussian_step[i]; - } - delete[] f_gaussian_step; - } -} - - -void ShaderBag::SelectInitialSmoothingFilter(int octave_min, SiftParam&param) -{ - float sigma = param.GetInitialSmoothSigma(octave_min); - if(sigma == 0) - { - f_gaussian_skip0 = NULL; - }else - { - for(unsigned int i = 0; i < f_gaussian_skip0_v.size(); i++) - { - if(f_gaussian_skip0_v[i]->_id == octave_min) - { - f_gaussian_skip0 = f_gaussian_skip0_v[i]; - return ; - } - } - FilterGLSL * filter = new FilterGLSL(sigma); - filter->_id = octave_min; - f_gaussian_skip0_v.push_back(filter); - f_gaussian_skip0 = filter; - } -} - -void ShaderBag::CreateGaussianFilters(SiftParam&param) -{ - if(param._sigma_skip0>0.0f) - { - FilterGLSL * filter; - f_gaussian_skip0 = filter = new FilterGLSL(param._sigma_skip0); - filter->_id = GlobalUtil::_octave_min_default; - f_gaussian_skip0_v.push_back(filter); - } - if(param._sigma_skip1>0.0f) - { - f_gaussian_skip1 = new FilterGLSL(param._sigma_skip1); - } - - f_gaussian_step = new FilterProgram*[param._sigma_num]; - for(int i = 0; i< param._sigma_num; i++) - { - f_gaussian_step[i] = new FilterGLSL(param._sigma[i]); - } - _gaussian_step_num = param._sigma_num; -} - - -void ShaderBag::LoadDynamicShaders(SiftParam& param) -{ - LoadKeypointShader(param._dog_threshold, param._edge_threshold); - LoadGenListShader(param._dog_level_num, 0); - CreateGaussianFilters(param); -} - - -void ShaderBagGLSL::LoadFixedShaders() -{ - - - s_gray = new ProgramGLSL( - "uniform sampler2DRect tex; void main(void){\n" - "float intensity = dot(vec3(0.299, 0.587, 0.114), texture2DRect(tex, gl_TexCoord[0].st ).rgb);\n" - "gl_FragColor = vec4(intensity, intensity, intensity, 1.0);}"); - - - s_debug = new ProgramGLSL( "void main(void){gl_FragColor.rg = gl_TexCoord[0].st;}"); - - - s_sampling = new ProgramGLSL( - "uniform sampler2DRect tex; void main(void){gl_FragColor.rg= texture2DRect(tex, gl_TexCoord[0].st).rg;}"); - - // - s_grad_pass = new ProgramGLSL( - "uniform sampler2DRect tex; void main ()\n" - "{\n" - " vec4 v1, v2, gg;\n" - " vec4 cc = texture2DRect(tex, gl_TexCoord[0].xy);\n" - " gg.x = texture2DRect(tex, gl_TexCoord[1].xy).r;\n" - " gg.y = texture2DRect(tex, gl_TexCoord[2].xy).r;\n" - " gg.z = texture2DRect(tex, gl_TexCoord[3].xy).r;\n" - " gg.w = texture2DRect(tex, gl_TexCoord[4].xy).r;\n" - " vec2 dxdy = (gg.yw - gg.xz); \n" - " float grad = 0.5*length(dxdy);\n" - " float theta = grad==0.0? 0.0: atan(dxdy.y, dxdy.x);\n" - " gl_FragData[0] = vec4(cc.rg, grad, theta);\n" - "}\n\0"); - - ProgramGLSL * program; - s_margin_copy = program = new ProgramGLSL( - "uniform sampler2DRect tex; uniform vec2 truncate;\n" - "void main(){ gl_FragColor = texture2DRect(tex, min(gl_TexCoord[0].xy, truncate)); }"); - - _param_margin_copy_truncate = glGetUniformLocation(*program, "truncate"); - - - GlobalUtil::_OrientationPack2 = 0; - LoadOrientationShader(); - - if(s_orientation == NULL) - { - //Load a simplified version if the right version is not supported - s_orientation = program = new ProgramGLSL( - "uniform sampler2DRect tex; uniform sampler2DRect oTex;\n" - " uniform float size; void main(){\n" - " vec4 cc = texture2DRect(tex, gl_TexCoord[0].st);\n" - " vec4 oo = texture2DRect(oTex, cc.rg);\n" - " gl_FragColor.rg = cc.rg;\n" - " gl_FragColor.b = oo.a;\n" - " gl_FragColor.a = size;}"); - - _param_orientation_gtex = glGetUniformLocation(*program, "oTex"); - _param_orientation_size = glGetUniformLocation(*program, "size"); - GlobalUtil::_MaxOrientation = 0; - GlobalUtil::_FullSupported = 0; - std::cerr<<"Orientation simplified on this hardware"<<endl; - } - - if(GlobalUtil::_DescriptorPPT) LoadDescriptorShader(); - if(s_descriptor_fp == NULL) - { - GlobalUtil::_DescriptorPPT = GlobalUtil::_FullSupported = 0; - std::cerr<<"Descriptor ignored on this hardware"<<endl; - } - - s_zero_pass = new ProgramGLSL("void main(){gl_FragColor = vec4(0.0);}"); -} - - -void ShaderBagGLSL::LoadDisplayShaders() -{ - s_copy_key = new ProgramGLSL( - "uniform sampler2DRect tex; void main(){\n" - "gl_FragColor.rg= texture2DRect(tex, gl_TexCoord[0].st).rg; gl_FragColor.ba = vec2(0.0,1.0); }"); - - - ProgramGLSL * program; - s_vertex_list = program = new ProgramGLSL( - "uniform vec4 sizes; uniform sampler2DRect tex;\n" - "void main(void){\n" - "float fwidth = sizes.y; float twidth = sizes.z; float rwidth = sizes.w; \n" - "float index = 0.1*(fwidth*floor(gl_TexCoord[0].y) + gl_TexCoord[0].x);\n" - "float px = mod(index, twidth);\n" - "vec2 tpos= floor(vec2(px, index*rwidth))+0.5;\n" - "vec4 cc = texture2DRect(tex, tpos );\n" - "float size = 3.0 * cc.a; //sizes.x;// \n" - "gl_FragColor.zw = vec2(0.0, 1.0);\n" - "if(any(lessThan(cc.xy,vec2(0.0)))) {gl_FragColor.xy = cc.xy; }\n" - "else {float type = fract(px);\n" - "vec2 dxy = vec2(0); \n" - "dxy.x = type < 0.1 ? 0.0 : (((type <0.5) || (type > 0.9))? size : -size);\n" - "dxy.y = type < 0.2 ? 0.0 : (((type < 0.3) || (type > 0.7) )? -size :size); \n" - "float s = sin(cc.b); float c = cos(cc.b); \n" - "gl_FragColor.x = cc.x + c*dxy.x-s*dxy.y;\n" - "gl_FragColor.y = cc.y + c*dxy.y+s*dxy.x;}\n}\n"); - - _param_genvbo_size = glGetUniformLocation(*program, "sizes"); - - s_display_gaussian = new ProgramGLSL( - "uniform sampler2DRect tex; void main(void){float r = texture2DRect(tex, gl_TexCoord[0].st).r;\n" - "gl_FragColor = vec4(r, r, r, 1);}" ); - - s_display_dog = new ProgramGLSL( - "uniform sampler2DRect tex; void main(void){float g = 0.5+(20.0*texture2DRect(tex, gl_TexCoord[0].st).g);\n" - "gl_FragColor = vec4(g, g, g, 0.0);}" ); - - s_display_grad = new ProgramGLSL( - "uniform sampler2DRect tex; void main(void){\n" - " vec4 cc = texture2DRect(tex, gl_TexCoord[0].st);gl_FragColor = vec4(5.0* cc.bbb, 1.0);}"); - - s_display_keys= new ProgramGLSL( - "uniform sampler2DRect tex; void main(void){\n" - " vec4 cc = texture2DRect(tex, gl_TexCoord[0].st);\n" - " if(cc.r ==0.0) discard; gl_FragColor = (cc.r==1.0? vec4(1.0, 0.0, 0,1.0):vec4(0.0,1.0,0.0,1.0));}"); -} - -void ShaderBagGLSL::LoadKeypointShader(float threshold, float edge_threshold) -{ - float threshold0 = threshold* (GlobalUtil::_SubpixelLocalization?0.8f:1.0f); - float threshold1 = threshold; - float threshold2 = (edge_threshold+1)*(edge_threshold+1)/edge_threshold; - ostringstream out;; - streampos pos; - - //tex(X)(Y) - //X: (CLR) (CENTER 0, LEFT -1, RIGHT +1) - //Y: (CDU) (CENTER 0, DOWN -1, UP +1) - if(GlobalUtil::_DarknessAdaption) - { - out << "#define THRESHOLD0 (" << threshold0 << " * min(2.0 * cc.r + 0.1, 1.0))\n" - "#define THRESHOLD1 (" << threshold1 << " * min(2.0 * cc.r + 0.1, 1.0))\n" - "#define THRESHOLD2 " << threshold2 << "\n"; - }else - { - out << "#define THRESHOLD0 " << threshold0 << "\n" - "#define THRESHOLD1 " << threshold1 << "\n" - "#define THRESHOLD2 " << threshold2 << "\n"; - } - - out<< - "uniform sampler2DRect tex, texU, texD; void main ()\n" - "{\n" - " vec4 v1, v2, gg, temp;\n" - " vec2 TexRU = vec2(gl_TexCoord[2].x, gl_TexCoord[4].y); \n" - " vec4 cc = texture2DRect(tex, gl_TexCoord[0].xy);\n" - " temp = texture2DRect(tex, gl_TexCoord[1].xy);\n" - " v1.x = temp.g; gg.x = temp.r;\n" - " temp = texture2DRect(tex, gl_TexCoord[2].xy) ;\n" - " v1.y = temp.g; gg.y = temp.r;\n" - " temp = texture2DRect(tex, gl_TexCoord[3].xy) ;\n" - " v1.z = temp.g; gg.z = temp.r;\n" - " temp = texture2DRect(tex, gl_TexCoord[4].xy) ;\n" - " v1.w = temp.g; gg.w = temp.r;\n" - " v2.x = texture2DRect(tex, gl_TexCoord[5].xy).g;\n" - " v2.y = texture2DRect(tex, gl_TexCoord[6].xy).g;\n" - " v2.z = texture2DRect(tex, gl_TexCoord[7].xy).g;\n" - " v2.w = texture2DRect(tex, TexRU.xy).g;\n" - " vec2 dxdy = (gg.yw - gg.xz); \n" - " float grad = 0.5*length(dxdy);\n" - " float theta = grad==0.0? 0.0: atan(dxdy.y, dxdy.x);\n" - " gl_FragData[0] = vec4(cc.rg, grad, theta);\n" - - //test against 8 neighbours - //use variable to identify type of extremum - //1.0 for local maximum and 0.5 for minimum - << - " float dog = 0.0; \n" - " gl_FragData[1] = vec4(0, 0, 0, 0); \n" - " dog = cc.g > float(THRESHOLD0) && all(greaterThan(cc.gggg, max(v1, v2)))?1.0: 0.0;\n" - " dog = cc.g < float(-THRESHOLD0) && all(lessThan(cc.gggg, min(v1, v2)))?0.5: dog;\n" - " if(dog == 0.0) return;\n"; - - pos = out.tellp(); - //do edge supression first.. - //vector v1 is < (-1, 0), (1, 0), (0,-1), (0, 1)> - //vector v2 is < (-1,-1), (-1,1), (1,-1), (1, 1)> - - out<< - " float fxx, fyy, fxy; \n" - " vec4 D2 = v1.xyzw - cc.gggg;\n" - " vec2 D4 = v2.xw - v2.yz;\n" - " fxx = D2.x + D2.y;\n" - " fyy = D2.z + D2.w;\n" - " fxy = 0.25*(D4.x + D4.y);\n" - " float fxx_plus_fyy = fxx + fyy;\n" - " float score_up = fxx_plus_fyy*fxx_plus_fyy; \n" - " float score_down = (fxx*fyy - fxy*fxy);\n" - " if( score_down <= 0.0 || score_up > THRESHOLD2 * score_down)return;\n"; - - //... - out<<" \n" - " vec2 D5 = 0.5*(v1.yw-v1.xz); \n" - " float fx = D5.x, fy = D5.y ; \n" - " float fs, fss , fxs, fys ; \n" - " vec2 v3; vec4 v4, v5, v6;\n" - //read 9 pixels of upper level - << - " v3.x = texture2DRect(texU, gl_TexCoord[0].xy).g;\n" - " v4.x = texture2DRect(texU, gl_TexCoord[1].xy).g;\n" - " v4.y = texture2DRect(texU, gl_TexCoord[2].xy).g;\n" - " v4.z = texture2DRect(texU, gl_TexCoord[3].xy).g;\n" - " v4.w = texture2DRect(texU, gl_TexCoord[4].xy).g;\n" - " v6.x = texture2DRect(texU, gl_TexCoord[5].xy).g;\n" - " v6.y = texture2DRect(texU, gl_TexCoord[6].xy).g;\n" - " v6.z = texture2DRect(texU, gl_TexCoord[7].xy).g;\n" - " v6.w = texture2DRect(texU, TexRU.xy).g;\n" - //compare with 9 pixels of upper level - //read and compare with 9 pixels of lower level - //the maximum case - << - " if(dog == 1.0)\n" - " {\n" - " if(cc.g < v3.x || any(lessThan(cc.gggg, v4)) ||any(lessThan(cc.gggg, v6)))return; \n" - " v3.y = texture2DRect(texD, gl_TexCoord[0].xy).g;\n" - " v5.x = texture2DRect(texD, gl_TexCoord[1].xy).g;\n" - " v5.y = texture2DRect(texD, gl_TexCoord[2].xy).g;\n" - " v5.z = texture2DRect(texD, gl_TexCoord[3].xy).g;\n" - " v5.w = texture2DRect(texD, gl_TexCoord[4].xy).g;\n" - " v6.x = texture2DRect(texD, gl_TexCoord[5].xy).g;\n" - " v6.y = texture2DRect(texD, gl_TexCoord[6].xy).g;\n" - " v6.z = texture2DRect(texD, gl_TexCoord[7].xy).g;\n" - " v6.w = texture2DRect(texD, TexRU.xy).g;\n" - " if(cc.g < v3.y || any(lessThan(cc.gggg, v5)) ||any(lessThan(cc.gggg, v6)))return; \n" - " }\n" - //the minimum case - << - " else{\n" - " if(cc.g > v3.x || any(greaterThan(cc.gggg, v4)) ||any(greaterThan(cc.gggg, v6)))return; \n" - " v3.y = texture2DRect(texD, gl_TexCoord[0].xy).g;\n" - " v5.x = texture2DRect(texD, gl_TexCoord[1].xy).g;\n" - " v5.y = texture2DRect(texD, gl_TexCoord[2].xy).g;\n" - " v5.z = texture2DRect(texD, gl_TexCoord[3].xy).g;\n" - " v5.w = texture2DRect(texD, gl_TexCoord[4].xy).g;\n" - " v6.x = texture2DRect(texD, gl_TexCoord[5].xy).g;\n" - " v6.y = texture2DRect(texD, gl_TexCoord[6].xy).g;\n" - " v6.z = texture2DRect(texD, gl_TexCoord[7].xy).g;\n" - " v6.w = texture2DRect(texD, TexRU.xy).g;\n" - " if(cc.g > v3.y || any(greaterThan(cc.gggg, v5)) ||any(greaterThan(cc.gggg, v6)))return; \n" - " }\n"; - - if(GlobalUtil::_SubpixelLocalization) - - // sub-pixel localization FragData1 = vec4(dog, 0, 0, 0); return; - out << - " fs = 0.5*( v3.x - v3.y ); \n" - " fss = v3.x + v3.y - cc.g - cc.g;\n" - " fxs = 0.25 * ( v4.y + v5.x - v4.x - v5.y);\n" - " fys = 0.25 * ( v4.w + v5.z - v4.z - v5.w);\n" - - // - // let dog difference be quatratic function of dx, dy, ds; - // df(dx, dy, ds) = fx * dx + fy*dy + fs * ds + - // + 0.5 * ( fxx * dx * dx + fyy * dy * dy + fss * ds * ds) - // + (fxy * dx * dy + fxs * dx * ds + fys * dy * ds) - // (fx, fy, fs, fxx, fyy, fss, fxy, fxs, fys are the derivatives) - - //the local extremum satisfies - // df/dx = 0, df/dy = 0, df/dz = 0 - - //that is - // |-fx| | fxx fxy fxs | |dx| - // |-fy| = | fxy fyy fys | * |dy| - // |-fs| | fxs fys fss | |ds| - // need to solve dx, dy, ds - - // Use Gauss elimination to solve the linear system - << - " vec3 dxys = vec3(0.0); \n" - " vec4 A0, A1, A2 ; \n" - " A0 = vec4(fxx, fxy, fxs, -fx); \n" - " A1 = vec4(fxy, fyy, fys, -fy); \n" - " A2 = vec4(fxs, fys, fss, -fs); \n" - " vec3 x3 = abs(vec3(fxx, fxy, fxs)); \n" - " float maxa = max(max(x3.x, x3.y), x3.z); \n" - " if(maxa >= 1e-10 ) { \n" - " if(x3.y ==maxa ) \n" - " { \n" - " vec4 TEMP = A1; A1 = A0; A0 = TEMP; \n" - " }else if( x3.z == maxa ) \n" - " { \n" - " vec4 TEMP = A2; A2 = A0; A0 = TEMP; \n" - " } \n" - " A0 /= A0.x; \n" - " A1 -= A1.x * A0; \n" - " A2 -= A2.x * A0; \n" - " vec2 x2 = abs(vec2(A1.y, A2.y)); \n" - " if( x2.y > x2.x ) \n" - " { \n" - " vec3 TEMP = A2.yzw; \n" - " A2.yzw = A1.yzw; \n" - " A1.yzw = TEMP; \n" - " x2.x = x2.y; \n" - " } \n" - " if(x2.x >= 1e-10) { \n" - " A1.yzw /= A1.y; \n" - " A2.yzw -= A2.y * A1.yzw; \n" - " if(abs(A2.z) >= 1e-10) { \n" - // compute dx, dy, ds: - << - " \n" - " dxys.z = A2.w /A2.z; \n" - " dxys.y = A1.w - dxys.z*A1.z; \n" - " dxys.x = A0.w - dxys.z*A0.z - dxys.y*A0.y; \n" - - //one more threshold which I forgot in versions prior to 286 - << - " bool dog_test = (abs(cc.g + 0.5*dot(vec3(fx, fy, fs), dxys ))<= float(THRESHOLD1)) ;\n" - " if(dog_test || any(greaterThan(abs(dxys), vec3(1.0)))) dog = 0.0;\n" - " }\n" - " }\n" - " }\n" - //keep the point when the offset is less than 1 - << - " gl_FragData[1] = vec4( dog, dxys); \n"; - else - - out<< - " gl_FragData[1] = vec4( dog, 0.0, 0.0, 0.0) ; \n"; - - out<< - "}\n" <<'\0'; - - - - ProgramGLSL * program = new ProgramGLSL(out.str().c_str()); - if(program->IsNative()) - { - s_keypoint = program ; - //parameter - }else - { - delete program; - out.seekp(pos); - out << - " gl_FragData[1] = vec4(dog, 0.0, 0.0, 0.0) ; \n" - "}\n" <<'\0'; - s_keypoint = program = new ProgramGLSL(out.str().c_str()); - GlobalUtil::_SubpixelLocalization = 0; - std::cerr<<"Detection simplified on this hardware"<<endl; - } - - _param_dog_texu = glGetUniformLocation(*program, "texU"); - _param_dog_texd = glGetUniformLocation(*program, "texD"); -} - - -void ShaderBagGLSL::SetDogTexParam(int texU, int texD) -{ - glUniform1i(_param_dog_texu, 1); - glUniform1i(_param_dog_texd, 2); -} - -void ShaderBagGLSL::SetGenListStepParam(int tex, int tex0) -{ - glUniform1i(_param_genlist_step_tex0, 1); -} -void ShaderBagGLSL::SetGenVBOParam( float width, float fwidth, float size) -{ - float sizes[4] = {size*3.0f, fwidth, width, 1.0f/width}; - glUniform4fv(_param_genvbo_size, 1, sizes); - -} - - - -void ShaderBagGLSL::UnloadProgram() -{ - glUseProgram(0); -} - - - -void ShaderBagGLSL::LoadGenListShader(int ndoglev, int nlev) -{ - ProgramGLSL * program; - - s_genlist_init_tight = new ProgramGLSL( - "uniform sampler2DRect tex; void main (void){\n" - "vec4 helper = vec4( texture2DRect(tex, gl_TexCoord[0].xy).r, texture2DRect(tex, gl_TexCoord[1].xy).r,\n" - "texture2DRect(tex, gl_TexCoord[2].xy).r, texture2DRect(tex, gl_TexCoord[3].xy).r);\n" - "gl_FragColor = vec4(greaterThan(helper, vec4(0.0,0.0,0.0,0.0)));\n" - "}"); - - - s_genlist_init_ex = program = new ProgramGLSL( - "uniform sampler2DRect tex;uniform vec2 bbox;\n" - "void main (void ){\n" - "vec4 helper = vec4( texture2DRect(tex, gl_TexCoord[0].xy).r, texture2DRect(tex, gl_TexCoord[1].xy).r,\n" - "texture2DRect(tex, gl_TexCoord[2].xy).r, texture2DRect(tex, gl_TexCoord[3].xy).r);\n" - "bvec4 helper2 = bvec4( \n" - "all(lessThan(gl_TexCoord[0].xy , bbox)) && helper.x >0.0,\n" - "all(lessThan(gl_TexCoord[1].xy , bbox)) && helper.y >0.0,\n" - "all(lessThan(gl_TexCoord[2].xy , bbox)) && helper.z >0.0,\n" - "all(lessThan(gl_TexCoord[3].xy , bbox)) && helper.w >0.0);\n" - "gl_FragColor = vec4(helper2);\n" - "}"); - _param_genlist_init_bbox = glGetUniformLocation( *program, "bbox"); - - - //reduction ... - s_genlist_histo = new ProgramGLSL( - "uniform sampler2DRect tex; void main (void){\n" - "vec4 helper; vec4 helper2; \n" - "helper = texture2DRect(tex, gl_TexCoord[0].xy); helper2.xy = helper.xy + helper.zw; \n" - "helper = texture2DRect(tex, gl_TexCoord[1].xy); helper2.zw = helper.xy + helper.zw; \n" - "gl_FragColor.rg = helper2.xz + helper2.yw;\n" - "helper = texture2DRect(tex, gl_TexCoord[2].xy); helper2.xy = helper.xy + helper.zw; \n" - "helper = texture2DRect(tex, gl_TexCoord[3].xy); helper2.zw = helper.xy + helper.zw; \n" - "gl_FragColor.ba= helper2.xz+helper2.yw;\n" - "}"); - - - //read of the first part, which generates tex coordinates - s_genlist_start= program = LoadGenListStepShader(1, 1); - _param_ftex_width= glGetUniformLocation(*program, "width"); - _param_genlist_start_tex0 = glGetUniformLocation(*program, "tex0"); - //stepping - s_genlist_step = program = LoadGenListStepShader(0, 1); - _param_genlist_step_tex0= glGetUniformLocation(*program, "tex0"); - -} - -void ShaderBagGLSL::SetMarginCopyParam(int xmax, int ymax) -{ - float truncate[2] = {xmax - 0.5f , ymax - 0.5f}; - glUniform2fv(_param_margin_copy_truncate, 1, truncate); -} - -void ShaderBagGLSL::SetGenListInitParam(int w, int h) -{ - float bbox[2] = {w - 1.0f, h - 1.0f}; - glUniform2fv(_param_genlist_init_bbox, 1, bbox); -} -void ShaderBagGLSL::SetGenListStartParam(float width, int tex0) -{ - glUniform1f(_param_ftex_width, width); - glUniform1i(_param_genlist_start_tex0, 0); -} - - -ProgramGLSL* ShaderBagGLSL::LoadGenListStepShader(int start, int step) -{ - int i; - // char chanels[5] = "rgba"; - ostringstream out; - - for(i = 0; i < step; i++) out<<"uniform sampler2DRect tex"<<i<<";\n"; - if(start) - { - out<<"uniform float width;\n"; - out<<"void main(void){\n"; - out<<"float index = floor(gl_TexCoord[0].y) * width + floor(gl_TexCoord[0].x);\n"; - out<<"vec2 pos = vec2(0.5, 0.5);\n"; - }else - { - out<<"uniform sampler2DRect tex;\n"; - out<<"void main(void){\n"; - out<<"vec4 tc = texture2DRect( tex, gl_TexCoord[0].xy);\n"; - out<<"vec2 pos = tc.rg; float index = tc.b;\n"; - } - out<<"vec2 sum; vec4 cc;\n"; - - - if(step>0) - { - out<<"vec2 cpos = vec2(-0.5, 0.5);\t vec2 opos;\n"; - for(i = 0; i < step; i++) - { - - out<<"cc = texture2DRect(tex"<<i<<", pos);\n"; - out<<"sum.x = cc.r + cc.g; sum.y = sum.x + cc.b; \n"; - out<<"if (index <cc.r){ opos = cpos.xx;}\n"; - out<<"else if(index < sum.x ) {opos = cpos.yx; index -= cc.r;}\n"; - out<<"else if(index < sum.y ) {opos = cpos.xy; index -= sum.x;}\n"; - out<<"else {opos = cpos.yy; index -= sum.y;}\n"; - out<<"pos = (pos + pos + opos);\n"; - } - } - out<<"gl_FragColor = vec4(pos, index, 1.0);\n"; - out<<"}\n"<<'\0'; - return new ProgramGLSL(out.str().c_str()); -} - - -void ShaderBagGLSL::LoadOrientationShader() -{ - ostringstream out; - - if(GlobalUtil::_IsNvidia) - { - out << "#pragma optionNV(ifcvt none)\n" - "#pragma optionNV(unroll all)\n"; - } - - out<<"\n" - "#define GAUSSIAN_WF float("<<GlobalUtil::_OrientationGaussianFactor<<") \n" - "#define SAMPLE_WF float("<<GlobalUtil::_OrientationWindowFactor<< " )\n" - "#define ORIENTATION_THRESHOLD "<< GlobalUtil::_MulitiOrientationThreshold << "\n" - "uniform sampler2DRect tex; \n" - "uniform sampler2DRect gradTex; \n" - "uniform vec4 size; \n" - << ((GlobalUtil::_SubpixelLocalization || GlobalUtil::_KeepExtremumSign)? " uniform sampler2DRect texS; \n" : " ") << - "void main() \n" - "{ \n" - " vec4 bins[10]; \n" - " bins[0] = vec4(0.0);bins[1] = vec4(0.0);bins[2] = vec4(0.0); \n" - " bins[3] = vec4(0.0);bins[4] = vec4(0.0);bins[5] = vec4(0.0); \n" - " bins[6] = vec4(0.0);bins[7] = vec4(0.0);bins[8] = vec4(0.0); \n" - " vec4 loc = texture2DRect(tex, gl_TexCoord[0].xy); \n" - " vec2 pos = loc.xy; \n" - " bool orientation_mode = (size.z != 0.0); \n" - " float sigma = orientation_mode? abs(size.z) : loc.w; \n"; - if(GlobalUtil::_SubpixelLocalization || GlobalUtil::_KeepExtremumSign) - { - out<< - " if(orientation_mode){\n" - " vec4 offset = texture2DRect(texS, pos);\n" - " pos.xy = pos.xy + offset.yz; \n" - " sigma = sigma * pow(size.w, offset.w);\n" - " #if "<< GlobalUtil::_KeepExtremumSign << "\n" - " if(offset.x < 0.6) sigma = -sigma; \n" - " #endif\n" - " }\n"; - } - out<< - " //bool fixed_orientation = (size.z < 0.0); \n" - " if(size.z < 0.0) {gl_FragData[0] = vec4(pos, 0.0, sigma); return;}" - " float gsigma = sigma * GAUSSIAN_WF; \n" - " vec2 win = abs(vec2(sigma * (SAMPLE_WF * GAUSSIAN_WF))) ; \n" - " vec2 dim = size.xy; \n" - " float dist_threshold = win.x*win.x+0.5; \n" - " float factor = -0.5/(gsigma*gsigma); \n" - " vec4 sz; vec2 spos; \n" - " //if(any(pos.xy <= 1)) discard; \n" - " sz.xy = max( pos - win, vec2(1,1)); \n" - " sz.zw = min( pos + win, dim-vec2(2, 2)); \n" - " sz = floor(sz)+0.5;"; - //loop to get the histogram - - out<<"\n" - " for(spos.y = sz.y; spos.y <= sz.w; spos.y+=1.0) \n" - " { \n" - " for(spos.x = sz.x; spos.x <= sz.z; spos.x+=1.0) \n" - " { \n" - " vec2 offset = spos - pos; \n" - " float sq_dist = dot(offset,offset); \n" - " if( sq_dist < dist_threshold){ \n" - " vec4 cc = texture2DRect(gradTex, spos); \n" - " float grad = cc.b; float theta = cc.a; \n" - " float idx = floor(degrees(theta)*0.1); \n" - " if(idx < 0.0 ) idx += 36.0; \n" - " float weight = grad*exp(sq_dist * factor); \n" - " float vidx = fract(idx * 0.25) * 4.0;//mod(idx, 4.0) ; \n" - " vec4 inc = weight*vec4(equal(vec4(vidx), vec4(0.0,1.0,2.0,3.0)));"; - - if(GlobalUtil::_UseDynamicIndexing) - { - //dynamic indexing may not be faster - out<<"\n" - " int iidx = int((idx*0.25)); \n" - " bins[iidx]+=inc; \n" - " } \n" - " } \n" - " }"; - - }else - { - //nvfp40 still does not support dynamic array indexing - //unrolled binary search... - out<<"\n" - " if(idx < 16.0) \n" - " { \n" - " if(idx < 8.0) \n" - " { \n" - " if(idx < 4.0) { bins[0]+=inc;} \n" - " else { bins[1]+=inc;} \n" - " }else \n" - " { \n" - " if(idx < 12.0){ bins[2]+=inc;} \n" - " else { bins[3]+=inc;} \n" - " } \n" - " }else if(idx < 32.0) \n" - " { \n" - " if(idx < 24.0) \n" - " { \n" - " if(idx <20.0) { bins[4]+=inc;} \n" - " else { bins[5]+=inc;} \n" - " }else \n" - " { \n" - " if(idx < 28.0){ bins[6]+=inc;} \n" - " else { bins[7]+=inc;} \n" - " } \n" - " }else \n" - " { \n" - " bins[8]+=inc; \n" - " } \n" - " } \n" - " } \n" - " }"; - - } - - WriteOrientationCodeToStream(out); - - ProgramGLSL * program = new ProgramGLSL(out.str().c_str()); - if(program->IsNative()) - { - s_orientation = program ; - _param_orientation_gtex = glGetUniformLocation(*program, "gradTex"); - _param_orientation_size = glGetUniformLocation(*program, "size"); - _param_orientation_stex = glGetUniformLocation(*program, "texS"); - }else - { - delete program; - } -} - - -void ShaderBagGLSL::WriteOrientationCodeToStream(std::ostream& out) -{ - //smooth histogram and find the largest -/* - smoothing kernel: (1 3 6 7 6 3 1 )/27 - the same as 3 pass of (1 1 1)/3 averaging - maybe better to use 4 pass on the vectors... -*/ - - - //the inner loop on different array numbers is always unrolled in fp40 - - //bug fixed here:) - out<<"\n" - " //mat3 m1 = mat3(1, 0, 0, 3, 1, 0, 6, 3, 1)/27.0; \n" - " mat3 m1 = mat3(1, 3, 6, 0, 1, 3,0, 0, 1)/27.0; \n" - " mat4 m2 = mat4(7, 6, 3, 1, 6, 7, 6, 3, 3, 6, 7, 6, 1, 3, 6, 7)/27.0;\n" - " #define FILTER_CODE(i) { \\\n" - " vec4 newb = (bins[i]* m2); \\\n" - " newb.xyz += ( prev.yzw * m1); \\\n" - " prev = bins[i]; \\\n" - " newb.wzy += ( bins[i+1].zyx *m1); \\\n" - " bins[i] = newb;}\n" - " for (int j=0; j<2; j++) \n" - " { \n" - " vec4 prev = bins[8]; \n" - " bins[9] = bins[0]; \n"; - - if(GlobalUtil::_KeepShaderLoop) - { - out<< - " for (int i=0; i<9; i++) \n" - " { \n" - " FILTER_CODE(i); \n" - " } \n" - " }"; - - }else - { - //manually unroll the loop for ATI. - out << - " FILTER_CODE(0);\n" - " FILTER_CODE(1);\n" - " FILTER_CODE(2);\n" - " FILTER_CODE(3);\n" - " FILTER_CODE(4);\n" - " FILTER_CODE(5);\n" - " FILTER_CODE(6);\n" - " FILTER_CODE(7);\n" - " FILTER_CODE(8);\n" - " }\n"; - } - //find the maximum voting - out<<"\n" - " vec4 maxh; vec2 maxh2; \n" - " vec4 maxh4 = max(max(max(max(max(max(max(max(bins[0], bins[1]), bins[2]), \n" - " bins[3]), bins[4]), bins[5]), bins[6]), bins[7]), bins[8]);\n" - " maxh2 = max(maxh4.xy, maxh4.zw); maxh = vec4(max(maxh2.x, maxh2.y));"; - - char *testpeak_code; - char *savepeak_code; - - //save two/three/four orientations with the largest votings? - - if(GlobalUtil::_MaxOrientation>1) - { - out<<"\n" - " vec4 Orientations = vec4(0.0, 0.0, 0.0, 0.0); \n" - " vec4 weights = vec4(0.0,0.0,0.0,0.0); "; - - testpeak_code = "\\\n" - " {test = greaterThan(bins[i], hh);"; - - //save the orientations in weight-decreasing order - if(GlobalUtil::_MaxOrientation ==2) - { - savepeak_code = "\\\n" - " if(weight <=weights.g){}\\\n" - " else if(weight >weights.r)\\\n" - " {weights.rg = vec2(weight, weights.r); Orientations.rg = vec2(th, Orientations.r);}\\\n" - " else {weights.g = weight; Orientations.g = th;}"; - }else if(GlobalUtil::_MaxOrientation ==3) - { - savepeak_code = "\\\n" - " if(weight <=weights.b){}\\\n" - " else if(weight >weights.r)\\\n" - " {weights.rgb = vec3(weight, weights.rg); Orientations.rgb = vec3(th, Orientations.rg);}\\\n" - " else if(weight >weights.g)\\\n" - " {weights.gb = vec2(weight, weights.g); Orientations.gb = vec2(th, Orientations.g);}\\\n" - " else {weights.b = weight; Orientations.b = th;}"; - }else - { - savepeak_code = "\\\n" - " if(weight <=weights.a){}\\\n" - " else if(weight >weights.r)\\\n" - " {weights = vec4(weight, weights.rgb); Orientations = vec4(th, Orientations.rgb);}\\\n" - " else if(weight >weights.g)\\\n" - " {weights.gba = vec3(weight, weights.gb); Orientations.gba = vec3(th, Orientations.gb);}\\\n" - " else if(weight >weights.b)\\\n" - " {weights.ba = vec2(weight, weights.b); Orientations.ba = vec2(th, Orientations.b);}\\\n" - " else {weights.a = weight; Orientations.a = th;}"; - } - - }else - { - out<<"\n" - " float Orientation; "; - testpeak_code ="\\\n" - " if(npeaks<=0.0){\\\n" - " test = equal(bins[i], maxh) ;"; - savepeak_code="\\\n" - " npeaks++; \\\n" - " Orientation = th;"; - - } - //find the peaks - out <<"\n" - " #define FINDPEAK(i, k)" <<testpeak_code<<"\\\n" - " if( any ( test) ) \\\n" - " { \\\n" - " if(test.r && bins[i].x > prevb && bins[i].x > bins[i].y ) \\\n" - " { \\\n" - " float di = -0.5 * (bins[i].y-prevb) / (bins[i].y+prevb-bins[i].x - bins[i].x) ; \\\n" - " float th = (k+di+0.5); float weight = bins[i].x;" - <<savepeak_code<<"\\\n" - " }\\\n" - " else if(test.g && all( greaterThan(bins[i].yy , bins[i].xz)) ) \\\n" - " { \\\n" - " float di = -0.5 * (bins[i].z-bins[i].x) / (bins[i].z+bins[i].x-bins[i].y- bins[i].y) ; \\\n" - " float th = (k+di+1.5); float weight = bins[i].y; " - <<savepeak_code<<" \\\n" - " }\\\n" - " if(test.b && all( greaterThan( bins[i].zz , bins[i].yw)) ) \\\n" - " { \\\n" - " float di = -0.5 * (bins[i].w-bins[i].y) / (bins[i].w+bins[i].y-bins[i].z- bins[i].z) ; \\\n" - " float th = (k+di+2.5); float weight = bins[i].z; " - <<savepeak_code<<" \\\n" - " }\\\n" - " else if(test.a && bins[i].w > bins[i].z && bins[i].w > bins[i+1].x ) \\\n" - " { \\\n" - " float di = -0.5 * (bins[i+1].x-bins[i].z) / (bins[i+1].x+bins[i].z-bins[i].w - bins[i].w) ; \\\n" - " float th = (k+di+3.5); float weight = bins[i].w; " - <<savepeak_code<<" \\\n" - " }\\\n" - " }}\\\n" - " prevb = bins[i].w;"; - //the following loop will be unrolled anyway in fp40, - //taking more than 1000 instrucsions.. - //.... - if(GlobalUtil::_KeepShaderLoop) - { - out<<"\n" - " vec4 hh = maxh * ORIENTATION_THRESHOLD; bvec4 test; \n" - " bins[9] = bins[0]; \n" - " float npeaks = 0.0, k = 0.0; \n" - " float prevb = bins[8].w; \n" - " for (int i = 0; i < 9; i++) \n" - " {\n" - " FINDPEAK(i, k);\n" - " k = k + 4.0; \n" - " }"; - }else - { - //loop unroll for ATI. - out <<"\n" - " vec4 hh = maxh * ORIENTATION_THRESHOLD; bvec4 test;\n" - " bins[9] = bins[0]; \n" - " float npeaks = 0.0; \n" - " float prevb = bins[8].w; \n" - " FINDPEAK(0, 0.0);\n" - " FINDPEAK(1, 4.0);\n" - " FINDPEAK(2, 8.0);\n" - " FINDPEAK(3, 12.0);\n" - " FINDPEAK(4, 16.0);\n" - " FINDPEAK(5, 20.0);\n" - " FINDPEAK(6, 24.0);\n" - " FINDPEAK(7, 28.0);\n" - " FINDPEAK(8, 32.0);\n"; - } - //WRITE output - if(GlobalUtil::_MaxOrientation>1) - { - out<<"\n" - " if(orientation_mode){\n" - " npeaks = dot(vec4(1,1," - <<(GlobalUtil::_MaxOrientation>2 ? 1 : 0)<<"," - <<(GlobalUtil::_MaxOrientation >3? 1 : 0)<<"), vec4(greaterThan(weights, hh)));\n" - " gl_FragData[0] = vec4(pos, npeaks, sigma);\n" - " gl_FragData[1] = radians((Orientations )*10.0);\n" - " }else{\n" - " gl_FragData[0] = vec4(pos, radians((Orientations.x)*10.0), sigma);\n" - " }\n"; - }else - { - out<<"\n" - " gl_FragData[0] = vec4(pos, radians((Orientation)*10.0), sigma);\n"; - } - //end - out<<"\n" - "}\n"<<'\0'; - - -} - -void ShaderBagGLSL::SetSimpleOrientationInput(int oTex, float sigma, float sigma_step) -{ - glUniform1i(_param_orientation_gtex, 1); - glUniform1f(_param_orientation_size, sigma); -} - - - - -void ShaderBagGLSL::SetFeatureOrientationParam(int gtex, int width, int height, float sigma, int stex, float step) -{ - /// - glUniform1i(_param_orientation_gtex, 1); - - if((GlobalUtil::_SubpixelLocalization || GlobalUtil::_KeepExtremumSign)&& stex) - { - //specify texutre for subpixel subscale localization - glUniform1i(_param_orientation_stex, 2); - } - - float size[4]; - size[0] = (float)width; - size[1] = (float)height; - size[2] = sigma; - size[3] = step; - glUniform4fv(_param_orientation_size, 1, size); -} - - -void ShaderBagGLSL::LoadDescriptorShaderF2() -{ - //one shader outpout 128/8 = 16 , each fragout encodes 4 - //const double twopi = 2.0*3.14159265358979323846; - //const double rpi = 8.0/twopi; - ostringstream out; - out<<setprecision(8); - - out<<"\n" - "#define M_PI 3.14159265358979323846\n" - "#define TWO_PI (2.0*M_PI)\n" - "#define RPI 1.2732395447351626861510701069801\n" - "#define WF size.z\n" - "uniform sampler2DRect tex; \n" - "uniform sampler2DRect gradTex; \n" - "uniform vec4 dsize; \n" - "uniform vec3 size; \n" - "void main() \n" - "{\n" - " vec2 dim = size.xy; //image size \n" - " float index = dsize.x*floor(gl_TexCoord[0].y * 0.5) + gl_TexCoord[0].x;\n" - " float idx = 8.0 * fract(index * 0.125) + 8.0 * floor(2.0 * fract(gl_TexCoord[0].y * 0.5)); \n" - " index = floor(index*0.125) + 0.49; \n" - " vec2 coord = floor( vec2( mod(index, dsize.z), index*dsize.w)) + 0.5 ;\n" - " vec2 pos = texture2DRect(tex, coord).xy; \n" - " if(any(lessThanEqual(pos.xy, vec2(1.0))) || any(greaterThanEqual(pos.xy, dim-1.0)))// discard; \n" - " { gl_FragData[0] = gl_FragData[1] = vec4(0.0); return; }\n" - " float anglef = texture2DRect(tex, coord).z;\n" - " if(anglef > M_PI) anglef -= TWO_PI;\n" - " float sigma = texture2DRect(tex, coord).w; \n" - " float spt = abs(sigma * WF); //default to be 3*sigma \n"; - - //rotation - out<< - " vec4 cscs, rots; \n" - " cscs.y = sin(anglef); cscs.x = cos(anglef); \n" - " cscs.zw = - cscs.xy; \n" - " rots = cscs /spt; \n" - " cscs *= spt; \n"; - - //here cscs is actually (cos, sin, -cos, -sin) * (factor: 3)*sigma - //and rots is (cos, sin, -cos, -sin ) /(factor*sigma) - //devide the 4x4 sift grid into 16 1x1 block, and each corresponds to a shader thread - //To use linear interoplation, 1x1 is increased to 2x2, by adding 0.5 to each side - - out<< - "vec4 temp; vec2 pt, offsetpt; \n" - " /*the fraction part of idx is .5*/ \n" - " offsetpt.x = 4.0* fract(idx*0.25) - 2.0; \n" - " offsetpt.y = floor(idx*0.25) - 1.5; \n" - " temp = cscs.xwyx*offsetpt.xyxy; \n" - " pt = pos + temp.xz + temp.yw; \n"; - - //get a horizontal bounding box of the rotated rectangle - out<< - " vec2 bwin = abs(cscs.xy); \n" - " float bsz = bwin.x + bwin.y; \n" - " vec4 sz; \n" - " sz.xy = max(pt - vec2(bsz), vec2(1,1));\n" - " sz.zw = min(pt + vec2(bsz), dim - vec2(2, 2)); \n" - " sz = floor(sz)+0.5;"; //move sample point to pixel center - //get voting for two box - - out<<"\n" - " vec4 DA, DB; vec2 spos; \n" - " DA = DB = vec4(0.0, 0.0, 0.0, 0.0); \n" - " for(spos.y = sz.y; spos.y <= sz.w; spos.y+=1.0) \n" - " { \n" - " for(spos.x = sz.x; spos.x <= sz.z; spos.x+=1.0) \n" - " { \n" - " vec2 diff = spos - pt; \n" - " temp = rots.xywx * diff.xyxy;\n" - " vec2 nxy = (temp.xz + temp.yw); \n" - " vec2 nxyn = abs(nxy); \n" - " if(all( lessThan(nxyn, vec2(1.0)) ))\n" - " {\n" - " vec4 cc = texture2DRect(gradTex, spos); \n" - " float mod = cc.b; float angle = cc.a; \n" - " float theta0 = RPI * (anglef - angle); \n" - " float theta = theta0 < 0.0? theta0 + 8.0 : theta0;;\n" - " diff = nxy + offsetpt.xy; \n" - " float ww = exp(-0.125*dot(diff, diff));\n" - " vec2 weights = vec2(1) - nxyn;\n" - " float weight = weights.x * weights.y *mod*ww; \n" - " float theta1 = floor(theta); \n" - " float weight2 = (theta - theta1) * weight;\n" - " float weight1 = weight - weight2;\n" - " DA += vec4(equal(vec4(theta1), vec4(0, 1, 2, 3)))*weight1;\n" - " DA += vec4(equal(vec4(theta1), vec4(7, 0, 1, 2)))*weight2; \n" - " DB += vec4(equal(vec4(theta1), vec4(4, 5, 6, 7)))*weight1;\n" - " DB += vec4(equal(vec4(theta1), vec4(3, 4, 5, 6)))*weight2; \n" - " }\n" - " }\n" - " }\n"; - - out<< - " gl_FragData[0] = DA; gl_FragData[1] = DB;\n" - "}\n"<<'\0'; - - ProgramGLSL * program = new ProgramGLSL(out.str().c_str()); - - if(program->IsNative()) - { - s_descriptor_fp = program ; - _param_descriptor_gtex = glGetUniformLocation(*program, "gradTex"); - _param_descriptor_size = glGetUniformLocation(*program, "size"); - _param_descriptor_dsize = glGetUniformLocation(*program, "dsize"); - }else - { - delete program; - } - - -} - -void ShaderBagGLSL::LoadDescriptorShader() -{ - GlobalUtil::_DescriptorPPT = 16; - LoadDescriptorShaderF2(); -} - - -void ShaderBagGLSL::SetFeatureDescirptorParam(int gtex, int otex, float dwidth, float fwidth, float width, float height, float sigma) -{ - /// - glUniform1i(_param_descriptor_gtex, 1); - - float dsize[4] ={dwidth, 1.0f/dwidth, fwidth, 1.0f/fwidth}; - glUniform4fv(_param_descriptor_dsize, 1, dsize); - float size[3]; - size[0] = width; - size[1] = height; - size[2] = GlobalUtil::_DescriptorWindowFactor; - glUniform3fv(_param_descriptor_size, 1, size); - -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void ShaderBagPKSL::LoadFixedShaders() -{ - ProgramGLSL * program; - - - s_gray = new ProgramGLSL( - "uniform sampler2DRect tex; void main(){\n" - "float intensity = dot(vec3(0.299, 0.587, 0.114), texture2DRect(tex,gl_TexCoord[0].xy ).rgb);\n" - "gl_FragColor= vec4(intensity, intensity, intensity, 1.0);}" ); - - - s_sampling = new ProgramGLSL( - "uniform sampler2DRect tex; void main(){\n" - "gl_FragColor= vec4( texture2DRect(tex,gl_TexCoord[0].st ).r,texture2DRect(tex,gl_TexCoord[1].st ).r,\n" - " texture2DRect(tex,gl_TexCoord[2].st ).r,texture2DRect(tex,gl_TexCoord[3].st ).r);}" ); - - - s_margin_copy = program = new ProgramGLSL( - "uniform sampler2DRect tex; uniform vec4 truncate; void main(){\n" - "vec4 cc = texture2DRect(tex, min(gl_TexCoord[0].xy, truncate.xy)); \n" - "bvec2 ob = lessThan(gl_TexCoord[0].xy, truncate.xy);\n" - "if(ob.y) { gl_FragColor = (truncate.z ==0.0 ? cc.rrbb : cc.ggaa); } \n" - "else if(ob.x) {gl_FragColor = (truncate.w <1.5 ? cc.rgrg : cc.baba);} \n" - "else { vec4 weights = vec4(vec4(0.0, 1.0, 2.0, 3.0) == truncate.wwww);\n" - "float v = dot(weights, cc); gl_FragColor = vec4(v);}}"); - - _param_margin_copy_truncate = glGetUniformLocation(*program, "truncate"); - - - - s_zero_pass = new ProgramGLSL("void main(){gl_FragColor = vec4(0.0);}"); - - - - s_grad_pass = program = new ProgramGLSL( - "uniform sampler2DRect tex; uniform sampler2DRect texp; void main ()\n" - "{\n" - " vec4 v1, v2, gg;\n" - " vec4 cc = texture2DRect(tex, gl_TexCoord[0].xy);\n" - " vec4 cp = texture2DRect(texp, gl_TexCoord[0].xy);\n" - " gl_FragData[0] = cc - cp; \n" - " vec4 cl = texture2DRect(tex, gl_TexCoord[1].xy); vec4 cr = texture2DRect(tex, gl_TexCoord[2].xy);\n" - " vec4 cd = texture2DRect(tex, gl_TexCoord[3].xy); vec4 cu = texture2DRect(tex, gl_TexCoord[4].xy);\n" - " vec4 dx = (vec4(cr.rb, cc.ga) - vec4(cc.rb, cl.ga)).zxwy;\n" - " vec4 dy = (vec4(cu.rg, cc.ba) - vec4(cc.rg, cd.ba)).zwxy;\n" - " vec4 grad = 0.5 * sqrt(dx*dx + dy * dy);\n" - " gl_FragData[1] = grad;\n" - " vec4 invalid = vec4(equal(grad, vec4(0.0))); \n" - " vec4 ov = atan(dy, dx + invalid); \n" - " gl_FragData[2] = ov; \n" - "}\n\0"); //when - - _param_grad_pass_texp = glGetUniformLocation(*program, "texp"); - - - GlobalUtil::_OrientationPack2 = 0; - LoadOrientationShader(); - - if(s_orientation == NULL) - { - //Load a simplified version if the right version is not supported - s_orientation = program = new ProgramGLSL( - "uniform sampler2DRect tex; uniform sampler2DRect oTex; uniform vec2 size; void main(){\n" - " vec4 cc = texture2DRect(tex, gl_TexCoord[0].xy);\n" - " vec2 co = cc.xy * 0.5; \n" - " vec4 oo = texture2DRect(oTex, co);\n" - " bvec2 bo = lessThan(fract(co), vec2(0.5)); \n" - " float o = bo.y? (bo.x? oo.r : oo.g) : (bo.x? oo.b : oo.a); \n" - " gl_FragColor = vec4(cc.rg, o, size.x * pow(size.y, cc.a));}"); - - _param_orientation_gtex= glGetUniformLocation(*program, "oTex"); - _param_orientation_size= glGetUniformLocation(*program, "size"); - GlobalUtil::_MaxOrientation = 0; - GlobalUtil::_FullSupported = 0; - std::cerr<<"Orientation simplified on this hardware"<<endl; - } - - if(GlobalUtil::_DescriptorPPT) - { - LoadDescriptorShader(); - if(s_descriptor_fp == NULL) - { - GlobalUtil::_DescriptorPPT = GlobalUtil::_FullSupported = 0; - std::cerr<<"Descriptor ignored on this hardware"<<endl; - } - } -} - - -void ShaderBagPKSL::LoadDisplayShaders() -{ - ProgramGLSL * program; - - s_copy_key = new ProgramGLSL( - "uniform sampler2DRect tex;void main(){\n" - "gl_FragColor= vec4(texture2DRect(tex, gl_TexCoord[0].xy).rg, 0,1);}"); - - //shader used to write a vertex buffer object - //which is used to draw the quads of each feature - s_vertex_list = program = new ProgramGLSL( - "uniform sampler2DRect tex; uniform vec4 sizes; void main(){\n" - "float fwidth = sizes.y; \n" - "float twidth = sizes.z; \n" - "float rwidth = sizes.w; \n" - "float index = 0.1*(fwidth*floor(gl_TexCoord[0].y) + gl_TexCoord[0].x);\n" - "float px = mod(index, twidth);\n" - "vec2 tpos= floor(vec2(px, index*rwidth))+0.5;\n" - "vec4 cc = texture2DRect(tex, tpos );\n" - "float size = 3.0 * cc.a; \n" - "gl_FragColor.zw = vec2(0.0, 1.0);\n" - "if(any(lessThan(cc.xy,vec2(0.0)))) {gl_FragColor.xy = cc.xy;}else \n" - "{\n" - " float type = fract(px);\n" - " vec2 dxy; float s, c;\n" - " dxy.x = type < 0.1 ? 0.0 : (((type <0.5) || (type > 0.9))? size : -size);\n" - " dxy.y = type < 0.2 ? 0.0 : (((type < 0.3) || (type > 0.7) )? -size :size); \n" - " s = sin(cc.b); c = cos(cc.b); \n" - " gl_FragColor.x = cc.x + c*dxy.x-s*dxy.y;\n" - " gl_FragColor.y = cc.y + c*dxy.y+s*dxy.x;}\n" - "}\n\0"); - /*gl_FragColor = vec4(tpos, 0.0, 1.0);}\n\0");*/ - - _param_genvbo_size = glGetUniformLocation(*program, "sizes"); - - s_display_gaussian = new ProgramGLSL( - "uniform sampler2DRect tex; void main(){\n" - "vec4 pc = texture2DRect(tex, gl_TexCoord[0].xy); bvec2 ff = lessThan(fract(gl_TexCoord[0].xy), vec2(0.5));\n" - "float v = ff.y?(ff.x? pc.r : pc.g):(ff.x?pc.b:pc.a); gl_FragColor = vec4(vec3(v), 1.0);}"); - - s_display_dog = new ProgramGLSL( - "uniform sampler2DRect tex; void main(){\n" - "vec4 pc = texture2DRect(tex, gl_TexCoord[0].xy); bvec2 ff = lessThan(fract(gl_TexCoord[0].xy), vec2(0.5));\n" - "float v = ff.y ?(ff.x ? pc.r : pc.g):(ff.x ? pc.b : pc.a);float g = (0.5+20.0*v);\n" - "gl_FragColor = vec4(g, g, g, 1.0);}" ); - - - s_display_grad = new ProgramGLSL( - "uniform sampler2DRect tex; void main(){\n" - "vec4 pc = texture2DRect(tex, gl_TexCoord[0].xy); bvec2 ff = lessThan(fract(gl_TexCoord[0].xy), vec2(0.5));\n" - "float v = ff.y ?(ff.x ? pc.r : pc.g):(ff.x ? pc.b : pc.a); gl_FragColor = vec4(5.0 *vec3(v), 1.0); }"); - - s_display_keys= new ProgramGLSL( - "uniform sampler2DRect tex; void main(){\n" - "vec4 oc = texture2DRect(tex, gl_TexCoord[0].xy); \n" - "vec4 cc = vec4(equal(abs(oc.rrrr), vec4(1.0, 2.0, 3.0, 4.0))); \n" - "bvec2 ff = lessThan(fract(gl_TexCoord[0].xy) , vec2(0.5));\n" - "float v = ff.y ?(ff.x ? cc.r : cc.g):(ff.x ? cc.b : cc.a);\n" - "if(v == 0.0) discard; \n" - "else if(oc.r > 0.0) gl_FragColor = vec4(1.0, 0.0, 0,1.0); \n" - "else gl_FragColor = vec4(0.0,1.0,0.0,1.0); }" ); -} - -void ShaderBagPKSL::LoadOrientationShader(void) -{ - ostringstream out; - if(GlobalUtil::_IsNvidia) - { - out << "#pragma optionNV(ifcvt none)\n" - "#pragma optionNV(unroll all)\n"; - } - out<<"\n" - "#define GAUSSIAN_WF float("<<GlobalUtil::_OrientationGaussianFactor<<") \n" - "#define SAMPLE_WF float("<<GlobalUtil::_OrientationWindowFactor<< " )\n" - "#define ORIENTATION_THRESHOLD "<< GlobalUtil::_MulitiOrientationThreshold << "\n" - "uniform sampler2DRect tex; uniform sampler2DRect gtex;\n" - "uniform sampler2DRect otex; uniform vec4 size;\n" - "void main() \n" - "{ \n" - " vec4 bins[10]; \n" - " bins[0] = vec4(0.0);bins[1] = vec4(0.0);bins[2] = vec4(0.0); \n" - " bins[3] = vec4(0.0);bins[4] = vec4(0.0);bins[5] = vec4(0.0); \n" - " bins[6] = vec4(0.0);bins[7] = vec4(0.0);bins[8] = vec4(0.0); \n" - " vec4 sift = texture2DRect(tex, gl_TexCoord[0].xy); \n" - " vec2 pos = sift.xy; \n" - " bool orientation_mode = (size.z != 0.0); \n" - " float sigma = orientation_mode? (abs(size.z) * pow(size.w, sift.w) * sift.z) : (sift.w); \n" - " //bool fixed_orientation = (size.z < 0.0); \n" - " if(size.z < 0.0) {gl_FragData[0] = vec4(pos, 0.0, sigma); return;}" - " float gsigma = sigma * GAUSSIAN_WF; \n" - " vec2 win = abs(vec2(sigma * (SAMPLE_WF * GAUSSIAN_WF))); \n" - " vec2 dim = size.xy; \n" - " vec4 dist_threshold = vec4(win.x*win.x+0.5); \n" - " float factor = -0.5/(gsigma*gsigma); \n" - " vec4 sz; vec2 spos; \n" - " //if(any(pos.xy <= float(1))) discard; \n" - " sz.xy = max( pos - win, vec2(2.0,2.0)); \n" - " sz.zw = min( pos + win, dim-vec2(3.0)); \n" - " sz = floor(sz*0.5) + 0.5; "; - //loop to get the histogram - - out<<"\n" - " for(spos.y = sz.y; spos.y <= sz.w; spos.y+=1.0) \n" - " { \n" - " for(spos.x = sz.x; spos.x <= sz.z; spos.x+=1.0) \n" - " { \n" - " vec2 offset = 2.0 * spos - pos - vec2(0.5); \n" - " vec4 off = vec4(offset, offset + vec2(1)); \n" - " vec4 distsq = off.xzxz * off.xzxz + off.yyww * off.yyww; \n" - " bvec4 inside = lessThan(distsq, dist_threshold); \n" - " if(any(inside)) \n" - " { \n" - " vec4 gg = texture2DRect(gtex, spos); \n" - " vec4 oo = texture2DRect(otex, spos); \n" - " vec4 weight = gg * exp(distsq * factor); \n" - " vec4 idxv = floor(degrees(oo)*0.1); \n" - " idxv+= (vec4(lessThan(idxv, vec4(0.0)))*36.0); \n" - " vec4 vidx = fract(idxv * 0.25) * 4.0;//mod(idxv, 4.0); \n"; - // - if(GlobalUtil::_UseDynamicIndexing) - { - // it might be slow on some GPUs - out<<"\n" - " for(int i = 0 ; i < 4; i++)\n" - " {\n" - " if(inside[i])\n" - " {\n" - " float idx = idxv[i]; \n" - " vec4 inc = weight[i] * vec4(equal(vec4(vidx[i]), vec4(0.0,1.0,2.0,3.0))); \n" - " int iidx = int(floor(idx*0.25)); \n" - " bins[iidx]+=inc; \n" - " } \n" - " } \n" - " } \n" - " } \n" - " }"; - - }else - { - //nvfp40 still does not support dynamic array indexing - //unrolled binary search - //it seems to be faster than the dyanmic indexing version on some GPUs - out<<"\n" - " for(int i = 0 ; i < 4; i++)\n" - " {\n" - " if(inside[i])\n" - " {\n" - " float idx = idxv[i]; \n" - " vec4 inc = weight[i] * vec4(equal(vec4(vidx[i]), vec4(0,1,2,3))); \n" - " if(idx < 16.0) \n" - " { \n" - " if(idx < 8.0) \n" - " { \n" - " if(idx < 4.0) { bins[0]+=inc;} \n" - " else { bins[1]+=inc;} \n" - " }else \n" - " { \n" - " if(idx < 12.0){ bins[2]+=inc;} \n" - " else { bins[3]+=inc;} \n" - " } \n" - " }else if(idx < 32.0) \n" - " { \n" - " if(idx < 24.0) \n" - " { \n" - " if(idx <20.0) { bins[4]+=inc;} \n" - " else { bins[5]+=inc;} \n" - " }else \n" - " { \n" - " if(idx < 28.0){ bins[6]+=inc;} \n" - " else { bins[7]+=inc;} \n" - " } \n" - " }else \n" - " { \n" - " bins[8]+=inc; \n" - " } \n" - " } \n" - " } \n" - " } \n" - " } \n" - " }"; - - } - - //reuse the code from the unpacked version.. - ShaderBagGLSL::WriteOrientationCodeToStream(out); - - - - ProgramGLSL * program = new ProgramGLSL(out.str().c_str()); - if(program->IsNative()) - { - s_orientation = program ; - _param_orientation_gtex = glGetUniformLocation(*program, "gtex"); - _param_orientation_otex = glGetUniformLocation(*program, "otex"); - _param_orientation_size = glGetUniformLocation(*program, "size"); - }else - { - delete program; - } -} - -void ShaderBagPKSL::SetGenListStartParam(float width, int tex0) -{ - glUniform1f(_param_ftex_width, width); - glUniform1i(_param_genlist_start_tex0, 0); -} - -void ShaderBagPKSL::LoadGenListShader(int ndoglev,int nlev) -{ - ProgramGLSL * program; - - s_genlist_init_tight = new ProgramGLSL( - "uniform sampler2DRect tex; void main ()\n" - "{\n" - " vec4 key = vec4(texture2DRect(tex, gl_TexCoord[0].xy).r, \n" - " texture2DRect(tex, gl_TexCoord[1].xy).r, \n" - " texture2DRect(tex, gl_TexCoord[2].xy).r, \n" - " texture2DRect(tex, gl_TexCoord[3].xy).r); \n" - " gl_FragColor = vec4(notEqual(key, vec4(0.0))); \n" - "}"); - - s_genlist_init_ex = program = new ProgramGLSL( - "uniform sampler2DRect tex; uniform vec4 bbox; void main ()\n" - "{\n" - " vec4 helper1 = vec4(equal(vec4(abs(texture2DRect(tex, gl_TexCoord[0].xy).r)), vec4(1.0, 2.0, 3.0, 4.0)));\n" - " vec4 helper2 = vec4(equal(vec4(abs(texture2DRect(tex, gl_TexCoord[1].xy).r)), vec4(1.0, 2.0, 3.0, 4.0)));\n" - " vec4 helper3 = vec4(equal(vec4(abs(texture2DRect(tex, gl_TexCoord[2].xy).r)), vec4(1.0, 2.0, 3.0, 4.0)));\n" - " vec4 helper4 = vec4(equal(vec4(abs(texture2DRect(tex, gl_TexCoord[3].xy).r)), vec4(1.0, 2.0, 3.0, 4.0)));\n" - " vec4 bx1 = vec4(lessThan(gl_TexCoord[0].xxyy, bbox)); \n" - " vec4 bx4 = vec4(lessThan(gl_TexCoord[3].xxyy, bbox)); \n" - " vec4 bx2 = vec4(bx4.xy, bx1.zw); \n" - " vec4 bx3 = vec4(bx1.xy, bx4.zw);\n" - " helper1 = min(min(bx1.xyxy, bx1.zzww), helper1);\n" - " helper2 = min(min(bx2.xyxy, bx2.zzww), helper2);\n" - " helper3 = min(min(bx3.xyxy, bx3.zzww), helper3);\n" - " helper4 = min(min(bx4.xyxy, bx4.zzww), helper4);\n" - " gl_FragColor.r = float(any(greaterThan(max(helper1.xy, helper1.zw), vec2(0.0)))); \n" - " gl_FragColor.g = float(any(greaterThan(max(helper2.xy, helper2.zw), vec2(0.0)))); \n" - " gl_FragColor.b = float(any(greaterThan(max(helper3.xy, helper3.zw), vec2(0.0)))); \n" - " gl_FragColor.a = float(any(greaterThan(max(helper4.xy, helper4.zw), vec2(0.0)))); \n" - "}"); - _param_genlist_init_bbox = glGetUniformLocation( *program, "bbox"); - - s_genlist_end = program = new ProgramGLSL( - GlobalUtil::_KeepExtremumSign == 0 ? - - "uniform sampler2DRect tex; uniform sampler2DRect ktex; void main()\n" - "{\n" - " vec4 tc = texture2DRect( tex, gl_TexCoord[0].xy);\n" - " vec2 pos = tc.rg; float index = tc.b;\n" - " vec4 tk = texture2DRect( ktex, pos); \n" - " vec4 keys = vec4(equal(abs(tk.rrrr), vec4(1.0, 2.0, 3.0, 4.0))); \n" - " vec2 opos; \n" - " opos.x = dot(keys, vec4(-0.5, 0.5, -0.5, 0.5));\n" - " opos.y = dot(keys, vec4(-0.5, -0.5, 0.5, 0.5));\n" - " gl_FragColor = vec4(opos + pos * 2.0 + tk.yz, 1.0, tk.w);\n" - "}" : - - "uniform sampler2DRect tex; uniform sampler2DRect ktex; void main()\n" - "{\n" - " vec4 tc = texture2DRect( tex, gl_TexCoord[0].xy);\n" - " vec2 pos = tc.rg; float index = tc.b;\n" - " vec4 tk = texture2DRect( ktex, pos); \n" - " vec4 keys = vec4(equal(abs(tk.rrrr), vec4(1.0, 2.0, 3.0, 4.0))) \n" - " vec2 opos; \n" - " opos.x = dot(keys, vec4(-0.5, 0.5, -0.5, 0.5));\n" - " opos.y = dot(keys, vec4(-0.5, -0.5, 0.5, 0.5));\n" - " gl_FragColor = vec4(opos + pos * 2.0 + tk.yz, sign(tk.r), tk.w);\n" - "}" - ); - - _param_genlist_end_ktex = glGetUniformLocation(*program, "ktex"); - - //reduction ... - s_genlist_histo = new ProgramGLSL( - "uniform sampler2DRect tex; void main ()\n" - "{\n" - " vec4 helper; vec4 helper2; \n" - " helper = texture2DRect(tex, gl_TexCoord[0].xy); helper2.xy = helper.xy + helper.zw; \n" - " helper = texture2DRect(tex, gl_TexCoord[1].xy); helper2.zw = helper.xy + helper.zw; \n" - " gl_FragColor.rg = helper2.xz + helper2.yw;\n" - " helper = texture2DRect(tex, gl_TexCoord[2].xy); helper2.xy = helper.xy + helper.zw; \n" - " helper = texture2DRect(tex, gl_TexCoord[3].xy); helper2.zw = helper.xy + helper.zw; \n" - " gl_FragColor.ba= helper2.xz+helper2.yw;\n" - "}"); - - - //read of the first part, which generates tex coordinates - - s_genlist_start= program = ShaderBagGLSL::LoadGenListStepShader(1, 1); - _param_ftex_width= glGetUniformLocation(*program, "width"); - _param_genlist_start_tex0 = glGetUniformLocation(*program, "tex0"); - //stepping - s_genlist_step = program = ShaderBagGLSL::LoadGenListStepShader(0, 1); - _param_genlist_step_tex0= glGetUniformLocation(*program, "tex0"); - -} -void ShaderBagPKSL::UnloadProgram(void) -{ - glUseProgram(0); -} -void ShaderBagPKSL::LoadKeypointShader(float dog_threshold, float edge_threshold) -{ - float threshold0 = dog_threshold* (GlobalUtil::_SubpixelLocalization?0.8f:1.0f); - float threshold1 = dog_threshold; - float threshold2 = (edge_threshold+1)*(edge_threshold+1)/edge_threshold; - ostringstream out;; - out<<setprecision(8); - - if(GlobalUtil::_IsNvidia) - { - out << "#pragma optionNV(ifcvt none)\n" - "#pragma optionNV(unroll all)\n"; - - } - if(GlobalUtil::_KeepShaderLoop) - { - out << "#define REPEAT4(FUNCTION)\\\n" - "for(int i = 0; i < 4; ++i)\\\n" - "{\\\n" - " FUNCTION(i);\\\n" - "}\n"; - }else - { - //loop unroll - out << "#define REPEAT4(FUNCTION)\\\n" - "FUNCTION(0);\\\n" - "FUNCTION(1);\\\n" - "FUNCTION(2);\\\n" - "FUNCTION(3);\n"; - } - //tex(X)(Y) - //X: (CLR) (CENTER 0, LEFT -1, RIGHT +1) - //Y: (CDU) (CENTER 0, DOWN -1, UP +1) - - if(GlobalUtil::_DarknessAdaption) - { - out << "#define THRESHOLD0(i) (" << threshold0 << "* ii[i])\n" - "#define THRESHOLD1 (" << threshold1 << "* ii[0])\n" - "#define THRESHOLD2 " << threshold2 << "\n" - "#define DEFINE_EXTRA() vec4 ii = texture2DRect(texI, gl_TexCoord[0].xy); " - "ii = min(2.0 * ii + 0.1, 1.0) \n" - "#define MOVE_EXTRA(idx) ii[0] = ii[idx]\n"; - out << "uniform sampler2DRect texI;\n"; - }else - { - out << "#define THRESHOLD0(i) " << threshold0 << "\n" - "#define THRESHOLD1 " << threshold1 << "\n" - "#define THRESHOLD2 " << threshold2 << "\n" - "#define DEFINE_EXTRA()\n" - "#define MOVE_EXTRA(idx) \n" ; - } - - out<< - "uniform sampler2DRect tex; uniform sampler2DRect texU;\n" - "uniform sampler2DRect texD; void main ()\n" - "{\n" - " vec2 TexRU = vec2(gl_TexCoord[2].x, gl_TexCoord[4].y); \n" - " vec4 ccc = texture2DRect(tex, gl_TexCoord[0].xy);\n" - " vec4 clc = texture2DRect(tex, gl_TexCoord[1].xy);\n" - " vec4 crc = texture2DRect(tex, gl_TexCoord[2].xy);\n" - " vec4 ccd = texture2DRect(tex, gl_TexCoord[3].xy);\n" - " vec4 ccu = texture2DRect(tex, gl_TexCoord[4].xy);\n" - " vec4 cld = texture2DRect(tex, gl_TexCoord[5].xy);\n" - " vec4 clu = texture2DRect(tex, gl_TexCoord[6].xy);\n" - " vec4 crd = texture2DRect(tex, gl_TexCoord[7].xy);\n" - " vec4 cru = texture2DRect(tex, TexRU.xy);\n" - " vec4 cc = ccc;\n" - " vec4 v1[4], v2[4];\n" - " v1[0] = vec4(clc.g, ccc.g, ccd.b, ccc.b);\n" - " v1[1] = vec4(ccc.r, crc.r, ccd.a, ccc.a);\n" - " v1[2] = vec4(clc.a, ccc.a, ccc.r, ccu.r);\n" - " v1[3] = vec4(ccc.b, crc.b, ccc.g, ccu.g);\n" - " v2[0] = vec4(cld.a, clc.a, ccd.a, ccc.a);\n" - " v2[1] = vec4(ccd.b, ccc.b, crd.b, crc.b);\n" - " v2[2] = vec4(clc.g, clu.g, ccc.g, ccu.g);\n" - " v2[3] = vec4(ccc.r, ccu.r, crc.r, cru.r);\n" - " DEFINE_EXTRA();\n"; - - //test against 8 neighbours - //use variable to identify type of extremum - //1.0 for local maximum and -1.0 for minimum - out << - " vec4 key = vec4(0.0); \n" - " #define KEYTEST_STEP0(i) \\\n" - " {\\\n" - " bvec4 test1 = greaterThan(vec4(cc[i]), max(v1[i], v2[i])), test2 = lessThan(vec4(cc[i]), min(v1[i], v2[i]));\\\n" - " key[i] = cc[i] > float(THRESHOLD0(i)) && all(test1)?1.0: 0.0;\\\n" - " key[i] = cc[i] < float(-THRESHOLD0(i)) && all(test2)? -1.0: key[i];\\\n" - " }\n" - " REPEAT4(KEYTEST_STEP0);\n" - " if(gl_TexCoord[0].x < 1.0) {key.rb = vec2(0.0);}\n" - " if(gl_TexCoord[0].y < 1.0) {key.rg = vec2(0.0);}\n" - " gl_FragColor = vec4(0.0);\n" - " if(any(notEqual(key, vec4(0.0)))) {\n"; - - //do edge supression first.. - //vector v1 is < (-1, 0), (1, 0), (0,-1), (0, 1)> - //vector v2 is < (-1,-1), (-1,1), (1,-1), (1, 1)> - - out<< - " float fxx[4], fyy[4], fxy[4], fx[4], fy[4];\n" - " #define EDGE_SUPPRESION(i) \\\n" - " if(key[i] != 0.0)\\\n" - " {\\\n" - " vec4 D2 = v1[i].xyzw - cc[i];\\\n" - " vec2 D4 = v2[i].xw - v2[i].yz;\\\n" - " vec2 D5 = 0.5*(v1[i].yw-v1[i].xz); \\\n" - " fx[i] = D5.x; fy[i] = D5.y ;\\\n" - " fxx[i] = D2.x + D2.y;\\\n" - " fyy[i] = D2.z + D2.w;\\\n" - " fxy[i] = 0.25*(D4.x + D4.y);\\\n" - " float fxx_plus_fyy = fxx[i] + fyy[i];\\\n" - " float score_up = fxx_plus_fyy*fxx_plus_fyy; \\\n" - " float score_down = (fxx[i]*fyy[i] - fxy[i]*fxy[i]);\\\n" - " if( score_down <= 0.0 || score_up > THRESHOLD2 * score_down)key[i] = 0.0;\\\n" - " }\n" - " REPEAT4(EDGE_SUPPRESION);\n" - " if(any(notEqual(key, vec4(0.0)))) {\n"; - - //////////////////////////////////////////////// - //read 9 pixels of upper/lower level - out<< - " vec4 v4[4], v5[4], v6[4];\n" - " ccc = texture2DRect(texU, gl_TexCoord[0].xy);\n" - " clc = texture2DRect(texU, gl_TexCoord[1].xy);\n" - " crc = texture2DRect(texU, gl_TexCoord[2].xy);\n" - " ccd = texture2DRect(texU, gl_TexCoord[3].xy);\n" - " ccu = texture2DRect(texU, gl_TexCoord[4].xy);\n" - " cld = texture2DRect(texU, gl_TexCoord[5].xy);\n" - " clu = texture2DRect(texU, gl_TexCoord[6].xy);\n" - " crd = texture2DRect(texU, gl_TexCoord[7].xy);\n" - " cru = texture2DRect(texU, TexRU.xy);\n" - " vec4 cu = ccc;\n" - " v4[0] = vec4(clc.g, ccc.g, ccd.b, ccc.b);\n" - " v4[1] = vec4(ccc.r, crc.r, ccd.a, ccc.a);\n" - " v4[2] = vec4(clc.a, ccc.a, ccc.r, ccu.r);\n" - " v4[3] = vec4(ccc.b, crc.b, ccc.g, ccu.g);\n" - " v6[0] = vec4(cld.a, clc.a, ccd.a, ccc.a);\n" - " v6[1] = vec4(ccd.b, ccc.b, crd.b, crc.b);\n" - " v6[2] = vec4(clc.g, clu.g, ccc.g, ccu.g);\n" - " v6[3] = vec4(ccc.r, ccu.r, crc.r, cru.r);\n" - << - " #define KEYTEST_STEP1(i)\\\n" - " if(key[i] == 1.0)\\\n" - " {\\\n" - " bvec4 test = lessThan(vec4(cc[i]), max(v4[i], v6[i])); \\\n" - " if(cc[i] < cu[i] || any(test))key[i] = 0.0; \\\n" - " }else if(key[i] == -1.0)\\\n" - " {\\\n" - " bvec4 test = greaterThan(vec4(cc[i]), min(v4[i], v6[i])); \\\n" - " if(cc[i] > cu[i] || any(test) )key[i] = 0.0; \\\n" - " }\n" - " REPEAT4(KEYTEST_STEP1);\n" - " if(any(notEqual(key, vec4(0.0)))) { \n" - << - " ccc = texture2DRect(texD, gl_TexCoord[0].xy);\n" - " clc = texture2DRect(texD, gl_TexCoord[1].xy);\n" - " crc = texture2DRect(texD, gl_TexCoord[2].xy);\n" - " ccd = texture2DRect(texD, gl_TexCoord[3].xy);\n" - " ccu = texture2DRect(texD, gl_TexCoord[4].xy);\n" - " cld = texture2DRect(texD, gl_TexCoord[5].xy);\n" - " clu = texture2DRect(texD, gl_TexCoord[6].xy);\n" - " crd = texture2DRect(texD, gl_TexCoord[7].xy);\n" - " cru = texture2DRect(texD, TexRU.xy);\n" - " vec4 cd = ccc;\n" - " v5[0] = vec4(clc.g, ccc.g, ccd.b, ccc.b);\n" - " v5[1] = vec4(ccc.r, crc.r, ccd.a, ccc.a);\n" - " v5[2] = vec4(clc.a, ccc.a, ccc.r, ccu.r);\n" - " v5[3] = vec4(ccc.b, crc.b, ccc.g, ccu.g);\n" - " v6[0] = vec4(cld.a, clc.a, ccd.a, ccc.a);\n" - " v6[1] = vec4(ccd.b, ccc.b, crd.b, crc.b);\n" - " v6[2] = vec4(clc.g, clu.g, ccc.g, ccu.g);\n" - " v6[3] = vec4(ccc.r, ccu.r, crc.r, cru.r);\n" - << - " #define KEYTEST_STEP2(i)\\\n" - " if(key[i] == 1.0)\\\n" - " {\\\n" - " bvec4 test = lessThan(vec4(cc[i]), max(v5[i], v6[i]));\\\n" - " if(cc[i] < cd[i] || any(test))key[i] = 0.0; \\\n" - " }else if(key[i] == -1.0)\\\n" - " {\\\n" - " bvec4 test = greaterThan(vec4(cc[i]), min(v5[i], v6[i]));\\\n" - " if(cc[i] > cd[i] || any(test))key[i] = 0.0; \\\n" - " }\n" - " REPEAT4(KEYTEST_STEP2);\n" - " float keysum = dot(abs(key), vec4(1, 1, 1, 1)) ;\n" - " //assume there is only one keypoint in the four. \n" - " if(keysum==1.0) {\n"; - - ////////////////////////////////////////////////////////////////////// - if(GlobalUtil::_SubpixelLocalization) - - out << - " vec3 offset = vec3(0.0, 0.0, 0.0); \n" - " #define TESTMOVE_KEYPOINT(idx) \\\n" - " if(key[idx] != 0.0) \\\n" - " {\\\n" - " cu[0] = cu[idx]; cd[0] = cd[idx]; cc[0] = cc[idx]; \\\n" - " v4[0] = v4[idx]; v5[0] = v5[idx]; \\\n" - " fxy[0] = fxy[idx]; fxx[0] = fxx[idx]; fyy[0] = fyy[idx]; \\\n" - " fx[0] = fx[idx]; fy[0] = fy[idx]; MOVE_EXTRA(idx); \\\n" - " }\n" - " TESTMOVE_KEYPOINT(1);\n" - " TESTMOVE_KEYPOINT(2);\n" - " TESTMOVE_KEYPOINT(3);\n" - << - - " float fs = 0.5*( cu[0] - cd[0] ); \n" - " float fss = cu[0] + cd[0] - cc[0] - cc[0];\n" - " float fxs = 0.25 * (v4[0].y + v5[0].x - v4[0].x - v5[0].y);\n" - " float fys = 0.25 * (v4[0].w + v5[0].z - v4[0].z - v5[0].w);\n" - " vec4 A0, A1, A2 ; \n" - " A0 = vec4(fxx[0], fxy[0], fxs, -fx[0]); \n" - " A1 = vec4(fxy[0], fyy[0], fys, -fy[0]); \n" - " A2 = vec4(fxs, fys, fss, -fs); \n" - " vec3 x3 = abs(vec3(fxx[0], fxy[0], fxs)); \n" - " float maxa = max(max(x3.x, x3.y), x3.z); \n" - " if(maxa >= 1e-10 ) \n" - " { \n" - " if(x3.y ==maxa ) \n" - " { \n" - " vec4 TEMP = A1; A1 = A0; A0 = TEMP; \n" - " }else if( x3.z == maxa ) \n" - " { \n" - " vec4 TEMP = A2; A2 = A0; A0 = TEMP; \n" - " } \n" - " A0 /= A0.x; \n" - " A1 -= A1.x * A0; \n" - " A2 -= A2.x * A0; \n" - " vec2 x2 = abs(vec2(A1.y, A2.y)); \n" - " if( x2.y > x2.x ) \n" - " { \n" - " vec3 TEMP = A2.yzw; \n" - " A2.yzw = A1.yzw; \n" - " A1.yzw = TEMP; \n" - " x2.x = x2.y; \n" - " } \n" - " if(x2.x >= 1e-10) { \n" - " A1.yzw /= A1.y; \n" - " A2.yzw -= A2.y * A1.yzw; \n" - " if(abs(A2.z) >= 1e-10) {\n" - " offset.z = A2.w /A2.z; \n" - " offset.y = A1.w - offset.z*A1.z; \n" - " offset.x = A0.w - offset.z*A0.z - offset.y*A0.y; \n" - " bool test = (abs(cc[0] + 0.5*dot(vec3(fx[0], fy[0], fs), offset ))>float(THRESHOLD1)) ;\n" - " if(!test || any( greaterThan(abs(offset), vec3(1.0)))) key = vec4(0.0);\n" - " }\n" - " }\n" - " }\n" - <<"\n" - " float keyv = dot(key, vec4(1.0, 2.0, 3.0, 4.0));\n" - " gl_FragColor = vec4(keyv, offset);\n" - " }}}}\n" - "}\n" <<'\0'; - - else out << "\n" - " float keyv = dot(key, vec4(1.0, 2.0, 3.0, 4.0));\n" - " gl_FragColor = vec4(keyv, 0.0, 0.0, 0.0);\n" - " }}}}\n" - "}\n" <<'\0'; - - ProgramGLSL * program = new ProgramGLSL(out.str().c_str()); - s_keypoint = program ; - - //parameter - _param_dog_texu = glGetUniformLocation(*program, "texU"); - _param_dog_texd = glGetUniformLocation(*program, "texD"); - if(GlobalUtil::_DarknessAdaption) _param_dog_texi = glGetUniformLocation(*program, "texI"); -} -void ShaderBagPKSL::SetDogTexParam(int texU, int texD) -{ - glUniform1i(_param_dog_texu, 1); - glUniform1i(_param_dog_texd, 2); - if(GlobalUtil::_DarknessAdaption)glUniform1i(_param_dog_texi, 3); -} -void ShaderBagPKSL::SetGenListStepParam(int tex, int tex0) -{ - glUniform1i(_param_genlist_step_tex0, 1); -} - -void ShaderBagPKSL::SetGenVBOParam(float width, float fwidth,float size) -{ - float sizes[4] = {size*3.0f, fwidth, width, 1.0f/width}; - glUniform4fv(_param_genvbo_size, 1, sizes); -} -void ShaderBagPKSL::SetGradPassParam(int texP) -{ - glUniform1i(_param_grad_pass_texp, 1); -} - -void ShaderBagPKSL::LoadDescriptorShader() -{ - GlobalUtil::_DescriptorPPT = 16; - LoadDescriptorShaderF2(); - s_rect_description = LoadDescriptorProgramRECT(); -} - -ProgramGLSL* ShaderBagPKSL::LoadDescriptorProgramRECT() -{ - //one shader outpout 128/8 = 16 , each fragout encodes 4 - //const double twopi = 2.0*3.14159265358979323846; - //const double rpi = 8.0/twopi; - ostringstream out; - out<<setprecision(8); - if(GlobalUtil::_KeepShaderLoop) - { - out << "#define REPEAT4(FUNCTION)\\\n" - "for(int i = 0; i < 4; ++i)\\\n" - "{\\\n" - " FUNCTION(i);\\\n" - "}\n"; - }else - { - //loop unroll for ATI - out << "#define REPEAT4(FUNCTION)\\\n" - "FUNCTION(0);\\\n" - "FUNCTION(1);\\\n" - "FUNCTION(2);\\\n" - "FUNCTION(3);\n"; - } - - out<<"\n" - "#define M_PI 3.14159265358979323846\n" - "#define TWO_PI (2.0*M_PI)\n" - "#define RPI 1.2732395447351626861510701069801\n" - "#define WF size.z\n" - "uniform sampler2DRect tex; \n" - "uniform sampler2DRect gtex; \n" - "uniform sampler2DRect otex; \n" - "uniform vec4 dsize; \n" - "uniform vec3 size; \n" - "void main() \n" - "{\n" - " vec2 dim = size.xy; //image size \n" - " float index = dsize.x*floor(gl_TexCoord[0].y * 0.5) + gl_TexCoord[0].x;\n" - " float idx = 8.0* fract(index * 0.125) + 8.0 * floor(2.0* fract(gl_TexCoord[0].y * 0.5)); \n" - " index = floor(index*0.125)+ 0.49; \n" - " vec2 coord = floor( vec2( mod(index, dsize.z), index*dsize.w)) + 0.5 ;\n" - " vec2 pos = texture2DRect(tex, coord).xy; \n" - " vec2 wsz = texture2DRect(tex, coord).zw;\n" - " float aspect_ratio = wsz.y / wsz.x;\n" - " float aspect_sq = aspect_ratio * aspect_ratio; \n" - " vec2 spt = wsz * 0.25; vec2 ispt = 1.0 / spt; \n"; - - //here cscs is actually (cos, sin, -cos, -sin) * (factor: 3)*sigma - //and rots is (cos, sin, -cos, -sin ) /(factor*sigma) - //devide the 4x4 sift grid into 16 1x1 block, and each corresponds to a shader thread - //To use linear interoplation, 1x1 is increased to 2x2, by adding 0.5 to each side - out<< - " vec4 temp; vec2 pt; \n" - " pt.x = pos.x + fract(idx*0.25) * wsz.x; \n" - " pt.y = pos.y + (floor(idx*0.25) + 0.5) * spt.y; \n"; - - //get a horizontal bounding box of the rotated rectangle - out<< - " vec4 sz; \n" - " sz.xy = max(pt - spt, vec2(2,2));\n" - " sz.zw = min(pt + spt, dim - vec2(3)); \n" - " sz = floor(sz * 0.5)+0.5;"; //move sample point to pixel center - //get voting for two box - - out<<"\n" - " vec4 DA, DB; vec2 spos; \n" - " DA = DB = vec4(0.0, 0.0, 0.0, 0.0); \n" - " vec4 nox = vec4(0.0, 1.0, 0.0, 1.0); \n" - " vec4 noy = vec4(0.0, 0.0, 1.0, 1.0); \n" - " for(spos.y = sz.y; spos.y <= sz.w; spos.y+=1.0) \n" - " { \n" - " for(spos.x = sz.x; spos.x <= sz.z; spos.x+=1.0) \n" - " { \n" - " vec2 tpt = spos * 2.0 - pt - 0.5; \n" - " vec4 nx = (tpt.x + nox) * ispt.x; \n" - " vec4 ny = (tpt.y + noy) * ispt.y; \n" - " vec4 nxn = abs(nx), nyn = abs(ny); \n" - " bvec4 inside = lessThan(max(nxn, nyn) , vec4(1.0)); \n" - " if(any(inside))\n" - " {\n" - " vec4 gg = texture2DRect(gtex, spos);\n" - " vec4 oo = texture2DRect(otex, spos);\n" - //" vec4 cc = cos(oo), ss = sin(oo); \n" - //" oo = atan(ss* aspect_ratio, cc); \n" - //" gg = gg * sqrt(ss * ss * aspect_sq + cc * cc); \n " - " vec4 theta0 = (- oo)*RPI;\n" - " vec4 theta = 8.0 * fract(1.0 + 0.125 * theta0); \n" - " vec4 theta1 = floor(theta); \n" - " vec4 weight = (vec4(1) - nxn) * (vec4(1) - nyn) * gg; \n" - " vec4 weight2 = (theta - theta1) * weight; \n" - " vec4 weight1 = weight - weight2; \n" - " #define ADD_DESCRIPTOR(i) \\\n" - " if(inside[i])\\\n" - " {\\\n" - " DA += vec4(equal(vec4(theta1[i]), vec4(0, 1, 2, 3)))*weight1[i]; \\\n" - " DA += vec4(equal(vec4(theta1[i]), vec4(7, 0, 1, 2)))*weight2[i]; \\\n" - " DB += vec4(equal(vec4(theta1[i]), vec4(4, 5, 6, 7)))*weight1[i]; \\\n" - " DB += vec4(equal(vec4(theta1[i]), vec4(3, 4, 5, 6)))*weight2[i]; \\\n" - " }\n" - " REPEAT4(ADD_DESCRIPTOR);\n" - " }\n" - " }\n" - " }\n"; - out<< - " gl_FragData[0] = DA; gl_FragData[1] = DB;\n" - "}\n"<<'\0'; - - ProgramGLSL * program = new ProgramGLSL(out.str().c_str()); - if(program->IsNative()) - { - return program; - } - else - { - delete program; - return NULL; - } -} - -ProgramGLSL* ShaderBagPKSL::LoadDescriptorProgramPKSL() -{ - //one shader outpout 128/8 = 16 , each fragout encodes 4 - //const double twopi = 2.0*3.14159265358979323846; - //const double rpi = 8.0/twopi; - ostringstream out; - out<<setprecision(8); - - if(GlobalUtil::_KeepShaderLoop) - { - out << "#define REPEAT4(FUNCTION)\\\n" - "for(int i = 0; i < 4; ++i)\\\n" - "{\\\n" - " FUNCTION(i);\\\n" - "}\n"; - }else - { - //loop unroll for ATI - out << "#define REPEAT4(FUNCTION)\\\n" - "FUNCTION(0);\\\n" - "FUNCTION(1);\\\n" - "FUNCTION(2);\\\n" - "FUNCTION(3);\n"; - } - - out<<"\n" - "#define M_PI 3.14159265358979323846\n" - "#define TWO_PI (2.0*M_PI)\n" - "#define RPI 1.2732395447351626861510701069801\n" - "#define WF size.z\n" - "uniform sampler2DRect tex; \n" - "uniform sampler2DRect gtex; \n" - "uniform sampler2DRect otex; \n" - "uniform vec4 dsize; \n" - "uniform vec3 size; \n" - "void main() \n" - "{\n" - " vec2 dim = size.xy; //image size \n" - " float index = dsize.x*floor(gl_TexCoord[0].y * 0.5) + gl_TexCoord[0].x;\n" - " float idx = 8.0* fract(index * 0.125) + 8.0 * floor(2.0* fract(gl_TexCoord[0].y * 0.5)); \n" - " index = floor(index*0.125)+ 0.49; \n" - " vec2 coord = floor( vec2( mod(index, dsize.z), index*dsize.w)) + 0.5 ;\n" - " vec2 pos = texture2DRect(tex, coord).xy; \n" - " if(any(lessThan(pos.xy, vec2(1.0))) || any(greaterThan(pos.xy, dim-1.0))) " - " //discard; \n" - " { gl_FragData[0] = gl_FragData[1] = vec4(0.0); return; }\n" - " float anglef = texture2DRect(tex, coord).z;\n" - " if(anglef > M_PI) anglef -= TWO_PI;\n" - " float sigma = texture2DRect(tex, coord).w; \n" - " float spt = abs(sigma * WF); //default to be 3*sigma \n"; - //rotation - out<< - " vec4 cscs, rots; \n" - " cscs.x = cos(anglef); cscs.y = sin(anglef); \n" - " cscs.zw = - cscs.xy; \n" - " rots = cscs /spt; \n" - " cscs *= spt; \n"; - - //here cscs is actually (cos, sin, -cos, -sin) * (factor: 3)*sigma - //and rots is (cos, sin, -cos, -sin ) /(factor*sigma) - //devide the 4x4 sift grid into 16 1x1 block, and each corresponds to a shader thread - //To use linear interoplation, 1x1 is increased to 2x2, by adding 0.5 to each side - out<< - " vec4 temp; vec2 pt, offsetpt; \n" - " /*the fraction part of idx is .5*/ \n" - " offsetpt.x = 4.0* fract(idx*0.25) - 2.0; \n" - " offsetpt.y = floor(idx*0.25) - 1.5; \n" - " temp = cscs.xwyx*offsetpt.xyxy; \n" - " pt = pos + temp.xz + temp.yw; \n"; - - //get a horizontal bounding box of the rotated rectangle - out<< - " vec2 bwin = abs(cscs.xy); \n" - " float bsz = bwin.x + bwin.y; \n" - " vec4 sz; \n" - " sz.xy = max(pt - vec2(bsz), vec2(2,2));\n" - " sz.zw = min(pt + vec2(bsz), dim - vec2(3)); \n" - " sz = floor(sz * 0.5)+0.5;"; //move sample point to pixel center - //get voting for two box - - out<<"\n" - " vec4 DA, DB; vec2 spos; \n" - " DA = DB = vec4(0.0, 0.0, 0.0, 0.0); \n" - " vec4 nox = vec4(0.0, rots.xy, rots.x + rots.y); \n" - " vec4 noy = vec4(0.0, rots.wx, rots.w + rots.x); \n" - " for(spos.y = sz.y; spos.y <= sz.w; spos.y+=1.0) \n" - " { \n" - " for(spos.x = sz.x; spos.x <= sz.z; spos.x+=1.0) \n" - " { \n" - " vec2 tpt = spos * 2.0 - pt - 0.5; \n" - " vec4 temp = rots.xywx * tpt.xyxy; \n" - " vec2 temp2 = temp.xz + temp.yw; \n" - " vec4 nx = temp2.x + nox; \n" - " vec4 ny = temp2.y + noy; \n" - " vec4 nxn = abs(nx), nyn = abs(ny); \n" - " bvec4 inside = lessThan(max(nxn, nyn) , vec4(1.0)); \n" - " if(any(inside))\n" - " {\n" - " vec4 gg = texture2DRect(gtex, spos);\n" - " vec4 oo = texture2DRect(otex, spos);\n" - " vec4 theta0 = (anglef - oo)*RPI;\n" - " vec4 theta = 8.0 * fract(1.0 + 0.125 * theta0); \n" - " vec4 theta1 = floor(theta); \n" - " vec4 diffx = nx + offsetpt.x, diffy = ny + offsetpt.y; \n" - " vec4 ww = exp(-0.125 * (diffx * diffx + diffy * diffy )); \n" - " vec4 weight = (vec4(1) - nxn) * (vec4(1) - nyn) * gg * ww; \n" - " vec4 weight2 = (theta - theta1) * weight; \n" - " vec4 weight1 = weight - weight2; \n" - " #define ADD_DESCRIPTOR(i) \\\n" - " if(inside[i])\\\n" - " {\\\n" - " DA += vec4(equal(vec4(theta1[i]), vec4(0, 1, 2, 3)))*weight1[i]; \\\n" - " DA += vec4(equal(vec4(theta1[i]), vec4(7, 0, 1, 2)))*weight2[i]; \\\n" - " DB += vec4(equal(vec4(theta1[i]), vec4(4, 5, 6, 7)))*weight1[i]; \\\n" - " DB += vec4(equal(vec4(theta1[i]), vec4(3, 4, 5, 6)))*weight2[i]; \\\n" - " }\n" - " REPEAT4(ADD_DESCRIPTOR);\n" - " }\n" - " }\n" - " }\n"; - out<< - " gl_FragData[0] = DA; gl_FragData[1] = DB;\n" - "}\n"<<'\0'; - - ProgramGLSL * program = new ProgramGLSL(out.str().c_str()); - if(program->IsNative()) - { - return program; - } - else - { - delete program; - return NULL; - } -} - -void ShaderBagPKSL::LoadDescriptorShaderF2() -{ - - ProgramGLSL * program = LoadDescriptorProgramPKSL(); - if( program ) - { - s_descriptor_fp = program; - _param_descriptor_gtex = glGetUniformLocation(*program, "gtex"); - _param_descriptor_otex = glGetUniformLocation(*program, "otex"); - _param_descriptor_size = glGetUniformLocation(*program, "size"); - _param_descriptor_dsize = glGetUniformLocation(*program, "dsize"); - } -} - - - -void ShaderBagPKSL::SetSimpleOrientationInput(int oTex, float sigma, float sigma_step) -{ - glUniform1i(_param_orientation_gtex, 1); - glUniform2f(_param_orientation_size, sigma, sigma_step); -} - - -void ShaderBagPKSL::SetFeatureOrientationParam(int gtex, int width, int height, float sigma, int otex, float step) -{ - /// - glUniform1i(_param_orientation_gtex, 1); - glUniform1i(_param_orientation_otex, 2); - - float size[4]; - size[0] = (float)width; - size[1] = (float)height; - size[2] = sigma; - size[3] = step; - glUniform4fv(_param_orientation_size, 1, size); -} - -void ShaderBagPKSL::SetFeatureDescirptorParam(int gtex, int otex, float dwidth, float fwidth, float width, float height, float sigma) -{ - if(sigma == 0 && s_rect_description) - { - //rectangle description mode - s_rect_description->UseProgram(); - GLint param_descriptor_gtex = glGetUniformLocation(*s_rect_description, "gtex"); - GLint param_descriptor_otex = glGetUniformLocation(*s_rect_description, "otex"); - GLint param_descriptor_size = glGetUniformLocation(*s_rect_description, "size"); - GLint param_descriptor_dsize = glGetUniformLocation(*s_rect_description, "dsize"); - /// - glUniform1i(param_descriptor_gtex, 1); - glUniform1i(param_descriptor_otex, 2); - - float dsize[4] ={dwidth, 1.0f/dwidth, fwidth, 1.0f/fwidth}; - glUniform4fv(param_descriptor_dsize, 1, dsize); - float size[3]; - size[0] = width; - size[1] = height; - size[2] = GlobalUtil::_DescriptorWindowFactor; - glUniform3fv(param_descriptor_size, 1, size); - }else - { - /// - glUniform1i(_param_descriptor_gtex, 1); - glUniform1i(_param_descriptor_otex, 2); - - - float dsize[4] ={dwidth, 1.0f/dwidth, fwidth, 1.0f/fwidth}; - glUniform4fv(_param_descriptor_dsize, 1, dsize); - float size[3]; - size[0] = width; - size[1] = height; - size[2] = GlobalUtil::_DescriptorWindowFactor; - glUniform3fv(_param_descriptor_size, 1, size); - } - -} - - -void ShaderBagPKSL::SetGenListEndParam(int ktex) -{ - glUniform1i(_param_genlist_end_ktex, 1); -} -void ShaderBagPKSL::SetGenListInitParam(int w, int h) -{ - float bbox[4] = {(w -1.0f) * 0.5f +0.25f, (w-1.0f) * 0.5f - 0.25f, (h - 1.0f) * 0.5f + 0.25f, (h-1.0f) * 0.5f - 0.25f}; - glUniform4fv(_param_genlist_init_bbox, 1, bbox); -} - -void ShaderBagPKSL::SetMarginCopyParam(int xmax, int ymax) -{ - float truncate[4]; - truncate[0] = (xmax - 0.5f) * 0.5f; //((xmax + 1) >> 1) - 0.5f; - truncate[1] = (ymax - 0.5f) * 0.5f; //((ymax + 1) >> 1) - 0.5f; - truncate[2] = (xmax %2 == 1)? 0.0f: 1.0f; - truncate[3] = truncate[2] + (((ymax % 2) == 1)? 0.0f : 2.0f); - glUniform4fv(_param_margin_copy_truncate, 1, truncate); -} diff --git a/3rdparty/SiftGPU/src/SiftGPU/ProgramGLSL.h b/3rdparty/SiftGPU/src/SiftGPU/ProgramGLSL.h deleted file mode 100644 index cc949539..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/ProgramGLSL.h +++ /dev/null @@ -1,267 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: ProgramGLSL.h -// Author: Changchang Wu -// Description : Interface for ProgramGLSL classes -// ProgramGLSL: Glsl Program -// FilterGLSL: Glsl Gaussian Filters -// ShaderBag: base class of ShaderBagPKSL and ShaderBagGLSL -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - - -#ifndef _PROGRAM_GLSL_H -#define _PROGRAM_GLSL_H - - -#include "ProgramGPU.h" - -class ProgramGLSL:public ProgramGPU -{ - class ShaderObject - { - GLuint _shaderID; - int _type; - int _compiled; - static int ReadShaderFile(const char * source, char *& code); - void CheckCompileLog(); - public: - void PrintCompileLog(ostream & os ); - int inline IsValidShaderObject(){ return _shaderID && _compiled;} - int IsValidVertexShader(); - int IsValidFragmentShader(); - GLuint GetShaderID(){return _shaderID;} - ~ShaderObject(); - ShaderObject(int shadertype, const char * source, int filesource =0); - }; - -protected: - int _linked; - GLint _TextureParam0; - GLuint _programID; -private: - void AttachShaderObject(ShaderObject& shader); - void DetachShaderObject(ShaderObject& shader); - -public: - void ReLink(); - int IsNative(); - int UseProgram(); - void PrintLinkLog(std::ostream&os); - int ValidateProgram(); - void CheckLinkLog(); - int LinkProgram(); - operator GLuint (){return _programID;} - virtual void * GetProgramID() { return (void*) _programID; } -public: - ProgramGLSL(); - ~ProgramGLSL(); - ProgramGLSL(const char* frag_source); -}; - - -class GLTexImage; -class FilterGLSL : public FilterProgram -{ -private: - ProgramGPU* CreateFilterH(float kernel[], int width); - ProgramGPU* CreateFilterV(float kernel[], int height); - ProgramGPU* CreateFilterHPK(float kernel[], int width); - ProgramGPU* CreateFilterVPK(float kernel[], int height); -public: - void MakeFilterProgram(float kernel[], int width); -public: - FilterGLSL(float sigma) ; -}; - -class SiftParam; - -///////////////////////////////////////////////////////////////////////////////// -//class ShaderBag -//desciption: pure virtual class -// provides storage and usage interface of all the shaders for SIFT -// two implementations are ShaderBagPKSL and ShaderBagGLSL -///////////////////////////////////////////////////////////////////////////////// -class ShaderBag -{ -public: - //shader: rgb to gray - ProgramGPU * s_gray; - //shader: copy keypoint to PBO - ProgramGPU * s_copy_key; - //shader: debug view - ProgramGPU * s_debug; - //shader: orientation - //shader: assign simple orientation to keypoints if hardware is low - ProgramGPU * s_orientation; - //shader: display gaussian levels - ProgramGPU * s_display_gaussian; - //shader: display difference of gassian - ProgramGPU * s_display_dog; - //shader: display gradient - ProgramGPU * s_display_grad; - //shader: display keypoints as red(maximum) and blue (minimum) - ProgramGPU * s_display_keys; - //shader: up/down-sample - ProgramGPU * s_sampling; - //shader: compute gradient/dog - ProgramGPU * s_grad_pass; - ProgramGPU * s_dog_pass; - //shader: keypoint detection in one pass - ProgramGPU * s_keypoint; - ProgramGPU * s_seperate_sp; - //shader: feature list generations.. - ProgramGPU * s_genlist_init_tight; - ProgramGPU * s_genlist_init_ex; - ProgramGPU * s_genlist_histo; - ProgramGPU * s_genlist_start; - ProgramGPU * s_genlist_step; - ProgramGPU * s_genlist_end; - ProgramGPU * s_zero_pass; - //shader: generate vertex to display SIFT as a square - ProgramGPU * s_vertex_list; - //shader: descriptor - ProgramGPU * s_descriptor_fp; - //shader: copy pixels to margin - ProgramGPU * s_margin_copy; -public: - FilterProgram * f_gaussian_skip0; - vector<FilterProgram*> f_gaussian_skip0_v; - FilterProgram * f_gaussian_skip1; - FilterProgram ** f_gaussian_step; - int _gaussian_step_num; -public: - virtual void SetGenListInitParam(int w, int h){}; - virtual void SetGenListEndParam(int ktex){}; - virtual void SetMarginCopyParam(int xmax, int ymax){}; - virtual void LoadDescriptorShader(){}; - virtual void SetFeatureDescirptorParam(int gtex, int otex, float dwidth, float fwidth, float width, float height, float sigma){}; - virtual void SetFeatureOrientationParam(int gtex, int width, int height, float sigma, int stex, float step){}; - virtual void SetSimpleOrientationInput(int oTex, float sigma, float sigma_step){}; - virtual void LoadOrientationShader() =0; - virtual void SetGenListStartParam(float width, int tex0) =0; - virtual void LoadGenListShader(int ndoglev, int nlev)=0; - virtual void UnloadProgram()=0; - virtual void LoadKeypointShader(float threshold, float edgeTrheshold) = 0; - virtual void LoadFixedShaders()=0; - virtual void LoadDisplayShaders() = 0; - virtual void SetDogTexParam(int texU, int texD)=0; - virtual void SetGradPassParam(int texP=0){} - virtual void SetGenListStepParam(int tex, int tex0) = 0; - virtual void SetGenVBOParam( float width, float fwidth, float size)=0; -public: - void CreateGaussianFilters(SiftParam&param); - void SelectInitialSmoothingFilter(int octave_min, SiftParam&param); - void LoadDynamicShaders(SiftParam& param); - ShaderBag(); - virtual ~ShaderBag(); -}; - - -class ShaderBagGLSL:public ShaderBag -{ - GLint _param_dog_texu; - GLint _param_dog_texd; - GLint _param_ftex_width; - GLint _param_genlist_start_tex0; - GLint _param_genlist_step_tex0; - GLint _param_genvbo_size; - GLint _param_orientation_gtex; - GLint _param_orientation_size; - GLint _param_orientation_stex; - GLint _param_margin_copy_truncate; - GLint _param_genlist_init_bbox; - GLint _param_descriptor_gtex; - GLint _param_descriptor_size; - GLint _param_descriptor_dsize; -public: - virtual void SetMarginCopyParam(int xmax, int ymax); - void SetSimpleOrientationInput(int oTex, float sigma, float sigma_step); - void LoadOrientationShader(); - void LoadDescriptorShaderF2(); - virtual void LoadDescriptorShader(); - virtual void SetFeatureOrientationParam(int gtex, int width, int height, float sigma, int stex = 0, float step = 1.0f); - virtual void SetFeatureDescirptorParam(int gtex, int otex, float dwidth, float fwidth, float width, float height, float sigma); - static void WriteOrientationCodeToStream(ostream& out); - static ProgramGLSL* LoadGenListStepShader(int start, int step); - virtual void SetGenListInitParam(int w, int h); - virtual void SetGenListStartParam(float width, int tex0); - virtual void LoadGenListShader(int ndoglev, int nlev); - virtual void UnloadProgram(); - virtual void LoadKeypointShader(float threshold, float edgeTrheshold); - virtual void LoadFixedShaders(); - virtual void LoadDisplayShaders(); - virtual void SetDogTexParam(int texU, int texD); - virtual void SetGenListStepParam(int tex, int tex0); - virtual void SetGenVBOParam( float width, float fwidth, float size); - virtual ~ShaderBagGLSL(){} -}; - - -class ShaderBagPKSL:public ShaderBag -{ -private: - GLint _param_dog_texu; - GLint _param_dog_texd; - GLint _param_dog_texi; - GLint _param_margin_copy_truncate; - GLint _param_grad_pass_texp; - GLint _param_genlist_init_bbox; - GLint _param_genlist_start_tex0; - GLint _param_ftex_width; - GLint _param_genlist_step_tex0; - GLint _param_genlist_end_ktex; - GLint _param_genvbo_size; - GLint _param_orientation_gtex; - GLint _param_orientation_otex; - GLint _param_orientation_size; - GLint _param_descriptor_gtex; - GLint _param_descriptor_otex; - GLint _param_descriptor_size; - GLint _param_descriptor_dsize; - - // - ProgramGLSL* s_rect_description; -public: - ShaderBagPKSL () {s_rect_description = NULL; } - virtual ~ShaderBagPKSL() {if(s_rect_description) delete s_rect_description; } - virtual void LoadFixedShaders(); - virtual void LoadDisplayShaders(); - virtual void LoadOrientationShader() ; - virtual void SetGenListStartParam(float width, int tex0) ; - virtual void LoadGenListShader(int ndoglev, int nlev); - virtual void UnloadProgram(); - virtual void LoadKeypointShader(float threshold, float edgeTrheshold) ; - virtual void LoadDescriptorShader(); - virtual void LoadDescriptorShaderF2(); - static ProgramGLSL* LoadDescriptorProgramRECT(); - static ProgramGLSL* LoadDescriptorProgramPKSL(); -///////////////// - virtual void SetDogTexParam(int texU, int texD); - virtual void SetGradPassParam(int texP); - virtual void SetGenListStepParam(int tex, int tex0); - virtual void SetGenVBOParam( float width, float fwidth, float size); - virtual void SetFeatureDescirptorParam(int gtex, int otex, float dwidth, float fwidth, float width, float height, float sigma); - virtual void SetFeatureOrientationParam(int gtex, int width, int height, float sigma, int stex, float step); - virtual void SetSimpleOrientationInput(int oTex, float sigma, float sigma_step); - virtual void SetGenListEndParam(int ktex); - virtual void SetGenListInitParam(int w, int h); - virtual void SetMarginCopyParam(int xmax, int ymax); -}; - - -#endif - diff --git a/3rdparty/SiftGPU/src/SiftGPU/ProgramGPU.cpp b/3rdparty/SiftGPU/src/SiftGPU/ProgramGPU.cpp deleted file mode 100644 index c5a03ec8..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/ProgramGPU.cpp +++ /dev/null @@ -1,38 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: ProgramGPU.cpp -// Author: Changchang Wu -// Description : Implementation of ProgramGPU and FilterProgram -// This part is independent of GPU language -// -// -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - - -#include "GL/glew.h" -#include <iostream> -#include <vector> -#include <math.h> -using namespace std; - -#include "GlobalUtil.h" -#include "GLTexImage.h" -#include "ShaderMan.h" -#include "ProgramGPU.h" -#include "ProgramGLSL.h" -#include "SiftGPU.h" - diff --git a/3rdparty/SiftGPU/src/SiftGPU/ProgramGPU.h b/3rdparty/SiftGPU/src/SiftGPU/ProgramGPU.h deleted file mode 100644 index 203e52db..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/ProgramGPU.h +++ /dev/null @@ -1,59 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: ProgramGPU.h -// Author: Changchang Wu -// Description : Based class for GPU programs -// ProgramGPU: base class of ProgramGLSL -// FilterProgram: base class of FilterGLSL, FilterPKSL -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - - -#ifndef _PROGRAM_GPU_H -#define _PROGRAM_GPU_H - -//////////////////////////////////////////////////////////////////////////// -//class ProgramGPU -//description: pure virtual class -// provides a common interface for shader programs -/////////////////////////////////////////////////////////////////////////// -class ProgramGPU -{ -public: - //use a gpu program - virtual int UseProgram() = 0; - virtual void* GetProgramID() = 0; - //not used - virtual ~ProgramGPU(){}; -}; - -/////////////////////////////////////////////////////////////////////////// -//class FilterProgram -/////////////////////////////////////////////////////////////////////////// -class FilterProgram -{ -public: - ProgramGPU* s_shader_h; - ProgramGPU* s_shader_v; - int _size; - int _id; -public: - FilterProgram() { s_shader_h = s_shader_v = NULL; _size = _id = 0; } - virtual ~FilterProgram() { if(s_shader_h) delete s_shader_h; if(s_shader_v) delete s_shader_v;} -}; - -#endif - diff --git a/3rdparty/SiftGPU/src/SiftGPU/PyramidCL.cpp b/3rdparty/SiftGPU/src/SiftGPU/PyramidCL.cpp deleted file mode 100644 index 3d8d82ae..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/PyramidCL.cpp +++ /dev/null @@ -1,1132 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: PyramidCL.cpp -// Author: Changchang Wu -// Description : implementation of the PyramidCL class. -// OpenCL-based implementation of SiftPyramid -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - -#if defined(CL_SIFTGPU_ENABLED) - - -#include "GL/glew.h" -#include <CL/OpenCL.h> -#include <iostream> -#include <vector> -#include <algorithm> -#include <stdlib.h> -#include <math.h> -using namespace std; - -#include "GlobalUtil.h" -#include "GLTexImage.h" -#include "CLTexImage.h" -#include "SiftGPU.h" -#include "SiftPyramid.h" -#include "ProgramCL.h" -#include "PyramidCL.h" - - -#define USE_TIMING() double t, t0, tt; -#define OCTAVE_START() if(GlobalUtil::_timingO){ t = t0 = CLOCK(); cout<<"#"<<i+_down_sample_factor<<"\t"; } -#define LEVEL_FINISH() if(GlobalUtil::_timingL){ _OpenCL->FinishCL(); tt = CLOCK();cout<<(tt-t)<<"\t"; t = CLOCK();} -#define OCTAVE_FINISH() if(GlobalUtil::_timingO)cout<<"|\t"<<(CLOCK()-t0)<<endl; - - -PyramidCL::PyramidCL(SiftParam& sp) : SiftPyramid(sp) -{ - _allPyramid = NULL; - _histoPyramidTex = NULL; - _featureTex = NULL; - _descriptorTex = NULL; - _orientationTex = NULL; - _bufferTEX = NULL; - if(GlobalUtil::_usePackedTex) _OpenCL = new ProgramBagCL(); - else _OpenCL = new ProgramBagCLN(); - _OpenCL->InitProgramBag(sp); - _inputTex = new CLTexImage( _OpenCL->GetContextCL(), - _OpenCL->GetCommandQueue()); - ///////////////////////// - InitializeContext(); -} - -PyramidCL::~PyramidCL() -{ - DestroyPerLevelData(); - DestroySharedData(); - DestroyPyramidData(); - if(_OpenCL) delete _OpenCL; - if(_inputTex) delete _inputTex; - if(_bufferTEX) delete _bufferTEX; -} - -void PyramidCL::InitializeContext() -{ - GlobalUtil::InitGLParam(1); -} - -void PyramidCL::InitPyramid(int w, int h, int ds) -{ - int wp, hp, toobig = 0; - if(ds == 0) - { - _down_sample_factor = 0; - if(GlobalUtil::_octave_min_default>=0) - { - wp = w >> _octave_min_default; - hp = h >> _octave_min_default; - }else - { - //can't upsample by more than 8 - _octave_min_default = max(-3, _octave_min_default); - // - wp = w << (-_octave_min_default); - hp = h << (-_octave_min_default); - } - _octave_min = _octave_min_default; - }else - { - //must use 0 as _octave_min; - _octave_min = 0; - _down_sample_factor = ds; - w >>= ds; - h >>= ds; - wp = w; - hp = h; - } - - while(wp > GlobalUtil::_texMaxDim || hp > GlobalUtil::_texMaxDim ) - { - _octave_min ++; - wp >>= 1; - hp >>= 1; - toobig = 1; - } - if(toobig && GlobalUtil::_verbose && _octave_min > 0) - { - std::cout<< "**************************************************************\n" - "Image larger than allowed dimension, data will be downsampled!\n" - "use -maxd to change the settings\n" - "***************************************************************\n"; - } - - if( wp == _pyramid_width && hp == _pyramid_height && _allocated ) - { - FitPyramid(wp, hp); - }else if(GlobalUtil::_ForceTightPyramid || _allocated ==0) - { - ResizePyramid(wp, hp); - } - else if( wp > _pyramid_width || hp > _pyramid_height ) - { - ResizePyramid(max(wp, _pyramid_width), max(hp, _pyramid_height)); - if(wp < _pyramid_width || hp < _pyramid_height) FitPyramid(wp, hp); - } - else - { - //try use the pyramid allocated for large image on small input images - FitPyramid(wp, hp); - } - - _OpenCL->SelectInitialSmoothingFilter(_octave_min + _down_sample_factor, param); -} - -void PyramidCL::ResizePyramid(int w, int h) -{ - // - unsigned int totalkb = 0; - int _octave_num_new, input_sz, i, j; - // - - if(_pyramid_width == w && _pyramid_height == h && _allocated) return; - - if(w > GlobalUtil::_texMaxDim || h > GlobalUtil::_texMaxDim) return ; - - if(GlobalUtil::_verbose && GlobalUtil::_timingS) std::cout<<"[Allocate Pyramid]:\t" <<w<<"x"<<h<<endl; - //first octave does not change - _pyramid_octave_first = 0; - - - //compute # of octaves - input_sz = min(w,h) ; - _pyramid_width = w; - _pyramid_height = h; - - //reset to preset parameters - _octave_num_new = GlobalUtil::_octave_num_default; - - if(_octave_num_new < 1) _octave_num_new = GetRequiredOctaveNum(input_sz) ; - - if(_pyramid_octave_num != _octave_num_new) - { - //destroy the original pyramid if the # of octave changes - if(_octave_num >0) - { - DestroyPerLevelData(); - DestroyPyramidData(); - } - _pyramid_octave_num = _octave_num_new; - } - - _octave_num = _pyramid_octave_num; - - int noct = _octave_num; - int nlev = param._level_num; - int texNum = noct* nlev * DATA_NUM; - - // //initialize the pyramid - if(_allPyramid==NULL) - { - _allPyramid = new CLTexImage[ texNum]; - cl_context context = _OpenCL->GetContextCL(); - cl_command_queue queue = _OpenCL->GetCommandQueue(); - for(i = 0; i < texNum; ++i) _allPyramid[i].SetContext(context, queue); - } - - - - CLTexImage * gus = GetBaseLevel(_octave_min, DATA_GAUSSIAN); - CLTexImage * dog = GetBaseLevel(_octave_min, DATA_DOG); - CLTexImage * grd = GetBaseLevel(_octave_min, DATA_GRAD); - CLTexImage * rot = GetBaseLevel(_octave_min, DATA_ROT); - CLTexImage * key = GetBaseLevel(_octave_min, DATA_KEYPOINT); - - ////////////there could be "out of memory" happening during the allocation - - - - for(i = 0; i< noct; i++) - { - for( j = 0; j< nlev; j++, gus++, dog++, grd++, rot++, key++) - { - gus->InitPackedTex(w, h, GlobalUtil::_usePackedTex); - if(j==0)continue; - dog->InitPackedTex(w, h, GlobalUtil::_usePackedTex); - if(j < 1 + param._dog_level_num) - { - grd->InitPackedTex(w, h, GlobalUtil::_usePackedTex); - rot->InitPackedTex(w, h, GlobalUtil::_usePackedTex); - } - if(j > 1 && j < nlev -1) key->InitPackedTex(w, h, GlobalUtil::_usePackedTex); - } - //////////////////////////////////////// - int tsz = (gus -1)->GetTexPixelCount() * 16; - totalkb += ((nlev *5 -6)* tsz / 1024); - //several auxilary textures are not actually required - w>>=1; - h>>=1; - } - - totalkb += ResizeFeatureStorage(); - - _allocated = 1; - - if(GlobalUtil::_verbose && GlobalUtil::_timingS) std::cout<<"[Allocate Pyramid]:\t" <<(totalkb/1024)<<"MB\n"; - -} - -void PyramidCL::FitPyramid(int w, int h) -{ - _pyramid_octave_first = 0; - // - _octave_num = GlobalUtil::_octave_num_default; - - int _octave_num_max = GetRequiredOctaveNum(min(w, h)); - - if(_octave_num < 1 || _octave_num > _octave_num_max) - { - _octave_num = _octave_num_max; - } - - - int pw = _pyramid_width>>1, ph = _pyramid_height>>1; - while(_pyramid_octave_first + _octave_num < _pyramid_octave_num && - pw >= w && ph >= h) - { - _pyramid_octave_first++; - pw >>= 1; - ph >>= 1; - } - - ////////////////// - for(int i = 0; i < _octave_num; i++) - { - CLTexImage * tex = GetBaseLevel(i + _octave_min); - CLTexImage * dog = GetBaseLevel(i + _octave_min, DATA_DOG); - CLTexImage * grd = GetBaseLevel(i + _octave_min, DATA_GRAD); - CLTexImage * rot = GetBaseLevel(i + _octave_min, DATA_ROT); - CLTexImage * key = GetBaseLevel(i + _octave_min, DATA_KEYPOINT); - for(int j = param._level_min; j <= param._level_max; j++, tex++, dog++, grd++, rot++, key++) - { - tex->SetPackedSize(w, h, GlobalUtil::_usePackedTex); - if(j == param._level_min) continue; - dog->SetPackedSize(w, h, GlobalUtil::_usePackedTex); - if(j < param._level_max - 1) - { - grd->SetPackedSize(w, h, GlobalUtil::_usePackedTex); - rot->SetPackedSize(w, h, GlobalUtil::_usePackedTex); - } - if(j > param._level_min + 1 && j < param._level_max) key->SetPackedSize(w, h, GlobalUtil::_usePackedTex); - } - w>>=1; - h>>=1; - } -} - - -void PyramidCL::SetLevelFeatureNum(int idx, int fcount) -{ - _featureTex[idx].InitBufferTex(fcount, 1, 4); - _levelFeatureNum[idx] = fcount; -} - -int PyramidCL::ResizeFeatureStorage() -{ - int totalkb = 0; - if(_levelFeatureNum==NULL) _levelFeatureNum = new int[_octave_num * param._dog_level_num]; - std::fill(_levelFeatureNum, _levelFeatureNum+_octave_num * param._dog_level_num, 0); - - cl_context context = _OpenCL->GetContextCL(); - cl_command_queue queue = _OpenCL->GetCommandQueue(); - int wmax = GetBaseLevel(_octave_min)->GetImgWidth() * 2; - int hmax = GetBaseLevel(_octave_min)->GetImgHeight() * 2; - int whmax = max(wmax, hmax); - int w, i; - - // - int num = (int)ceil(log(double(whmax))/log(4.0)); - - if( _hpLevelNum != num) - { - _hpLevelNum = num; - if(_histoPyramidTex ) delete [] _histoPyramidTex; - _histoPyramidTex = new CLTexImage[_hpLevelNum]; - for(i = 0; i < _hpLevelNum; ++i) _histoPyramidTex[i].SetContext(context, queue); - } - - for(i = 0, w = 1; i < _hpLevelNum; i++) - { - _histoPyramidTex[i].InitBufferTex(w, whmax, 4); - w<<=2; - } - - // (4 ^ (_hpLevelNum) -1 / 3) pixels - totalkb += (((1 << (2 * _hpLevelNum)) -1) / 3 * 16 / 1024); - - //initialize the feature texture - int idx = 0, n = _octave_num * param._dog_level_num; - if(_featureTex==NULL) - { - _featureTex = new CLTexImage[n]; - for(i = 0; i <n; ++i) _featureTex[i].SetContext(context, queue); - } - if(GlobalUtil::_MaxOrientation >1 && GlobalUtil::_OrientationPack2==0 && _orientationTex== NULL) - { - _orientationTex = new CLTexImage[n]; - for(i = 0; i < n; ++i) _orientationTex[i].SetContext(context, queue); - } - - - for(i = 0; i < _octave_num; i++) - { - CLTexImage * tex = GetBaseLevel(i+_octave_min); - int fmax = int(4 * tex->GetTexWidth() * tex->GetTexHeight()*GlobalUtil::_MaxFeaturePercent); - // - if(fmax > GlobalUtil::_MaxLevelFeatureNum) fmax = GlobalUtil::_MaxLevelFeatureNum; - else if(fmax < 32) fmax = 32; //give it at least a space of 32 feature - - for(int j = 0; j < param._dog_level_num; j++, idx++) - { - _featureTex[idx].InitBufferTex(fmax, 1, 4); - totalkb += fmax * 16 /1024; - // - if(GlobalUtil::_MaxOrientation>1 && GlobalUtil::_OrientationPack2 == 0) - { - _orientationTex[idx].InitBufferTex(fmax, 1, 4); - totalkb += fmax * 16 /1024; - } - } - } - - //this just need be initialized once - if(_descriptorTex==NULL) - { - //initialize feature texture pyramid - int fmax = _featureTex->GetImgWidth(); - _descriptorTex = new CLTexImage(context, queue); - totalkb += ( fmax /2); - _descriptorTex->InitBufferTex(fmax *128, 1, 1); - }else - { - totalkb += _descriptorTex->GetDataSize()/1024; - } - return totalkb; -} - -void PyramidCL::GetFeatureDescriptors() -{ - //descriptors... - /*float* pd = &_descriptor_buffer[0]; - vector<float> descriptor_buffer2; - - //use another buffer if we need to re-order the descriptors - if(_keypoint_index.size() > 0) - { - descriptor_buffer2.resize(_descriptor_buffer.size()); - pd = &descriptor_buffer2[0]; - } - - CLTexImage * got, * ftex= _featureTex; - for(int i = 0, idx = 0; i < _octave_num; i++) - { - got = GetBaseLevel(i + _octave_min, DATA_GRAD) + 1; - for(int j = 0; j < param._dog_level_num; j++, ftex++, idx++, got++) - { - if(_levelFeatureNum[idx]==0) continue; - ProgramCL::ComputeDescriptor(ftex, got, _descriptorTex);//process - _descriptorTex->CopyToHost(pd); //readback descriptor - pd += 128*_levelFeatureNum[idx]; - } - } - - if(GlobalUtil::_timingS) _OpenCL->FinishCL(); - - if(_keypoint_index.size() > 0) - { - //put the descriptor back to the original order for keypoint list. - for(int i = 0; i < _featureNum; ++i) - { - int index = _keypoint_index[i]; - memcpy(&_descriptor_buffer[index*128], &descriptor_buffer2[i*128], 128 * sizeof(float)); - } - }*/ -} - -void PyramidCL::GenerateFeatureListTex() -{ - - vector<float> list; - int idx = 0; - const double twopi = 2.0*3.14159265358979323846; - float sigma_half_step = powf(2.0f, 0.5f / param._dog_level_num); - float octave_sigma = _octave_min>=0? float(1<<_octave_min): 1.0f/(1<<(-_octave_min)); - float offset = GlobalUtil::_LoweOrigin? 0 : 0.5f; - if(_down_sample_factor>0) octave_sigma *= float(1<<_down_sample_factor); - - _keypoint_index.resize(0); // should already be 0 - for(int i = 0; i < _octave_num; i++, octave_sigma*= 2.0f) - { - for(int j = 0; j < param._dog_level_num; j++, idx++) - { - list.resize(0); - float level_sigma = param.GetLevelSigma(j + param._level_min + 1) * octave_sigma; - float sigma_min = level_sigma / sigma_half_step; - float sigma_max = level_sigma * sigma_half_step; - int fcount = 0 ; - for(int k = 0; k < _featureNum; k++) - { - float * key = &_keypoint_buffer[k*4]; - if( (key[2] >= sigma_min && key[2] < sigma_max) - ||(key[2] < sigma_min && i ==0 && j == 0) - ||(key[2] > sigma_max && i == _octave_num -1 && j == param._dog_level_num - 1)) - { - //add this keypoint to the list - list.push_back((key[0] - offset) / octave_sigma + 0.5f); - list.push_back((key[1] - offset) / octave_sigma + 0.5f); - list.push_back(key[2] / octave_sigma); - list.push_back((float)fmod(twopi-key[3], twopi)); - fcount ++; - //save the index of keypoints - _keypoint_index.push_back(k); - } - - } - - _levelFeatureNum[idx] = fcount; - if(fcount==0)continue; - CLTexImage * ftex = _featureTex+idx; - - SetLevelFeatureNum(idx, fcount); - ftex->CopyFromHost(&list[0]); - } - } - - if(GlobalUtil::_verbose) - { - std::cout<<"#Features:\t"<<_featureNum<<"\n"; - } - -} - -void PyramidCL::ReshapeFeatureListCPU() -{ - int i, szmax =0, sz; - int n = param._dog_level_num*_octave_num; - for( i = 0; i < n; i++) - { - sz = _levelFeatureNum[i]; - if(sz > szmax ) szmax = sz; - } - float * buffer = new float[szmax*16]; - float * buffer1 = buffer; - float * buffer2 = buffer + szmax*4; - - - - _featureNum = 0; - -#ifdef NO_DUPLICATE_DOWNLOAD - const double twopi = 2.0*3.14159265358979323846; - _keypoint_buffer.resize(0); - float os = _octave_min>=0? float(1<<_octave_min): 1.0f/(1<<(-_octave_min)); - if(_down_sample_factor>0) os *= float(1<<_down_sample_factor); - float offset = GlobalUtil::_LoweOrigin? 0 : 0.5f; -#endif - - - for(i = 0; i < n; i++) - { - if(_levelFeatureNum[i]==0)continue; - - _featureTex[i].CopyToHost(buffer1); - - int fcount =0; - float * src = buffer1; - float * des = buffer2; - const static double factor = 2.0*3.14159265358979323846/65535.0; - for(int j = 0; j < _levelFeatureNum[i]; j++, src+=4) - { - unsigned short * orientations = (unsigned short*) (&src[3]); - if(orientations[0] != 65535) - { - des[0] = src[0]; - des[1] = src[1]; - des[2] = src[2]; - des[3] = float( factor* orientations[0]); - fcount++; - des += 4; - if(orientations[1] != 65535 && orientations[1] != orientations[0]) - { - des[0] = src[0]; - des[1] = src[1]; - des[2] = src[2]; - des[3] = float(factor* orientations[1]); - fcount++; - des += 4; - } - } - } - //texture size - SetLevelFeatureNum(i, fcount); - _featureTex[i].CopyFromHost(buffer2); - - if(fcount == 0) continue; - -#ifdef NO_DUPLICATE_DOWNLOAD - float oss = os * (1 << (i / param._dog_level_num)); - _keypoint_buffer.resize((_featureNum + fcount) * 4); - float* ds = &_keypoint_buffer[_featureNum * 4]; - float* fs = buffer2; - for(int k = 0; k < fcount; k++, ds+=4, fs+=4) - { - ds[0] = oss*(fs[0]-0.5f) + offset; //x - ds[1] = oss*(fs[1]-0.5f) + offset; //y - ds[2] = oss*fs[2]; //scale - ds[3] = (float)fmod(twopi-fs[3], twopi); //orientation, mirrored - } -#endif - _featureNum += fcount; - } - delete[] buffer; - if(GlobalUtil::_verbose) - { - std::cout<<"#Features MO:\t"<<_featureNum<<endl; - } -} - -void PyramidCL::GenerateFeatureDisplayVBO() -{ - //it is weried that this part is very slow. - //use a big VBO to save all the SIFT box vertices - /*int nvbo = _octave_num * param._dog_level_num; - if(_featureDisplayVBO==NULL) - { - //initialize the vbos - _featureDisplayVBO = new GLuint[nvbo]; - _featurePointVBO = new GLuint[nvbo]; - glGenBuffers(nvbo, _featureDisplayVBO); - glGenBuffers(nvbo, _featurePointVBO); - } - for(int i = 0; i < nvbo; i++) - { - if(_levelFeatureNum[i]<=0)continue; - CLTexImage * ftex = _featureTex + i; - CLTexImage texPBO1( _levelFeatureNum[i]* 10, 1, 4, _featureDisplayVBO[i]); - CLTexImage texPBO2(_levelFeatureNum[i], 1, 4, _featurePointVBO[i]); - _OpenCL->DisplayKeyBox(ftex, &texPBO1); - _OpenCL->DisplayKeyPoint(ftex, &texPBO2); - }*/ -} - -void PyramidCL::DestroySharedData() -{ - //histogram reduction - if(_histoPyramidTex) - { - delete[] _histoPyramidTex; - _hpLevelNum = 0; - _histoPyramidTex = NULL; - } - //descriptor storage shared by all levels - if(_descriptorTex) - { - delete _descriptorTex; - _descriptorTex = NULL; - } - //cpu reduction buffer. - if(_histo_buffer) - { - delete[] _histo_buffer; - _histo_buffer = 0; - } -} - -void PyramidCL::DestroyPerLevelData() -{ - //integers vector to store the feature numbers. - if(_levelFeatureNum) - { - delete [] _levelFeatureNum; - _levelFeatureNum = NULL; - } - //texture used to store features - if( _featureTex) - { - delete [] _featureTex; - _featureTex = NULL; - } - //texture used for multi-orientation - if(_orientationTex) - { - delete [] _orientationTex; - _orientationTex = NULL; - } - int no = _octave_num* param._dog_level_num; - - //two sets of vbos used to display the features - if(_featureDisplayVBO) - { - glDeleteBuffers(no, _featureDisplayVBO); - delete [] _featureDisplayVBO; - _featureDisplayVBO = NULL; - } - if( _featurePointVBO) - { - glDeleteBuffers(no, _featurePointVBO); - delete [] _featurePointVBO; - _featurePointVBO = NULL; - } -} - -void PyramidCL::DestroyPyramidData() -{ - if(_allPyramid) - { - delete [] _allPyramid; - _allPyramid = NULL; - } -} - -void PyramidCL::DownloadKeypoints() -{ - const double twopi = 2.0*3.14159265358979323846; - int idx = 0; - float * buffer = &_keypoint_buffer[0]; - vector<float> keypoint_buffer2; - //use a different keypoint buffer when processing with an exisint features list - //without orientation information. - if(_keypoint_index.size() > 0) - { - keypoint_buffer2.resize(_keypoint_buffer.size()); - buffer = &keypoint_buffer2[0]; - } - float * p = buffer, *ps; - CLTexImage * ftex = _featureTex; - ///////////////////// - float os = _octave_min>=0? float(1<<_octave_min): 1.0f/(1<<(-_octave_min)); - if(_down_sample_factor>0) os *= float(1<<_down_sample_factor); - float offset = GlobalUtil::_LoweOrigin? 0 : 0.5f; - ///////////////////// - for(int i = 0; i < _octave_num; i++, os *= 2.0f) - { - - for(int j = 0; j < param._dog_level_num; j++, idx++, ftex++) - { - - if(_levelFeatureNum[idx]>0) - { - ftex->CopyToHost(ps = p); - for(int k = 0; k < _levelFeatureNum[idx]; k++, ps+=4) - { - ps[0] = os*(ps[0]-0.5f) + offset; //x - ps[1] = os*(ps[1]-0.5f) + offset; //y - ps[2] = os*ps[2]; - ps[3] = (float)fmod(twopi-ps[3], twopi); //orientation, mirrored - } - p+= 4* _levelFeatureNum[idx]; - } - } - } - - //put the feature into their original order for existing keypoint - if(_keypoint_index.size() > 0) - { - for(int i = 0; i < _featureNum; ++i) - { - int index = _keypoint_index[i]; - memcpy(&_keypoint_buffer[index*4], &keypoint_buffer2[i*4], 4 * sizeof(float)); - } - } -} - -void PyramidCL::GenerateFeatureListCPU() -{ - //no cpu version provided - GenerateFeatureList(); -} - -void PyramidCL::GenerateFeatureList(int i, int j, int reduction_count, vector<int>& hbuffer) -{ - /*int fcount = 0, idx = i * param._dog_level_num + j; - int hist_level_num = _hpLevelNum - _pyramid_octave_first /2; - int ii, k, len; - - CLTexImage * htex, * ftex, * tex, *got; - ftex = _featureTex + idx; - htex = _histoPyramidTex + hist_level_num -1; - tex = GetBaseLevel(_octave_min + i, DATA_KEYPOINT) + 2 + j; - got = GetBaseLevel(_octave_min + i, DATA_GRAD) + 2 + j; - - _OpenCL->InitHistogram(tex, htex); - - for(k = 0; k < reduction_count - 1; k++, htex--) - { - ProgramCL::ReduceHistogram(htex, htex -1); - } - - //htex has the row reduction result - len = htex->GetImgHeight() * 4; - hbuffer.resize(len); - _OpenCL->FinishCL(); - htex->CopyToHost(&hbuffer[0]); - // - for(ii = 0; ii < len; ++ii) fcount += hbuffer[ii]; - SetLevelFeatureNum(idx, fcount); - - //build the feature list - if(fcount > 0) - { - _featureNum += fcount; - _keypoint_buffer.resize(fcount * 4); - //vector<int> ikbuf(fcount*4); - int* ibuf = (int*) (&_keypoint_buffer[0]); - - for(ii = 0; ii < len; ++ii) - { - int x = ii%4, y = ii / 4; - for(int jj = 0 ; jj < hbuffer[ii]; ++jj, ibuf+=4) - { - ibuf[0] = x; ibuf[1] = y; ibuf[2] = jj; ibuf[3] = 0; - } - } - _featureTex[idx].CopyFromHost(&_keypoint_buffer[0]); - - //////////////////////////////////////////// - ProgramCL::GenerateList(_featureTex + idx, ++htex); - for(k = 2; k < reduction_count; k++) - { - ProgramCL::GenerateList(_featureTex + idx, ++htex); - } - }*/ -} - -void PyramidCL::GenerateFeatureList() -{ - /*double t1, t2; - int ocount = 0, reduction_count; - int reverse = (GlobalUtil::_TruncateMethod == 1); - - vector<int> hbuffer; - _featureNum = 0; - - //for(int i = 0, idx = 0; i < _octave_num; i++) - FOR_EACH_OCTAVE(i, reverse) - { - CLTexImage* tex = GetBaseLevel(_octave_min + i, DATA_KEYPOINT) + 2; - reduction_count = FitHistogramPyramid(tex); - - if(GlobalUtil::_timingO) - { - t1 = CLOCK(); - ocount = 0; - std::cout<<"#"<<i+_octave_min + _down_sample_factor<<":\t"; - } - //for(int j = 0; j < param._dog_level_num; j++, idx++) - FOR_EACH_LEVEL(j, reverse) - { - if(GlobalUtil::_TruncateMethod && GlobalUtil::_FeatureCountThreshold > 0 && _featureNum > GlobalUtil::_FeatureCountThreshold) continue; - - GenerateFeatureList(i, j, reduction_count, hbuffer); - - ///////////////////////////// - if(GlobalUtil::_timingO) - { - int idx = i * param._dog_level_num + j; - ocount += _levelFeatureNum[idx]; - std::cout<< _levelFeatureNum[idx] <<"\t"; - } - } - if(GlobalUtil::_timingO) - { - t2 = CLOCK(); - std::cout << "| \t" << int(ocount) << " :\t(" << (t2 - t1) << ")\n"; - } - } - ///// - CopyGradientTex(); - ///// - if(GlobalUtil::_timingS)_OpenCL->FinishCL(); - - if(GlobalUtil::_verbose) - { - std::cout<<"#Features:\t"<<_featureNum<<"\n"; - }*/ -} - -GLTexImage* PyramidCL::GetLevelTexture(int octave, int level) -{ - return GetLevelTexture(octave, level, DATA_GAUSSIAN); -} - -GLTexImage* PyramidCL::ConvertTexCL2GL(CLTexImage* tex, int dataName) -{ - - if(_bufferTEX == NULL) _bufferTEX = new GLTexImage; - - /////////////////////////////////////////// - int ratio = GlobalUtil::_usePackedTex ? 2 : 1; - int width = tex->GetImgWidth() * ratio; - int height = tex->GetImgHeight() * ratio; - int tw = max(width, _bufferTEX->GetTexWidth()); - int th = max(height, _bufferTEX->GetTexHeight()); - _bufferTEX->InitTexture(tw, th, 1, GL_RGBA); - _bufferTEX->SetImageSize(width, height); - - ////////////////////////////////// - CLTexImage texCL(_OpenCL->GetContextCL(), _OpenCL->GetCommandQueue()); - texCL.InitTextureGL(*_bufferTEX, width, height, 4); - - switch(dataName) - { - case DATA_GAUSSIAN: _OpenCL->UnpackImage(tex, &texCL); break; - case DATA_DOG:_OpenCL->UnpackImageDOG(tex, &texCL); break; - case DATA_GRAD:_OpenCL->UnpackImageGRD(tex, &texCL); break; - case DATA_KEYPOINT:_OpenCL->UnpackImageKEY(tex, - tex - param._level_num * _pyramid_octave_num, &texCL);break; - default: - break; - } - - - return _bufferTEX; -} - -GLTexImage* PyramidCL::GetLevelTexture(int octave, int level, int dataName) -{ - CLTexImage* tex = GetBaseLevel(octave, dataName) + (level - param._level_min); - return ConvertTexCL2GL(tex, dataName); -} - -void PyramidCL::ConvertInputToCL(GLTexInput* input, CLTexImage* output) -{ - int ws = input->GetImgWidth(), hs = input->GetImgHeight(); - //copy the input image to pixel buffer object - if(input->_pixel_data) - { - output->InitTexture(ws, hs, 1); - output->CopyFromHost(input->_pixel_data); - }else /*if(input->_rgb_converted && input->CopyToPBO(_bufferPBO, ws, hs, GL_LUMINANCE)) - { - output->InitTexture(ws, hs, 1); - output->CopyFromPBO(ws, hs, _bufferPBO); - }else if(input->CopyToPBO(_bufferPBO, ws, hs)) - { - CLTexImage texPBO(ws, hs, 4, _bufferPBO); - output->InitTexture(ws, hs, 1); - ProgramCL::ReduceToSingleChannel(output, &texPBO, !input->_rgb_converted); - }else*/ - { - std::cerr<< "Unable To Convert Intput\n"; - } -} - -void PyramidCL::BuildPyramid(GLTexInput * input) -{ - - USE_TIMING(); - - int i, j; - - for ( i = _octave_min; i < _octave_min + _octave_num; i++) - { - - CLTexImage *tex = GetBaseLevel(i); - CLTexImage *buf = GetBaseLevel(i, DATA_DOG) +2; - FilterCL ** filter = _OpenCL->f_gaussian_step; - j = param._level_min + 1; - - OCTAVE_START(); - - if( i == _octave_min ) - { - if(GlobalUtil::_usePackedTex) - { - ConvertInputToCL(input, _inputTex); - if(i < 0) _OpenCL->SampleImageU(tex, _inputTex, -i- 1); - else _OpenCL->SampleImageD(tex, _inputTex, i + 1); - }else - { - if(i == 0) ConvertInputToCL(input, tex); - else - { - ConvertInputToCL(input, _inputTex); - if(i < 0) _OpenCL->SampleImageU(tex, _inputTex, -i); - else _OpenCL->SampleImageD(tex, _inputTex, i); - } - } - _OpenCL->FilterInitialImage(tex, buf); - }else - { - _OpenCL->SampleImageD(tex, GetBaseLevel(i - 1) + param._level_ds - param._level_min); - _OpenCL->FilterSampledImage(tex, buf); - } - LEVEL_FINISH(); - for( ; j <= param._level_max ; j++, tex++, filter++) - { - // filtering - _OpenCL->FilterImage(*filter, tex + 1, tex, buf); - LEVEL_FINISH(); - } - OCTAVE_FINISH(); - } - if(GlobalUtil::_timingS) _OpenCL->FinishCL(); -} - -void PyramidCL::DetectKeypointsEX() -{ - int i, j; - double t0, t, ts, t1, t2; - - if(GlobalUtil::_timingS && GlobalUtil::_verbose) ts = CLOCK(); - - for(i = _octave_min; i < _octave_min + _octave_num; i++) - { - CLTexImage * gus = GetBaseLevel(i) + 1; - CLTexImage * dog = GetBaseLevel(i, DATA_DOG) + 1; - CLTexImage * grd = GetBaseLevel(i, DATA_GRAD) + 1; - CLTexImage * rot = GetBaseLevel(i, DATA_ROT) + 1; - //compute the gradient - for(j = param._level_min +1; j <= param._level_max ; j++, gus++, dog++, grd++, rot++) - { - //input: gus and gus -1 - //output: gradient, dog, orientation - _OpenCL->ComputeDOG(gus, gus - 1, dog, grd, rot); - } - } - if(GlobalUtil::_timingS && GlobalUtil::_verbose) - { - _OpenCL->FinishCL(); - t1 = CLOCK(); - } - //if(GlobalUtil::_timingS) _OpenCL->FinishCL(); - //if(!GlobalUtil::_usePackedTex) return; //not finished - //return; - - for ( i = _octave_min; i < _octave_min + _octave_num; i++) - { - if(GlobalUtil::_timingO) - { - t0 = CLOCK(); - std::cout<<"#"<<(i + _down_sample_factor)<<"\t"; - } - CLTexImage * dog = GetBaseLevel(i, DATA_DOG) + 2; - CLTexImage * key = GetBaseLevel(i, DATA_KEYPOINT) +2; - - - for( j = param._level_min +2; j < param._level_max ; j++, dog++, key++) - { - if(GlobalUtil::_timingL)t = CLOCK(); - //input, dog, dog + 1, dog -1 - //output, key - _OpenCL->ComputeKEY(dog, key, param._dog_threshold, param._edge_threshold); - if(GlobalUtil::_timingL) - { - std::cout<<(CLOCK()-t)<<"\t"; - } - } - if(GlobalUtil::_timingO) - { - std::cout<<"|\t"<<(CLOCK()-t0)<<"\n"; - } - } - - if(GlobalUtil::_timingS) - { - _OpenCL->FinishCL(); - if(GlobalUtil::_verbose) - { - t2 = CLOCK(); - std::cout <<"<Gradient, DOG >\t"<<(t1-ts)<<"\n" - <<"<Get Keypoints >\t"<<(t2-t1)<<"\n"; - } - } -} - -void PyramidCL::CopyGradientTex() -{ - /*double ts, t1; - - if(GlobalUtil::_timingS && GlobalUtil::_verbose)ts = CLOCK(); - - for(int i = 0, idx = 0; i < _octave_num; i++) - { - CLTexImage * got = GetBaseLevel(i + _octave_min, DATA_GRAD) + 1; - //compute the gradient - for(int j = 0; j < param._dog_level_num ; j++, got++, idx++) - { - if(_levelFeatureNum[idx] > 0) got->CopyToTexture2D(); - } - } - if(GlobalUtil::_timingS) - { - ProgramCL::FinishCLDA(); - if(GlobalUtil::_verbose) - { - t1 = CLOCK(); - std::cout <<"<Copy Grad/Orientation>\t"<<(t1-ts)<<"\n"; - } - }*/ -} - -void PyramidCL::ComputeGradient() -{ - - /*int i, j; - double ts, t1; - - if(GlobalUtil::_timingS && GlobalUtil::_verbose)ts = CLOCK(); - - for(i = _octave_min; i < _octave_min + _octave_num; i++) - { - CLTexImage * gus = GetBaseLevel(i) + 1; - CLTexImage * dog = GetBaseLevel(i, DATA_DOG) + 1; - CLTexImage * got = GetBaseLevel(i, DATA_GRAD) + 1; - - //compute the gradient - for(j = 0; j < param._dog_level_num ; j++, gus++, dog++, got++) - { - ProgramCL::ComputeDOG(gus, dog, got); - } - } - if(GlobalUtil::_timingS) - { - ProgramCL::FinishCLDA(); - if(GlobalUtil::_verbose) - { - t1 = CLOCK(); - std::cout <<"<Gradient, DOG >\t"<<(t1-ts)<<"\n"; - } - }*/ -} - -int PyramidCL::FitHistogramPyramid(CLTexImage* tex) -{ - CLTexImage *htex; - int hist_level_num = _hpLevelNum - _pyramid_octave_first / 2; - htex = _histoPyramidTex + hist_level_num - 1; - int w = (tex->GetImgWidth() + 2) >> 2; - int h = tex->GetImgHeight(); - int count = 0; - for(int k = 0; k < hist_level_num; k++, htex--) - { - //htex->SetImageSize(w, h); - htex->InitTexture(w, h, 4); - ++count; - if(w == 1) - break; - w = (w + 3)>>2; - } - return count; -} - -void PyramidCL::GetFeatureOrientations() -{ - -/* - CLTexImage * ftex = _featureTex; - int * count = _levelFeatureNum; - float sigma, sigma_step = powf(2.0f, 1.0f/param._dog_level_num); - - for(int i = 0; i < _octave_num; i++) - { - CLTexImage* got = GetBaseLevel(i + _octave_min, DATA_GRAD) + 1; - CLTexImage* key = GetBaseLevel(i + _octave_min, DATA_KEYPOINT) + 2; - - for(int j = 0; j < param._dog_level_num; j++, ftex++, count++, got++, key++) - { - if(*count<=0)continue; - - //if(ftex->GetImgWidth() < *count) ftex->InitTexture(*count, 1, 4); - - sigma = param.GetLevelSigma(j+param._level_min+1); - - ProgramCL::ComputeOrientation(ftex, got, key, sigma, sigma_step, _existing_keypoints); - } - } - - if(GlobalUtil::_timingS)ProgramCL::FinishCL(); - */ - - -} - -void PyramidCL::GetSimplifiedOrientation() -{ - //no simplified orientation - GetFeatureOrientations(); -} - -CLTexImage* PyramidCL::GetBaseLevel(int octave, int dataName) -{ - if(octave <_octave_min || octave > _octave_min + _octave_num) return NULL; - int offset = (_pyramid_octave_first + octave - _octave_min) * param._level_num; - int num = param._level_num * _pyramid_octave_num; - return _allPyramid + num * dataName + offset; -} - -#endif - diff --git a/3rdparty/SiftGPU/src/SiftGPU/PyramidCL.h b/3rdparty/SiftGPU/src/SiftGPU/PyramidCL.h deleted file mode 100644 index 1ff6b181..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/PyramidCL.h +++ /dev/null @@ -1,83 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: PyramidCL.h -// Author: Changchang Wu -// Description : interface for the PyramdCL -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - - - -#ifndef _PYRAMID_CL_H -#define _PYRAMID_CL_H -#if defined(CL_SIFTGPU_ENABLED) - -class CLTexImage; -class SiftPyramid; -class ProgramBagCL; -class PyramidCL: public SiftPyramid -{ - CLTexImage* _inputTex; - CLTexImage* _allPyramid; - CLTexImage* _histoPyramidTex; - CLTexImage* _featureTex; - CLTexImage* _descriptorTex; - CLTexImage* _orientationTex; - ProgramBagCL* _OpenCL; - GLTexImage* _bufferTEX; -public: - virtual void GetFeatureDescriptors(); - virtual void GenerateFeatureListTex(); - virtual void ReshapeFeatureListCPU(); - virtual void GenerateFeatureDisplayVBO(); - virtual void DestroySharedData(); - virtual void DestroyPerLevelData(); - virtual void DestroyPyramidData(); - virtual void DownloadKeypoints(); - virtual void GenerateFeatureListCPU(); - virtual void GenerateFeatureList(); - virtual GLTexImage* GetLevelTexture(int octave, int level); - virtual GLTexImage* GetLevelTexture(int octave, int level, int dataName); - virtual void BuildPyramid(GLTexInput * input); - virtual void DetectKeypointsEX(); - virtual void ComputeGradient(); - virtual void GetFeatureOrientations(); - virtual void GetSimplifiedOrientation(); - virtual void InitPyramid(int w, int h, int ds = 0); - virtual void ResizePyramid(int w, int h); - - ////////// - void CopyGradientTex(); - void FitPyramid(int w, int h); - - void InitializeContext(); - int ResizeFeatureStorage(); - int FitHistogramPyramid(CLTexImage* tex); - void SetLevelFeatureNum(int idx, int fcount); - void ConvertInputToCL(GLTexInput* input, CLTexImage* output); - GLTexImage* ConvertTexCL2GL(CLTexImage* tex, int dataName); - CLTexImage* GetBaseLevel(int octave, int dataName = DATA_GAUSSIAN); -private: - void GenerateFeatureList(int i, int j, int reduction_count, vector<int>& hbuffer); -public: - PyramidCL(SiftParam& sp); - virtual ~PyramidCL(); -}; - - -#endif -#endif - diff --git a/3rdparty/SiftGPU/src/SiftGPU/PyramidCU.cpp b/3rdparty/SiftGPU/src/SiftGPU/PyramidCU.cpp deleted file mode 100644 index 368b6fe6..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/PyramidCU.cpp +++ /dev/null @@ -1,1190 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: PyramidCU.cpp -// Author: Changchang Wu -// Description : implementation of the PyramidCU class. -// CUDA-based implementation of SiftPyramid -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - -#if defined(CUDA_SIFTGPU_ENABLED) - - -#include "GL/glew.h" -#include <iostream> -#include <vector> -#include <algorithm> -#include <stdlib.h> -#include <string.h> -#include <math.h> -using namespace std; - -#include "GlobalUtil.h" -#include "GLTexImage.h" -#include "CuTexImage.h" -#include "SiftGPU.h" -#include "SiftPyramid.h" -#include "ProgramCU.h" -#include "PyramidCU.h" - - -//#include "imdebug/imdebuggl.h" -//#pragma comment (lib, "../lib/imdebug.lib") - - - -#define USE_TIMING() double t, t0, tt; -#define OCTAVE_START() if(GlobalUtil::_timingO){ t = t0 = CLOCK(); cout<<"#"<<i+_down_sample_factor<<"\t"; } -#define LEVEL_FINISH() if(GlobalUtil::_timingL){ ProgramCU::FinishCUDA(); tt = CLOCK();cout<<(tt-t)<<"\t"; t = CLOCK();} -#define OCTAVE_FINISH() if(GlobalUtil::_timingO)cout<<"|\t"<<(CLOCK()-t0)<<endl; - - -PyramidCU::PyramidCU(SiftParam& sp) : SiftPyramid(sp) -{ - _allPyramid = NULL; - _histoPyramidTex = NULL; - _featureTex = NULL; - _descriptorTex = NULL; - _orientationTex = NULL; - _bufferPBO = 0; - _bufferTEX = NULL; - _inputTex = new CuTexImage(); - - ///////////////////////// - InitializeContext(); -} - -PyramidCU::~PyramidCU() -{ - DestroyPerLevelData(); - DestroySharedData(); - DestroyPyramidData(); - if(_inputTex) delete _inputTex; - if(_bufferPBO) glDeleteBuffers(1, &_bufferPBO); - if(_bufferTEX) delete _bufferTEX; -} - -void PyramidCU::InitializeContext() -{ - GlobalUtil::InitGLParam(1); - GlobalUtil::_GoodOpenGL = max(GlobalUtil::_GoodOpenGL, 1); -} - -void PyramidCU::InitPyramid(int w, int h, int ds) -{ - int wp, hp, toobig = 0; - if(ds == 0) - { - // - TruncateWidth(w); - //// - _down_sample_factor = 0; - if(GlobalUtil::_octave_min_default>=0) - { - wp = w >> _octave_min_default; - hp = h >> _octave_min_default; - }else - { - //can't upsample by more than 8 - _octave_min_default = max(-3, _octave_min_default); - // - wp = w << (-_octave_min_default); - hp = h << (-_octave_min_default); - } - _octave_min = _octave_min_default; - }else - { - //must use 0 as _octave_min; - _octave_min = 0; - _down_sample_factor = ds; - w >>= ds; - h >>= ds; - ///// - - TruncateWidth(w); - - wp = w; - hp = h; - - } - - while(wp > GlobalUtil::_texMaxDim || hp > GlobalUtil::_texMaxDim ) - { - _octave_min ++; - wp >>= 1; - hp >>= 1; - toobig = 1; - } - - while(GlobalUtil::_MemCapGPU > 0 && GlobalUtil::_FitMemoryCap && (wp >_pyramid_width || hp > _pyramid_height)&& - max(max(wp, hp), max(_pyramid_width, _pyramid_height)) > 1024 * sqrt(GlobalUtil::_MemCapGPU / 110.0)) - { - _octave_min ++; - wp >>= 1; - hp >>= 1; - toobig = 2; - } - - - if(toobig && GlobalUtil::_verbose && _octave_min > 0) - { - std::cout<<(toobig == 2 ? "[**SKIP OCTAVES**]:\tExceeding Memory Cap (-nomc)\n" : - "[**SKIP OCTAVES**]:\tReaching the dimension limit(-maxd)!\n"); - } - //ResizePyramid(wp, hp); - if( wp == _pyramid_width && hp == _pyramid_height && _allocated ) - { - FitPyramid(wp, hp); - }else if(GlobalUtil::_ForceTightPyramid || _allocated ==0) - { - ResizePyramid(wp, hp); - } - else if( wp > _pyramid_width || hp > _pyramid_height ) - { - ResizePyramid(max(wp, _pyramid_width), max(hp, _pyramid_height)); - if(wp < _pyramid_width || hp < _pyramid_height) FitPyramid(wp, hp); - } - else - { - //try use the pyramid allocated for large image on small input images - FitPyramid(wp, hp); - } -} - -void PyramidCU::ResizePyramid(int w, int h) -{ - // - unsigned int totalkb = 0; - int _octave_num_new, input_sz, i, j; - // - - if(_pyramid_width == w && _pyramid_height == h && _allocated) return; - - if(w > GlobalUtil::_texMaxDim || h > GlobalUtil::_texMaxDim) return ; - - if(GlobalUtil::_verbose && GlobalUtil::_timingS) std::cout<<"[Allocate Pyramid]:\t" <<w<<"x"<<h<<endl; - //first octave does not change - _pyramid_octave_first = 0; - - - //compute # of octaves - - input_sz = min(w,h) ; - _pyramid_width = w; - _pyramid_height = h; - - - - //reset to preset parameters - - _octave_num_new = GlobalUtil::_octave_num_default; - - if(_octave_num_new < 1) - { - _octave_num_new = (int) floor (log ( double(input_sz))/log(2.0)) -3 ; - if(_octave_num_new<1 ) _octave_num_new = 1; - } - - if(_pyramid_octave_num != _octave_num_new) - { - //destroy the original pyramid if the # of octave changes - if(_octave_num >0) - { - DestroyPerLevelData(); - DestroyPyramidData(); - } - _pyramid_octave_num = _octave_num_new; - } - - _octave_num = _pyramid_octave_num; - - int noct = _octave_num; - int nlev = param._level_num; - - // //initialize the pyramid - if(_allPyramid==NULL) _allPyramid = new CuTexImage[ noct* nlev * DATA_NUM]; - - CuTexImage * gus = GetBaseLevel(_octave_min, DATA_GAUSSIAN); - CuTexImage * dog = GetBaseLevel(_octave_min, DATA_DOG); - CuTexImage * got = GetBaseLevel(_octave_min, DATA_GRAD); - CuTexImage * key = GetBaseLevel(_octave_min, DATA_KEYPOINT); - - ////////////there could be "out of memory" happening during the allocation - - for(i = 0; i< noct; i++) - { - int wa = ((w + 3) / 4) * 4; - - totalkb += ((nlev *8 -19)* (wa * h) * 4 / 1024); - for( j = 0; j< nlev; j++, gus++, dog++, got++, key++) - { - gus->InitTexture(wa, h); //nlev - if(j==0)continue; - dog->InitTexture(wa, h); //nlev -1 - if( j >= 1 && j < 1 + param._dog_level_num) - { - got->InitTexture(wa, h, 2); //2 * nlev - 6 - got->InitTexture2D(); - } - if(j > 1 && j < nlev -1) key->InitTexture(wa, h, 4); // nlev -3 ; 4 * nlev - 12 - } - w>>=1; - h>>=1; - } - - totalkb += ResizeFeatureStorage(); - - if(ProgramCU::CheckErrorCUDA("ResizePyramid")) SetFailStatus(); - - _allocated = 1; - - if(GlobalUtil::_verbose && GlobalUtil::_timingS) std::cout<<"[Allocate Pyramid]:\t" <<(totalkb/1024)<<"MB\n"; - -} - -void PyramidCU::FitPyramid(int w, int h) -{ - _pyramid_octave_first = 0; - // - _octave_num = GlobalUtil::_octave_num_default; - - int _octave_num_max = max(1, (int) floor (log ( double(min(w, h)))/log(2.0)) -3 ); - - if(_octave_num < 1 || _octave_num > _octave_num_max) - { - _octave_num = _octave_num_max; - } - - - int pw = _pyramid_width>>1, ph = _pyramid_height>>1; - while(_pyramid_octave_first + _octave_num < _pyramid_octave_num && - pw >= w && ph >= h) - { - _pyramid_octave_first++; - pw >>= 1; - ph >>= 1; - } - - ////////////////// - int nlev = param._level_num; - CuTexImage * gus = GetBaseLevel(_octave_min, DATA_GAUSSIAN); - CuTexImage * dog = GetBaseLevel(_octave_min, DATA_DOG); - CuTexImage * got = GetBaseLevel(_octave_min, DATA_GRAD); - CuTexImage * key = GetBaseLevel(_octave_min, DATA_KEYPOINT); - for(int i = 0; i< _octave_num; i++) - { - int wa = ((w + 3) / 4) * 4; - - for(int j = 0; j< nlev; j++, gus++, dog++, got++, key++) - { - gus->InitTexture(wa, h); //nlev - if(j==0)continue; - dog->InitTexture(wa, h); //nlev -1 - if( j >= 1 && j < 1 + param._dog_level_num) - { - got->InitTexture(wa, h, 2); //2 * nlev - 6 - got->InitTexture2D(); - } - if(j > 1 && j < nlev -1) key->InitTexture(wa, h, 4); // nlev -3 ; 4 * nlev - 12 - } - w>>=1; - h>>=1; - } -} - -int PyramidCU::CheckCudaDevice(int device) -{ - return ProgramCU::CheckCudaDevice(device); -} - -void PyramidCU::SetLevelFeatureNum(int idx, int fcount) -{ - _featureTex[idx].InitTexture(fcount, 1, 4); - _levelFeatureNum[idx] = fcount; -} - -int PyramidCU::ResizeFeatureStorage() -{ - int totalkb = 0; - if(_levelFeatureNum==NULL) _levelFeatureNum = new int[_octave_num * param._dog_level_num]; - std::fill(_levelFeatureNum, _levelFeatureNum+_octave_num * param._dog_level_num, 0); - - int wmax = GetBaseLevel(_octave_min)->GetImgWidth(); - int hmax = GetBaseLevel(_octave_min)->GetImgHeight(); - int whmax = max(wmax, hmax); - int w, i; - - // - int num = (int)ceil(log(double(whmax))/log(4.0)); - - if( _hpLevelNum != num) - { - _hpLevelNum = num; - if(_histoPyramidTex ) delete [] _histoPyramidTex; - _histoPyramidTex = new CuTexImage[_hpLevelNum]; - } - - for(i = 0, w = 1; i < _hpLevelNum; i++) - { - _histoPyramidTex[i].InitTexture(w, whmax, 4); - w<<=2; - } - - // (4 ^ (_hpLevelNum) -1 / 3) pixels - totalkb += (((1 << (2 * _hpLevelNum)) -1) / 3 * 16 / 1024); - - //initialize the feature texture - int idx = 0, n = _octave_num * param._dog_level_num; - if(_featureTex==NULL) _featureTex = new CuTexImage[n]; - if(GlobalUtil::_MaxOrientation >1 && GlobalUtil::_OrientationPack2==0 && _orientationTex== NULL) - _orientationTex = new CuTexImage[n]; - - - for(i = 0; i < _octave_num; i++) - { - CuTexImage * tex = GetBaseLevel(i+_octave_min); - int fmax = int(tex->GetImgWidth() * tex->GetImgHeight()*GlobalUtil::_MaxFeaturePercent); - // - if(fmax > GlobalUtil::_MaxLevelFeatureNum) fmax = GlobalUtil::_MaxLevelFeatureNum; - else if(fmax < 32) fmax = 32; //give it at least a space of 32 feature - - for(int j = 0; j < param._dog_level_num; j++, idx++) - { - _featureTex[idx].InitTexture(fmax, 1, 4); - totalkb += fmax * 16 /1024; - // - if(GlobalUtil::_MaxOrientation>1 && GlobalUtil::_OrientationPack2 == 0) - { - _orientationTex[idx].InitTexture(fmax, 1, 4); - totalkb += fmax * 16 /1024; - } - } - } - - - //this just need be initialized once - if(_descriptorTex==NULL) - { - //initialize feature texture pyramid - int fmax = _featureTex->GetImgWidth(); - _descriptorTex = new CuTexImage; - totalkb += ( fmax /2); - _descriptorTex->InitTexture(fmax *128, 1, 1); - }else - { - totalkb += _descriptorTex->GetDataSize()/1024; - } - return totalkb; -} - -void PyramidCU::GetFeatureDescriptors() -{ - //descriptors... - float* pd = &_descriptor_buffer[0]; - vector<float> descriptor_buffer2; - - //use another buffer if we need to re-order the descriptors - if(_keypoint_index.size() > 0) - { - descriptor_buffer2.resize(_descriptor_buffer.size()); - pd = &descriptor_buffer2[0]; - } - - CuTexImage * got, * ftex= _featureTex; - for(int i = 0, idx = 0; i < _octave_num; i++) - { - got = GetBaseLevel(i + _octave_min, DATA_GRAD) + 1; - for(int j = 0; j < param._dog_level_num; j++, ftex++, idx++, got++) - { - if(_levelFeatureNum[idx]==0) continue; - ProgramCU::ComputeDescriptor(ftex, got, _descriptorTex, IsUsingRectDescription());//process - _descriptorTex->CopyToHost(pd); //readback descriptor - pd += 128*_levelFeatureNum[idx]; - } - } - - if(GlobalUtil::_timingS) ProgramCU::FinishCUDA(); - - if(_keypoint_index.size() > 0) - { - //put the descriptor back to the original order for keypoint list. - for(int i = 0; i < _featureNum; ++i) - { - int index = _keypoint_index[i]; - memcpy(&_descriptor_buffer[index*128], &descriptor_buffer2[i*128], 128 * sizeof(float)); - } - } - - if(ProgramCU::CheckErrorCUDA("PyramidCU::GetFeatureDescriptors")) SetFailStatus(); -} - -void PyramidCU::GenerateFeatureListTex() -{ - - vector<float> list; - int idx = 0; - const double twopi = 2.0*3.14159265358979323846; - float sigma_half_step = powf(2.0f, 0.5f / param._dog_level_num); - float octave_sigma = _octave_min>=0? float(1<<_octave_min): 1.0f/(1<<(-_octave_min)); - float offset = GlobalUtil::_LoweOrigin? 0 : 0.5f; - if(_down_sample_factor>0) octave_sigma *= float(1<<_down_sample_factor); - - _keypoint_index.resize(0); // should already be 0 - for(int i = 0; i < _octave_num; i++, octave_sigma*= 2.0f) - { - for(int j = 0; j < param._dog_level_num; j++, idx++) - { - list.resize(0); - float level_sigma = param.GetLevelSigma(j + param._level_min + 1) * octave_sigma; - float sigma_min = level_sigma / sigma_half_step; - float sigma_max = level_sigma * sigma_half_step; - int fcount = 0 ; - for(int k = 0; k < _featureNum; k++) - { - float * key = &_keypoint_buffer[k*4]; - float sigmak = key[2]; - ////////////////////////////////////// - if(IsUsingRectDescription()) sigmak = min(key[2], key[3]) / 12.0f; - - if( (sigmak >= sigma_min && sigmak < sigma_max) - ||(sigmak < sigma_min && i ==0 && j == 0) - ||(sigmak > sigma_max && i == _octave_num -1 && j == param._dog_level_num - 1)) - { - //add this keypoint to the list - list.push_back((key[0] - offset) / octave_sigma + 0.5f); - list.push_back((key[1] - offset) / octave_sigma + 0.5f); - if(IsUsingRectDescription()) - { - list.push_back(key[2] / octave_sigma); - list.push_back(key[3] / octave_sigma); - }else - { - list.push_back(key[2] / octave_sigma); - list.push_back((float)fmod(twopi-key[3], twopi)); - } - fcount ++; - //save the index of keypoints - _keypoint_index.push_back(k); - } - - } - - _levelFeatureNum[idx] = fcount; - if(fcount==0)continue; - CuTexImage * ftex = _featureTex+idx; - - SetLevelFeatureNum(idx, fcount); - ftex->CopyFromHost(&list[0]); - } - } - - if(GlobalUtil::_verbose) - { - std::cout<<"#Features:\t"<<_featureNum<<"\n"; - } - -} - -void PyramidCU::ReshapeFeatureListCPU() -{ - int i, szmax =0, sz; - int n = param._dog_level_num*_octave_num; - for( i = 0; i < n; i++) - { - sz = _levelFeatureNum[i]; - if(sz > szmax ) szmax = sz; - } - float * buffer = new float[szmax*16]; - float * buffer1 = buffer; - float * buffer2 = buffer + szmax*4; - - - - _featureNum = 0; - -#ifdef NO_DUPLICATE_DOWNLOAD - const double twopi = 2.0*3.14159265358979323846; - _keypoint_buffer.resize(0); - float os = _octave_min>=0? float(1<<_octave_min): 1.0f/(1<<(-_octave_min)); - if(_down_sample_factor>0) os *= float(1<<_down_sample_factor); - float offset = GlobalUtil::_LoweOrigin? 0 : 0.5f; -#endif - - - for(i = 0; i < n; i++) - { - if(_levelFeatureNum[i]==0)continue; - - _featureTex[i].CopyToHost(buffer1); - - int fcount =0; - float * src = buffer1; - float * des = buffer2; - const static double factor = 2.0*3.14159265358979323846/65535.0; - for(int j = 0; j < _levelFeatureNum[i]; j++, src+=4) - { - unsigned short * orientations = (unsigned short*) (&src[3]); - if(orientations[0] != 65535) - { - des[0] = src[0]; - des[1] = src[1]; - des[2] = src[2]; - des[3] = float( factor* orientations[0]); - fcount++; - des += 4; - if(orientations[1] != 65535 && orientations[1] != orientations[0]) - { - des[0] = src[0]; - des[1] = src[1]; - des[2] = src[2]; - des[3] = float(factor* orientations[1]); - fcount++; - des += 4; - } - } - } - //texture size - SetLevelFeatureNum(i, fcount); - _featureTex[i].CopyFromHost(buffer2); - - if(fcount == 0) continue; - -#ifdef NO_DUPLICATE_DOWNLOAD - float oss = os * (1 << (i / param._dog_level_num)); - _keypoint_buffer.resize((_featureNum + fcount) * 4); - float* ds = &_keypoint_buffer[_featureNum * 4]; - float* fs = buffer2; - for(int k = 0; k < fcount; k++, ds+=4, fs+=4) - { - ds[0] = oss*(fs[0]-0.5f) + offset; //x - ds[1] = oss*(fs[1]-0.5f) + offset; //y - ds[2] = oss*fs[2]; //scale - ds[3] = (float)fmod(twopi-fs[3], twopi); //orientation, mirrored - } -#endif - _featureNum += fcount; - } - delete[] buffer; - if(GlobalUtil::_verbose) - { - std::cout<<"#Features MO:\t"<<_featureNum<<endl; - } -} - -void PyramidCU::GenerateFeatureDisplayVBO() -{ - //it is weried that this part is very slow. - //use a big VBO to save all the SIFT box vertices - int nvbo = _octave_num * param._dog_level_num; - if(_featureDisplayVBO==NULL) - { - //initialize the vbos - _featureDisplayVBO = new GLuint[nvbo]; - _featurePointVBO = new GLuint[nvbo]; - glGenBuffers(nvbo, _featureDisplayVBO); - glGenBuffers(nvbo, _featurePointVBO); - } - for(int i = 0; i < nvbo; i++) - { - if(_levelFeatureNum[i]<=0)continue; - CuTexImage * ftex = _featureTex + i; - CuTexImage texPBO1( _levelFeatureNum[i]* 10, 1, 4, _featureDisplayVBO[i]); - CuTexImage texPBO2(_levelFeatureNum[i], 1, 4, _featurePointVBO[i]); - ProgramCU::DisplayKeyBox(ftex, &texPBO1); - ProgramCU::DisplayKeyPoint(ftex, &texPBO2); - } -} - -void PyramidCU::DestroySharedData() -{ - //histogram reduction - if(_histoPyramidTex) - { - delete[] _histoPyramidTex; - _hpLevelNum = 0; - _histoPyramidTex = NULL; - } - //descriptor storage shared by all levels - if(_descriptorTex) - { - delete _descriptorTex; - _descriptorTex = NULL; - } - //cpu reduction buffer. - if(_histo_buffer) - { - delete[] _histo_buffer; - _histo_buffer = 0; - } -} - -void PyramidCU::DestroyPerLevelData() -{ - //integers vector to store the feature numbers. - if(_levelFeatureNum) - { - delete [] _levelFeatureNum; - _levelFeatureNum = NULL; - } - //texture used to store features - if( _featureTex) - { - delete [] _featureTex; - _featureTex = NULL; - } - //texture used for multi-orientation - if(_orientationTex) - { - delete [] _orientationTex; - _orientationTex = NULL; - } - int no = _octave_num* param._dog_level_num; - - //two sets of vbos used to display the features - if(_featureDisplayVBO) - { - glDeleteBuffers(no, _featureDisplayVBO); - delete [] _featureDisplayVBO; - _featureDisplayVBO = NULL; - } - if( _featurePointVBO) - { - glDeleteBuffers(no, _featurePointVBO); - delete [] _featurePointVBO; - _featurePointVBO = NULL; - } -} - -void PyramidCU::DestroyPyramidData() -{ - if(_allPyramid) - { - delete [] _allPyramid; - _allPyramid = NULL; - } -} - -void PyramidCU::DownloadKeypoints() -{ - const double twopi = 2.0*3.14159265358979323846; - int idx = 0; - float * buffer = &_keypoint_buffer[0]; - vector<float> keypoint_buffer2; - //use a different keypoint buffer when processing with an exisint features list - //without orientation information. - if(_keypoint_index.size() > 0) - { - keypoint_buffer2.resize(_keypoint_buffer.size()); - buffer = &keypoint_buffer2[0]; - } - float * p = buffer, *ps; - CuTexImage * ftex = _featureTex; - ///////////////////// - float os = _octave_min>=0? float(1<<_octave_min): 1.0f/(1<<(-_octave_min)); - if(_down_sample_factor>0) os *= float(1<<_down_sample_factor); - float offset = GlobalUtil::_LoweOrigin? 0 : 0.5f; - ///////////////////// - for(int i = 0; i < _octave_num; i++, os *= 2.0f) - { - - for(int j = 0; j < param._dog_level_num; j++, idx++, ftex++) - { - - if(_levelFeatureNum[idx]>0) - { - ftex->CopyToHost(ps = p); - for(int k = 0; k < _levelFeatureNum[idx]; k++, ps+=4) - { - ps[0] = os*(ps[0]-0.5f) + offset; //x - ps[1] = os*(ps[1]-0.5f) + offset; //y - ps[2] = os*ps[2]; - ps[3] = (float)fmod(twopi-ps[3], twopi); //orientation, mirrored - } - p+= 4* _levelFeatureNum[idx]; - } - } - } - - //put the feature into their original order for existing keypoint - if(_keypoint_index.size() > 0) - { - for(int i = 0; i < _featureNum; ++i) - { - int index = _keypoint_index[i]; - memcpy(&_keypoint_buffer[index*4], &keypoint_buffer2[i*4], 4 * sizeof(float)); - } - } -} - -void PyramidCU::GenerateFeatureListCPU() -{ - //no cpu version provided - GenerateFeatureList(); -} - -void PyramidCU::GenerateFeatureList(int i, int j, int reduction_count, vector<int>& hbuffer) -{ - int fcount = 0, idx = i * param._dog_level_num + j; - int hist_level_num = _hpLevelNum - _pyramid_octave_first /2; - int ii, k, len; - - CuTexImage * htex, * ftex, * tex, *got; - ftex = _featureTex + idx; - htex = _histoPyramidTex + hist_level_num -1; - tex = GetBaseLevel(_octave_min + i, DATA_KEYPOINT) + 2 + j; - got = GetBaseLevel(_octave_min + i, DATA_GRAD) + 2 + j; - - ProgramCU::InitHistogram(tex, htex); - - for(k = 0; k < reduction_count - 1; k++, htex--) - { - ProgramCU::ReduceHistogram(htex, htex -1); - } - - //htex has the row reduction result - len = htex->GetImgHeight() * 4; - hbuffer.resize(len); - ProgramCU::FinishCUDA(); - htex->CopyToHost(&hbuffer[0]); - - ////TO DO: track the error found here.. - for(ii = 0; ii < len; ++ii) {if(!(hbuffer[ii]>= 0)) hbuffer[ii] = 0; }//? - - - for(ii = 0; ii < len; ++ii) fcount += hbuffer[ii]; - SetLevelFeatureNum(idx, fcount); - - //build the feature list - if(fcount > 0) - { - _featureNum += fcount; - _keypoint_buffer.resize(fcount * 4); - //vector<int> ikbuf(fcount*4); - int* ibuf = (int*) (&_keypoint_buffer[0]); - - for(ii = 0; ii < len; ++ii) - { - int x = ii%4, y = ii / 4; - for(int jj = 0 ; jj < hbuffer[ii]; ++jj, ibuf+=4) - { - ibuf[0] = x; ibuf[1] = y; ibuf[2] = jj; ibuf[3] = 0; - } - } - _featureTex[idx].CopyFromHost(&_keypoint_buffer[0]); - - //////////////////////////////////////////// - ProgramCU::GenerateList(_featureTex + idx, ++htex); - for(k = 2; k < reduction_count; k++) - { - ProgramCU::GenerateList(_featureTex + idx, ++htex); - } - } -} - -void PyramidCU::GenerateFeatureList() -{ - double t1, t2; - int ocount = 0, reduction_count; - int reverse = (GlobalUtil::_TruncateMethod == 1); - - vector<int> hbuffer; - _featureNum = 0; - - //for(int i = 0, idx = 0; i < _octave_num; i++) - FOR_EACH_OCTAVE(i, reverse) - { - CuTexImage* tex = GetBaseLevel(_octave_min + i, DATA_KEYPOINT) + 2; - reduction_count = FitHistogramPyramid(tex); - - if(GlobalUtil::_timingO) - { - t1 = CLOCK(); - ocount = 0; - std::cout<<"#"<<i+_octave_min + _down_sample_factor<<":\t"; - } - //for(int j = 0; j < param._dog_level_num; j++, idx++) - FOR_EACH_LEVEL(j, reverse) - { - if(GlobalUtil::_TruncateMethod && GlobalUtil::_FeatureCountThreshold > 0 && _featureNum > GlobalUtil::_FeatureCountThreshold) continue; - - GenerateFeatureList(i, j, reduction_count, hbuffer); - - ///////////////////////////// - if(GlobalUtil::_timingO) - { - int idx = i * param._dog_level_num + j; - ocount += _levelFeatureNum[idx]; - std::cout<< _levelFeatureNum[idx] <<"\t"; - } - } - if(GlobalUtil::_timingO) - { - t2 = CLOCK(); - std::cout << "| \t" << int(ocount) << " :\t(" << (t2 - t1) << ")\n"; - } - } - ///// - CopyGradientTex(); - ///// - if(GlobalUtil::_timingS)ProgramCU::FinishCUDA(); - - if(GlobalUtil::_verbose) - { - std::cout<<"#Features:\t"<<_featureNum<<"\n"; - } - - if(ProgramCU::CheckErrorCUDA("PyramidCU::GenerateFeatureList")) SetFailStatus(); -} - -GLTexImage* PyramidCU::GetLevelTexture(int octave, int level) -{ - return GetLevelTexture(octave, level, DATA_GAUSSIAN); -} - -GLTexImage* PyramidCU::ConvertTexCU2GL(CuTexImage* tex, int dataName) -{ - - GLenum format = GL_LUMINANCE; - int convert_done = 1; - if(_bufferPBO == 0) glGenBuffers(1, &_bufferPBO); - if(_bufferTEX == NULL) _bufferTEX = new GLTexImage; - switch(dataName) - { - case DATA_GAUSSIAN: - { - convert_done = tex->CopyToPBO(_bufferPBO); - break; - } - case DATA_DOG: - { - CuTexImage texPBO(tex->GetImgWidth(), tex->GetImgHeight(), 1, _bufferPBO); - if(texPBO._cuData == 0 || tex->_cuData == NULL) convert_done = 0; - else ProgramCU::DisplayConvertDOG(tex, &texPBO); - break; - } - case DATA_GRAD: - { - CuTexImage texPBO(tex->GetImgWidth(), tex->GetImgHeight(), 1, _bufferPBO); - if(texPBO._cuData == 0 || tex->_cuData == NULL) convert_done = 0; - else ProgramCU::DisplayConvertGRD(tex, &texPBO); - break; - } - case DATA_KEYPOINT: - { - CuTexImage * dog = tex - param._level_num * _pyramid_octave_num; - format = GL_RGBA; - CuTexImage texPBO(tex->GetImgWidth(), tex->GetImgHeight(), 4, _bufferPBO); - if(texPBO._cuData == 0 || tex->_cuData == NULL) convert_done = 0; - else ProgramCU::DisplayConvertKEY(tex, dog, &texPBO); - break; - } - default: - convert_done = 0; - break; - } - - if(convert_done) - { - _bufferTEX->InitTexture(max(_bufferTEX->GetTexWidth(), tex->GetImgWidth()), max(_bufferTEX->GetTexHeight(), tex->GetImgHeight())); - _bufferTEX->CopyFromPBO(_bufferPBO, tex->GetImgWidth(), tex->GetImgHeight(), format); - }else - { - _bufferTEX->SetImageSize(0, 0); - } - - return _bufferTEX; -} - -GLTexImage* PyramidCU::GetLevelTexture(int octave, int level, int dataName) -{ - CuTexImage* tex = GetBaseLevel(octave, dataName) + (level - param._level_min); - //CuTexImage* gus = GetBaseLevel(octave, DATA_GAUSSIAN) + (level - param._level_min); - return ConvertTexCU2GL(tex, dataName); -} - -void PyramidCU::ConvertInputToCU(GLTexInput* input) -{ - int ws = input->GetImgWidth(), hs = input->GetImgHeight(); - TruncateWidth(ws); - //copy the input image to pixel buffer object - if(input->_pixel_data) - { - _inputTex->InitTexture(ws, hs, 1); - _inputTex->CopyFromHost(input->_pixel_data); - }else - { - if(_bufferPBO == 0) glGenBuffers(1, &_bufferPBO); - if(input->_rgb_converted && input->CopyToPBO(_bufferPBO, ws, hs, GL_LUMINANCE)) - { - _inputTex->InitTexture(ws, hs, 1); - _inputTex->CopyFromPBO(ws, hs, _bufferPBO); - }else if(input->CopyToPBO(_bufferPBO, ws, hs)) - { - CuTexImage texPBO(ws, hs, 4, _bufferPBO); - _inputTex->InitTexture(ws, hs, 1); - ProgramCU::ReduceToSingleChannel(_inputTex, &texPBO, !input->_rgb_converted); - }else - { - std::cerr<< "Unable To Convert Intput\n"; - } - } -} - -void PyramidCU::BuildPyramid(GLTexInput * input) -{ - - USE_TIMING(); - - int i, j; - - for ( i = _octave_min; i < _octave_min + _octave_num; i++) - { - - float* filter_sigma = param._sigma; - CuTexImage *tex = GetBaseLevel(i); - CuTexImage *buf = GetBaseLevel(i, DATA_KEYPOINT) +2; - j = param._level_min + 1; - - OCTAVE_START(); - - if( i == _octave_min ) - { - ConvertInputToCU(input); - - if(i == 0) - { - ProgramCU::FilterImage(tex, _inputTex, buf, - param.GetInitialSmoothSigma(_octave_min + _down_sample_factor)); - }else - { - if(i < 0) ProgramCU::SampleImageU(tex, _inputTex, -i); - else ProgramCU::SampleImageD(tex, _inputTex, i); - ProgramCU::FilterImage(tex, tex, buf, - param.GetInitialSmoothSigma(_octave_min + _down_sample_factor)); - } - }else - { - ProgramCU::SampleImageD(tex, GetBaseLevel(i - 1) + param._level_ds - param._level_min); - if(param._sigma_skip1 > 0) - { - ProgramCU::FilterImage(tex, tex, buf, param._sigma_skip1); - } - } - LEVEL_FINISH(); - for( ; j <= param._level_max ; j++, tex++, filter_sigma++) - { - // filtering - ProgramCU::FilterImage(tex + 1, tex, buf, *filter_sigma); - LEVEL_FINISH(); - } - OCTAVE_FINISH(); - } - if(GlobalUtil::_timingS) ProgramCU::FinishCUDA(); - - if(ProgramCU::CheckErrorCUDA("PyramidCU::BuildPyramid")) SetFailStatus(); -} - -void PyramidCU::DetectKeypointsEX() -{ - - - int i, j; - double t0, t, ts, t1, t2; - - if(GlobalUtil::_timingS && GlobalUtil::_verbose)ts = CLOCK(); - - for(i = _octave_min; i < _octave_min + _octave_num; i++) - { - CuTexImage * gus = GetBaseLevel(i) + 1; - CuTexImage * dog = GetBaseLevel(i, DATA_DOG) + 1; - CuTexImage * got = GetBaseLevel(i, DATA_GRAD) + 1; - //compute the gradient - for(j = param._level_min +1; j <= param._level_max ; j++, gus++, dog++, got++) - { - //input: gus and gus -1 - //output: gradient, dog, orientation - ProgramCU::ComputeDOG(gus, dog, got); - } - } - if(GlobalUtil::_timingS && GlobalUtil::_verbose) - { - ProgramCU::FinishCUDA(); - t1 = CLOCK(); - } - - for ( i = _octave_min; i < _octave_min + _octave_num; i++) - { - if(GlobalUtil::_timingO) - { - t0 = CLOCK(); - std::cout<<"#"<<(i + _down_sample_factor)<<"\t"; - } - CuTexImage * dog = GetBaseLevel(i, DATA_DOG) + 2; - CuTexImage * key = GetBaseLevel(i, DATA_KEYPOINT) +2; - - - for( j = param._level_min +2; j < param._level_max ; j++, dog++, key++) - { - if(GlobalUtil::_timingL)t = CLOCK(); - //input, dog, dog + 1, dog -1 - //output, key - ProgramCU::ComputeKEY(dog, key, param._dog_threshold, param._edge_threshold); - if(GlobalUtil::_timingL) - { - std::cout<<(CLOCK()-t)<<"\t"; - } - } - if(GlobalUtil::_timingO) - { - std::cout<<"|\t"<<(CLOCK()-t0)<<"\n"; - } - } - - if(GlobalUtil::_timingS) - { - ProgramCU::FinishCUDA(); - if(GlobalUtil::_verbose) - { - t2 = CLOCK(); - std::cout <<"<Gradient, DOG >\t"<<(t1-ts)<<"\n" - <<"<Get Keypoints >\t"<<(t2-t1)<<"\n"; - } - } -} - -void PyramidCU::CopyGradientTex() -{ - double ts, t1; - - if(GlobalUtil::_timingS && GlobalUtil::_verbose)ts = CLOCK(); - - for(int i = 0, idx = 0; i < _octave_num; i++) - { - CuTexImage * got = GetBaseLevel(i + _octave_min, DATA_GRAD) + 1; - //compute the gradient - for(int j = 0; j < param._dog_level_num ; j++, got++, idx++) - { - if(_levelFeatureNum[idx] > 0) got->CopyToTexture2D(); - } - } - if(GlobalUtil::_timingS) - { - ProgramCU::FinishCUDA(); - if(GlobalUtil::_verbose) - { - t1 = CLOCK(); - std::cout <<"<Copy Grad/Orientation>\t"<<(t1-ts)<<"\n"; - } - } -} - -void PyramidCU::ComputeGradient() -{ - - int i, j; - double ts, t1; - - if(GlobalUtil::_timingS && GlobalUtil::_verbose)ts = CLOCK(); - - for(i = _octave_min; i < _octave_min + _octave_num; i++) - { - CuTexImage * gus = GetBaseLevel(i) + 1; - CuTexImage * dog = GetBaseLevel(i, DATA_DOG) + 1; - CuTexImage * got = GetBaseLevel(i, DATA_GRAD) + 1; - - //compute the gradient - for(j = 0; j < param._dog_level_num ; j++, gus++, dog++, got++) - { - ProgramCU::ComputeDOG(gus, dog, got); - } - } - if(GlobalUtil::_timingS) - { - ProgramCU::FinishCUDA(); - if(GlobalUtil::_verbose) - { - t1 = CLOCK(); - std::cout <<"<Gradient, DOG >\t"<<(t1-ts)<<"\n"; - } - } -} - -int PyramidCU::FitHistogramPyramid(CuTexImage* tex) -{ - CuTexImage *htex; - int hist_level_num = _hpLevelNum - _pyramid_octave_first / 2; - htex = _histoPyramidTex + hist_level_num - 1; - int w = (tex->GetImgWidth() + 2) >> 2; - int h = tex->GetImgHeight(); - int count = 0; - for(int k = 0; k < hist_level_num; k++, htex--) - { - //htex->SetImageSize(w, h); - htex->InitTexture(w, h, 4); - ++count; - if(w == 1) - break; - w = (w + 3)>>2; - } - return count; -} - -void PyramidCU::GetFeatureOrientations() -{ - - CuTexImage * ftex = _featureTex; - int * count = _levelFeatureNum; - float sigma, sigma_step = powf(2.0f, 1.0f/param._dog_level_num); - - for(int i = 0; i < _octave_num; i++) - { - CuTexImage* got = GetBaseLevel(i + _octave_min, DATA_GRAD) + 1; - CuTexImage* key = GetBaseLevel(i + _octave_min, DATA_KEYPOINT) + 2; - - for(int j = 0; j < param._dog_level_num; j++, ftex++, count++, got++, key++) - { - if(*count<=0)continue; - - //if(ftex->GetImgWidth() < *count) ftex->InitTexture(*count, 1, 4); - - sigma = param.GetLevelSigma(j+param._level_min+1); - - ProgramCU::ComputeOrientation(ftex, got, key, sigma, sigma_step, _existing_keypoints); - } - } - - if(GlobalUtil::_timingS)ProgramCU::FinishCUDA(); - if(ProgramCU::CheckErrorCUDA("PyramidCU::GetFeatureOrientations")) SetFailStatus(); - -} - -void PyramidCU::GetSimplifiedOrientation() -{ - //no simplified orientation - GetFeatureOrientations(); -} - -CuTexImage* PyramidCU::GetBaseLevel(int octave, int dataName) -{ - if(octave <_octave_min || octave > _octave_min + _octave_num) return NULL; - int offset = (_pyramid_octave_first + octave - _octave_min) * param._level_num; - int num = param._level_num * _pyramid_octave_num; - if (dataName == DATA_ROT) dataName = DATA_GRAD; - return _allPyramid + num * dataName + offset; -} - -#endif - diff --git a/3rdparty/SiftGPU/src/SiftGPU/PyramidCU.h b/3rdparty/SiftGPU/src/SiftGPU/PyramidCU.h deleted file mode 100644 index feb7adeb..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/PyramidCU.h +++ /dev/null @@ -1,86 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: PyramidCU.h -// Author: Changchang Wu -// Description : interface for the PyramdCU -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - - - -#ifndef _PYRAMID_CU_H -#define _PYRAMID_CU_H -#if defined(CUDA_SIFTGPU_ENABLED) - -class GLTexImage; -class CuTexImage; -class SiftPyramid; -class PyramidCU:public SiftPyramid -{ - CuTexImage* _inputTex; - CuTexImage* _allPyramid; - CuTexImage* _histoPyramidTex; - CuTexImage* _featureTex; - CuTexImage* _descriptorTex; - CuTexImage* _orientationTex; - GLuint _bufferPBO; - GLTexImage* _bufferTEX; -public: - virtual void GetFeatureDescriptors(); - virtual void GenerateFeatureListTex(); - virtual void ReshapeFeatureListCPU(); - virtual void GenerateFeatureDisplayVBO(); - virtual void DestroySharedData(); - virtual void DestroyPerLevelData(); - virtual void DestroyPyramidData(); - virtual void DownloadKeypoints(); - virtual void GenerateFeatureListCPU(); - virtual void GenerateFeatureList(); - virtual GLTexImage* GetLevelTexture(int octave, int level); - virtual GLTexImage* GetLevelTexture(int octave, int level, int dataName); - virtual void BuildPyramid(GLTexInput * input); - virtual void DetectKeypointsEX(); - virtual void ComputeGradient(); - virtual void GetFeatureOrientations(); - virtual void GetSimplifiedOrientation(); - virtual void InitPyramid(int w, int h, int ds = 0); - virtual void ResizePyramid(int w, int h); - virtual int IsUsingRectDescription(){return _existing_keypoints & SIFT_RECT_DESCRIPTION; } - ////////// - void CopyGradientTex(); - void FitPyramid(int w, int h); - - void InitializeContext(); - int ResizeFeatureStorage(); - int FitHistogramPyramid(CuTexImage* tex); - void SetLevelFeatureNum(int idx, int fcount); - void ConvertInputToCU(GLTexInput* input); - GLTexImage* ConvertTexCU2GL(CuTexImage* tex, int dataName); - CuTexImage* GetBaseLevel(int octave, int dataName = DATA_GAUSSIAN); - void TruncateWidth(int& w) { w = GLTexInput::TruncateWidthCU(w); } - ////////////////////////// - static int CheckCudaDevice(int device); -private: - void GenerateFeatureList(int i, int j, int reduction_count, vector<int>& hbuffer); -public: - PyramidCU(SiftParam& sp); - virtual ~PyramidCU(); -}; - - - -#endif -#endif diff --git a/3rdparty/SiftGPU/src/SiftGPU/PyramidGL.cpp b/3rdparty/SiftGPU/src/SiftGPU/PyramidGL.cpp deleted file mode 100644 index 18c37d6a..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/PyramidGL.cpp +++ /dev/null @@ -1,2805 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: PyramidGL.cpp -// Author: Changchang Wu -// Description : implementation of PyramidGL/PyramidNaive/PyramidPackdc . -// -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - -#include "GL/glew.h" -#include <iostream> -#include <iomanip> -#include <vector> -#include <algorithm> -#include <fstream> -#include <math.h> -#include <string.h> -using namespace std; - -#include "GlobalUtil.h" -#include "GLTexImage.h" -#include "SiftGPU.h" -#include "ShaderMan.h" -#include "SiftPyramid.h" -#include "ProgramGLSL.h" -#include "PyramidGL.h" -#include "FrameBufferObject.h" - - -#if defined(__SSE__) || _MSC_VER > 1200 -#define USE_SSE_FOR_SIFTGPU -#include <xmmintrin.h> -#else -//#pragma message( "SSE optimization off!\n" ) -#endif - - -#define USE_TIMING() double t, t0, tt; -#define OCTAVE_START() if(GlobalUtil::_timingO){ t = t0 = CLOCK(); cout<<"#"<<i+_down_sample_factor<<"\t"; } -#define LEVEL_FINISH() if(GlobalUtil::_timingL){ glFinish(); tt = CLOCK();cout<<(tt-t)<<"\t"; t = CLOCK();} -#define OCTAVE_FINISH() if(GlobalUtil::_timingO)cout<<"|\t"<<(CLOCK()-t0)<<endl; - - -////////////////////////////////////////////////////////////////////// -// Construction/Destruction -////////////////////////////////////////////////////////////////////// -PyramidNaive::PyramidNaive(SiftParam& sp): PyramidGL(sp) -{ - _texPyramid = NULL; - _auxPyramid = NULL; -} - -PyramidNaive::~PyramidNaive() -{ - DestroyPyramidData(); -} - -//align must be 2^i -void PyramidGL:: GetAlignedStorageSize(int num, int align, int &fw, int &fh) -{ - if(num <=0) - { - fw = fh = 0; - }else if(num < align*align) - { - fw = align; - fh = (int)ceil(double(num) / fw); - }else if(GlobalUtil::_NarrowFeatureTex) - { - double dn = double(num); - int nb = (int) ceil(dn/GlobalUtil::_texMaxDim/align); - fw = align * nb; - fh = (int)ceil(dn /fw);/**/ - }else - { - double dn = double(num); - int nb = (int) ceil(dn/GlobalUtil::_texMaxDim/align); - fh = align * nb; - if(nb <=1) - { - fw = (int)ceil(dn / fh); - //align this dimension to blocksize - fw = ((int) ceil(double(fw) /align))*align; - }else - { - fw = GlobalUtil::_texMaxDim; - } - - } - - -} - -void PyramidGL::GetTextureStorageSize(int num, int &fw, int& fh) -{ - if(num <=0) - { - fw = fh = 0; - }else if(num <= GlobalUtil::_FeatureTexBlock) - { - fw = num; - fh = 1; - }else if(GlobalUtil::_NarrowFeatureTex) - { - double dn = double(num); - int nb = (int) ceil(dn/GlobalUtil::_texMaxDim/GlobalUtil::_FeatureTexBlock); - fw = GlobalUtil::_FeatureTexBlock * nb; - fh = (int)ceil(dn /fw);/**/ - }else - { - double dn = double(num); - int nb = (int) ceil(dn/GlobalUtil::_texMaxDim/GlobalUtil::_FeatureTexBlock); - fh = GlobalUtil::_FeatureTexBlock * nb; - if(nb <=1) - { - fw = (int)ceil(dn / fh); - - //align this dimension to blocksize - - // - if( fw < fh) - { - int temp = fh; - fh = fw; - fw = temp; - } - }else - { - fw = GlobalUtil::_texMaxDim; - } - } -} - -void PyramidNaive::DestroyPyramidData() -{ - if(_texPyramid) - { - delete [] _texPyramid; - _texPyramid = NULL; - } - if(_auxPyramid) - { - delete [] _auxPyramid; - _auxPyramid = NULL; - } -} -PyramidGL::PyramidGL(SiftParam &sp):SiftPyramid(sp) -{ - _featureTex = NULL; - _orientationTex = NULL; - _descriptorTex = NULL; - _histoPyramidTex = NULL; - ////////////////////////// - InitializeContext(); -} - -PyramidGL::~PyramidGL() -{ - DestroyPerLevelData(); - DestroySharedData(); - ShaderMan::DestroyShaders(); -} - -void PyramidGL::InitializeContext() -{ - GlobalUtil::InitGLParam(0); - if(!GlobalUtil::_GoodOpenGL) return; - - ////////////////////////////////////////////// - ShaderMan::InitShaderMan(param); -} - -void PyramidGL::DestroyPerLevelData() -{ - //integers vector to store the feature numbers. - if(_levelFeatureNum) - { - delete [] _levelFeatureNum; - _levelFeatureNum = NULL; - } - //texture used to store features - if( _featureTex) - { - delete [] _featureTex; - _featureTex = NULL; - } - //texture used for multi-orientation - if(_orientationTex) - { - delete [] _orientationTex; - _orientationTex = NULL; - } - int no = _octave_num* param._dog_level_num; - - //two sets of vbos used to display the features - if(_featureDisplayVBO) - { - glDeleteBuffers(no, _featureDisplayVBO); - delete [] _featureDisplayVBO; - _featureDisplayVBO = NULL; - } - if( _featurePointVBO) - { - glDeleteBuffers(no, _featurePointVBO); - delete [] _featurePointVBO; - _featurePointVBO = NULL; - } - -} - -void PyramidGL::DestroySharedData() -{ - //histogram reduction - if(_histoPyramidTex) - { - delete[] _histoPyramidTex; - _hpLevelNum = 0; - _histoPyramidTex = NULL; - } - - //descriptor storage shared by all levels - if(_descriptorTex) - { - delete [] _descriptorTex; - _descriptorTex = NULL; - } - //cpu reduction buffer. - if(_histo_buffer) - { - delete[] _histo_buffer; - _histo_buffer = 0; - } -} - -void PyramidNaive::FitHistogramPyramid() -{ - GLTexImage * tex, *htex; - int hist_level_num = _hpLevelNum - _pyramid_octave_first; - - tex = GetBaseLevel(_octave_min , DATA_KEYPOINT) + 2; - htex = _histoPyramidTex + hist_level_num - 1; - int w = tex->GetImgWidth() >> 1; - int h = tex->GetImgHeight() >> 1; - - for(int k = 0; k <hist_level_num -1; k++, htex--) - { - if(htex->GetImgHeight()!= h || htex->GetImgWidth() != w) - { - htex->SetImageSize(w, h); - htex->ZeroHistoMargin(); - } - - w = (w + 1)>>1; h = (h + 1) >> 1; - } -} - -void PyramidNaive::FitPyramid(int w, int h) -{ - //(w, h) <= (_pyramid_width, _pyramid_height); - - _pyramid_octave_first = 0; - // - _octave_num = GlobalUtil::_octave_num_default; - - int _octave_num_max = GetRequiredOctaveNum(min(w, h)); - - if(_octave_num < 1 || _octave_num > _octave_num_max) - { - _octave_num = _octave_num_max; - } - - - int pw = _pyramid_width>>1, ph = _pyramid_height>>1; - while(_pyramid_octave_first + _octave_num < _pyramid_octave_num && - pw >= w && ph >= h) - { - _pyramid_octave_first++; - pw >>= 1; - ph >>= 1; - } - - for(int i = 0; i < _octave_num; i++) - { - GLTexImage * tex = GetBaseLevel(i + _octave_min); - GLTexImage * aux = GetBaseLevel(i + _octave_min, DATA_KEYPOINT); - for(int j = param._level_min; j <= param._level_max; j++, tex++, aux++) - { - tex->SetImageSize(w, h); - aux->SetImageSize(w, h); - } - w>>=1; - h>>=1; - } -} -void PyramidNaive::InitPyramid(int w, int h, int ds) -{ - int wp, hp, toobig = 0; - if(ds == 0) - { - _down_sample_factor = 0; - if(GlobalUtil::_octave_min_default>=0) - { - wp = w >> GlobalUtil::_octave_min_default; - hp = h >> GlobalUtil::_octave_min_default; - }else - { - wp = w << (-GlobalUtil::_octave_min_default); - hp = h << (-GlobalUtil::_octave_min_default); - } - _octave_min = _octave_min_default; - }else - { - //must use 0 as _octave_min; - _octave_min = 0; - _down_sample_factor = ds; - w >>= ds; - h >>= ds; - wp = w; - hp = h; - - } - - while(wp > GlobalUtil::_texMaxDim || hp > GlobalUtil::_texMaxDim) - { - _octave_min ++; - wp >>= 1; - hp >>= 1; - toobig = 1; - } - - while(GlobalUtil::_MemCapGPU > 0 && GlobalUtil::_FitMemoryCap && (wp >_pyramid_width || hp > _pyramid_height) && - max(max(wp, hp), max(_pyramid_width, _pyramid_height)) > 1024 * sqrt(GlobalUtil::_MemCapGPU / 140.0)) - { - _octave_min ++; - wp >>= 1; - hp >>= 1; - toobig = 2; - } - - if(toobig && GlobalUtil::_verbose) - { - std::cout<<(toobig == 2 ? "[**SKIP OCTAVES**]:\tExceeding Memory Cap (-nomc)\n" : - "[**SKIP OCTAVES**]:\tReaching the dimension limit (-maxd)!\n"); - } - - if( wp == _pyramid_width && hp == _pyramid_height && _allocated ) - { - FitPyramid(wp, hp); - }else if(GlobalUtil::_ForceTightPyramid || _allocated ==0) - { - ResizePyramid(wp, hp); - } - else if( wp > _pyramid_width || hp > _pyramid_height ) - { - ResizePyramid(max(wp, _pyramid_width), max(hp, _pyramid_height)); - if(wp < _pyramid_width || hp < _pyramid_height) FitPyramid(wp, hp); - } - else - { - //try use the pyramid allocated for large image on small input images - FitPyramid(wp, hp); - } - - //select the initial smoothing filter according to the new _octave_min - ShaderMan::SelectInitialSmoothingFilter(_octave_min + _down_sample_factor, param); -} - -void PyramidNaive::ResizePyramid( int w, int h) -{ - // - unsigned int totalkb = 0; - int _octave_num_new, input_sz; - int i, j; - GLTexImage * tex, *aux; - // - - if(_pyramid_width == w && _pyramid_height == h && _allocated) return; - - if(w > GlobalUtil::_texMaxDim || h > GlobalUtil::_texMaxDim) return ; - - if(GlobalUtil::_verbose && GlobalUtil::_timingS) std::cout<<"[Allocate Pyramid]:\t" <<w<<"x"<<h<<endl; - //first octave does not change - _pyramid_octave_first = 0; - - - //compute # of octaves - - input_sz = min(w,h) ; - - - _pyramid_width = w; - _pyramid_height = h; - - //reset to preset parameters - _octave_num_new = GlobalUtil::_octave_num_default; - - if(_octave_num_new < 1) _octave_num_new = GetRequiredOctaveNum(input_sz) ; - - if(_pyramid_octave_num != _octave_num_new) - { - //destroy the original pyramid if the # of octave changes - if(_octave_num >0) - { - DestroyPerLevelData(); - DestroyPyramidData(); - } - _pyramid_octave_num = _octave_num_new; - } - - _octave_num = _pyramid_octave_num; - - int noct = _octave_num; - int nlev = param._level_num; - - // //initialize the pyramid - if(_texPyramid==NULL) _texPyramid = new GLTexImage[ noct* nlev ]; - if(_auxPyramid==NULL) _auxPyramid = new GLTexImage[ noct* nlev ]; - - - tex = GetBaseLevel(_octave_min, DATA_GAUSSIAN); - aux = GetBaseLevel(_octave_min, DATA_KEYPOINT); - for(i = 0; i< noct; i++) - { - totalkb += (nlev * w * h * 16 / 1024); - for( j = 0; j< nlev; j++, tex++) - { - tex->InitTexture(w, h); - //tex->AttachToFBO(0); - } - //several auxilary textures are not actually required - totalkb += ((nlev - 3) * w * h * 16 /1024); - for( j = 0; j< nlev ; j++, aux++) - { - if(j < 2) continue; - if(j >= nlev - 1) continue; - aux->InitTexture(w, h, 0); - //aux->AttachToFBO(0); - } - - w>>=1; - h>>=1; - } - - totalkb += ResizeFeatureStorage(); - - - // - _allocated = 1; - - if(GlobalUtil::_verbose && GlobalUtil::_timingS) std::cout<<"[Allocate Pyramid]:\t" <<(totalkb/1024)<<"MB\n"; - -} - - -int PyramidGL::ResizeFeatureStorage() -{ - int totalkb = 0; - if(_levelFeatureNum==NULL) _levelFeatureNum = new int[_octave_num * param._dog_level_num]; - std::fill(_levelFeatureNum, _levelFeatureNum+_octave_num * param._dog_level_num, 0); - - int wmax = GetBaseLevel(_octave_min)->GetDrawWidth(); - int hmax = GetBaseLevel(_octave_min)->GetDrawHeight(); - int w ,h, i; - - //use a fbo to initialize textures.. - FrameBufferObject fbo; - - // - if(_histo_buffer == NULL) _histo_buffer = new float[((size_t)1) << (2 + 2 * GlobalUtil::_ListGenSkipGPU)]; - //histogram for feature detection - - int num = (int)ceil(log(double(max(wmax, hmax)))/log(2.0)); - - if( _hpLevelNum != num) - { - _hpLevelNum = num; - if(GlobalUtil::_ListGenGPU) - { - if(_histoPyramidTex ) delete [] _histoPyramidTex; - _histoPyramidTex = new GLTexImage[_hpLevelNum]; - w = h = 1 ; - for(i = 0; i < _hpLevelNum; i++) - { - _histoPyramidTex[i].InitTexture(w, h, 0); - _histoPyramidTex[i].AttachToFBO(0); - w<<=1; - h<<=1; - } - } - } - - // (4 ^ (_hpLevelNum) -1 / 3) pixels - if(GlobalUtil::_ListGenGPU) totalkb += (((1 << (2 * _hpLevelNum)) -1) / 3 * 16 / 1024); - - - - //initialize the feature texture - - int idx = 0, n = _octave_num * param._dog_level_num; - if(_featureTex==NULL) _featureTex = new GLTexImage[n]; - if(GlobalUtil::_MaxOrientation >1 && GlobalUtil::_OrientationPack2==0) - { - if(_orientationTex== NULL) _orientationTex = new GLTexImage[n]; - } - - - for(i = 0; i < _octave_num; i++) - { - GLTexImage * tex = GetBaseLevel(i+_octave_min); - int fmax = int(tex->GetImgWidth()*tex->GetImgHeight()*GlobalUtil::_MaxFeaturePercent); - int fw, fh; - // - if(fmax > GlobalUtil::_MaxLevelFeatureNum) fmax = GlobalUtil::_MaxLevelFeatureNum; - else if(fmax < 32) fmax = 32; //give it at least a space of 32 feature - - GetTextureStorageSize(fmax, fw, fh); - - for(int j = 0; j < param._dog_level_num; j++, idx++) - { - - _featureTex[idx].InitTexture(fw, fh, 0); - _featureTex[idx].AttachToFBO(0); - // - if(_orientationTex) - { - _orientationTex[idx].InitTexture(fw, fh, 0); - _orientationTex[idx].AttachToFBO(0); - } - } - totalkb += fw * fh * 16 * param._dog_level_num * (_orientationTex? 2 : 1) /1024; - } - - - //this just need be initialized once - if(_descriptorTex==NULL) - { - //initialize feature texture pyramid - wmax = _featureTex->GetImgWidth(); - hmax = _featureTex->GetImgHeight(); - - int nf, ns; - if(GlobalUtil::_DescriptorPPT) - { - //32*4 = 128. - nf = 32 / GlobalUtil::_DescriptorPPT; // how many textures we need - ns = max(4, GlobalUtil::_DescriptorPPT); // how many point in one texture for one descriptor - }else - { - //at least one, resue for visualization and other work - nf = 1; ns = 4; - } - // - _alignment = ns; - // - _descriptorTex = new GLTexImage[nf]; - - int fw, fh; - GetAlignedStorageSize(hmax*wmax* max(ns, 10), _alignment, fw, fh); - - if(fh < hmax ) fh = hmax; - if(fw < wmax ) fw = wmax; - - totalkb += ( fw * fh * nf * 16 /1024); - for(i =0; i < nf; i++) - { - _descriptorTex[i].InitTexture(fw, fh); - } - }else - { - int nf = GlobalUtil::_DescriptorPPT? 32 / GlobalUtil::_DescriptorPPT: 1; - totalkb += nf * _descriptorTex[0].GetTexWidth() * _descriptorTex[0].GetTexHeight() * 16 /1024; - } - return totalkb; -} - - -void PyramidNaive::BuildPyramid(GLTexInput *input) -{ - USE_TIMING(); - GLTexPacked * tex; - FilterProgram** filter; - FrameBufferObject fbo; - - glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); - input->FitTexViewPort(); - - for (int i = _octave_min; i < _octave_min + _octave_num; i++) - { - - tex = (GLTexPacked*)GetBaseLevel(i); - filter = ShaderMan::s_bag->f_gaussian_step; - - OCTAVE_START(); - - if( i == _octave_min ) - { - if(i < 0) TextureUpSample(tex, input, 1<<(-i) ); - else TextureDownSample(tex, input, 1<<i); - ShaderMan::FilterInitialImage(tex, NULL); - }else - { - TextureDownSample(tex, GetLevelTexture(i-1, param._level_ds)); - ShaderMan::FilterSampledImage(tex, NULL); - } - LEVEL_FINISH(); - - for(int j = param._level_min + 1; j <= param._level_max ; j++, tex++, filter++) - { - // filtering - ShaderMan::FilterImage(*filter, tex+1, tex, NULL); - LEVEL_FINISH(); - } - OCTAVE_FINISH(); - - } - if(GlobalUtil::_timingS) glFinish(); - UnloadProgram(); -} - - - - - - -GLTexImage* PyramidNaive::GetLevelTexture(int octave, int level, int dataName) -{ - if(octave <_octave_min || octave > _octave_min + _octave_num) return NULL; - switch(dataName) - { - case DATA_GAUSSIAN: - case DATA_DOG: - case DATA_GRAD: - case DATA_ROT: - return _texPyramid+ (_pyramid_octave_first + octave - _octave_min) * param._level_num + (level - param._level_min); - case DATA_KEYPOINT: - return _auxPyramid + (_pyramid_octave_first + octave - _octave_min) * param._level_num + (level - param._level_min); - default: - return NULL; - } -} - -GLTexImage* PyramidNaive::GetLevelTexture(int octave, int level) -{ - return _texPyramid+ (_pyramid_octave_first + octave - _octave_min) * param._level_num - + (level - param._level_min); -} - -//in the packed implementation -// DATA_GAUSSIAN, DATA_DOG, DATA_GAD will be stored in different textures. -GLTexImage* PyramidNaive::GetBaseLevel(int octave, int dataName) -{ - if(octave <_octave_min || octave > _octave_min + _octave_num) return NULL; - switch(dataName) - { - case DATA_GAUSSIAN: - case DATA_DOG: - case DATA_GRAD: - case DATA_ROT: - return _texPyramid+ (_pyramid_octave_first + octave - _octave_min) * param._level_num; - case DATA_KEYPOINT: - return _auxPyramid + (_pyramid_octave_first + octave - _octave_min) * param._level_num; - default: - return NULL; - } -} - - - - - - - - - -void PyramidNaive::ComputeGradient() -{ - - int i, j; - double ts, t1; - GLTexImage * tex; - FrameBufferObject fbo; - - - if(GlobalUtil::_timingS && GlobalUtil::_verbose)ts = CLOCK(); - - glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); - - for ( i = _octave_min; i < _octave_min + _octave_num; i++) - { - for( j = param._level_min + 1 ; j < param._level_max ; j++) - { - tex = GetLevelTexture(i, j); - tex->FitTexViewPort(); - tex->AttachToFBO(0); - tex->BindTex(); - ShaderMan::UseShaderGradientPass(); - tex->DrawQuadMT4(); - } - } - - if(GlobalUtil::_timingS && GlobalUtil::_verbose) - { - glFinish(); - t1 = CLOCK(); - std::cout<<"<Compute Gradient>\t"<<(t1-ts)<<"\n"; - } - - UnloadProgram(); - GLTexImage::UnbindMultiTex(3); - fbo.UnattachTex(GL_COLOR_ATTACHMENT1_EXT); -} - - -//keypoint detection with subpixel localization -void PyramidNaive::DetectKeypointsEX() -{ - int i, j; - double t0, t, ts, t1, t2; - GLTexImage * tex, *aux; - FrameBufferObject fbo; - - if(GlobalUtil::_timingS && GlobalUtil::_verbose)ts = CLOCK(); - - glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); - //extra gradient data required for visualization - int gradient_only_levels[2] = {param._level_min +1, param._level_max}; - int n_gradient_only_level = GlobalUtil::_UseSiftGPUEX ? 2 : 1; - for ( i = _octave_min; i < _octave_min + _octave_num; i++) - { - for( j =0; j < n_gradient_only_level ; j++) - { - tex = GetLevelTexture(i, gradient_only_levels[j]); - tex->FitTexViewPort(); - tex->AttachToFBO(0); - tex->BindTex(); - ShaderMan::UseShaderGradientPass(); - tex->DrawQuadMT4(); - } - } - - if(GlobalUtil::_timingS && GlobalUtil::_verbose) - { - glFinish(); - t1 = CLOCK(); - } - - GLenum buffers[] = { GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT }; - glDrawBuffers(2, buffers); - for ( i = _octave_min; i < _octave_min + _octave_num; i++) - { - if(GlobalUtil::_timingO) - { - t0 = CLOCK(); - std::cout<<"#"<<(i + _down_sample_factor)<<"\t"; - } - tex = GetBaseLevel(i) + 2; - aux = GetBaseLevel(i, DATA_KEYPOINT) +2; - aux->FitTexViewPort(); - - for( j = param._level_min + 2; j < param._level_max ; j++, aux++, tex++) - { - if(GlobalUtil::_timingL)t = CLOCK(); - tex->AttachToFBO(0); - aux->AttachToFBO(1); - glActiveTexture(GL_TEXTURE0); - tex->BindTex(); - glActiveTexture(GL_TEXTURE1); - (tex+1)->BindTex(); - glActiveTexture(GL_TEXTURE2); - (tex-1)->BindTex(); - ShaderMan::UseShaderKeypoint((tex+1)->GetTexID(), (tex-1)->GetTexID()); - aux->DrawQuadMT8(); - - if(GlobalUtil::_timingL) - { - glFinish(); - std::cout<<(CLOCK()-t)<<"\t"; - } - tex->DetachFBO(0); - aux->DetachFBO(1); - } - if(GlobalUtil::_timingO) - { - std::cout<<"|\t"<<(CLOCK()-t0)<<"\n"; - } - } - - if(GlobalUtil::_timingS) - { - glFinish(); - t2 = CLOCK(); - if(GlobalUtil::_verbose) - std::cout <<"<Get Keypoints .. >\t"<<(t2-t1)<<"\n" - <<"<Extra Gradient.. >\t"<<(t1-ts)<<"\n"; - } - UnloadProgram(); - GLTexImage::UnbindMultiTex(3); - fbo.UnattachTex(GL_COLOR_ATTACHMENT1_EXT); - - -} - -void PyramidNaive::GenerateFeatureList(int i, int j) -{ - int hist_level_num = _hpLevelNum - _pyramid_octave_first; - int hist_skip_gpu = GlobalUtil::_ListGenSkipGPU; - int idx = i * param._dog_level_num + j; - GLTexImage* htex, *ftex, *tex; - tex = GetBaseLevel(_octave_min + i, DATA_KEYPOINT) + 2 + j; - ftex = _featureTex + idx; - htex = _histoPyramidTex + hist_level_num - 1 - i; - - /// - glActiveTexture(GL_TEXTURE0); - tex->BindTex(); - htex->AttachToFBO(0); - int tight = ((htex->GetImgWidth() * 2 == tex->GetImgWidth() -1 || tex->GetTexWidth() == tex->GetImgWidth()) && - (htex->GetImgHeight() *2 == tex->GetImgHeight()-1 || tex->GetTexHeight() == tex->GetImgHeight())); - ShaderMan::UseShaderGenListInit(tex->GetImgWidth(), tex->GetImgHeight(), tight); - htex->FitTexViewPort(); - //this uses the fact that no feature is on the edge. - htex->DrawQuadReduction(); - - //reduction.. - htex--; - - //this part might have problems on several GPUS - //because the output of one pass is the input of the next pass - //need to call glFinish to make it right - //but too much glFinish makes it slow - for(int k = 0; k <hist_level_num - i - 1 - hist_skip_gpu; k++, htex--) - { - htex->AttachToFBO(0); - htex->FitTexViewPort(); - (htex+1)->BindTex(); - ShaderMan::UseShaderGenListHisto(); - htex->DrawQuadReduction(); - } - - // - if(hist_skip_gpu == 0) - { - //read back one pixel - float fn[4], fcount; - glReadPixels(0, 0, 1, 1, GL_RGBA , GL_FLOAT, fn); - fcount = (fn[0] + fn[1] + fn[2] + fn[3]); - if(fcount < 1) fcount = 0; - - - _levelFeatureNum[ idx] = (int)(fcount); - SetLevelFeatureNum(idx, (int)fcount); - _featureNum += int(fcount); - - // - if(fcount < 1.0) return; - - - ///generate the feature texture - - htex= _histoPyramidTex; - - htex->BindTex(); - - //first pass - ftex->AttachToFBO(0); - if(GlobalUtil::_MaxOrientation>1) - { - //this is very important... - ftex->FitRealTexViewPort(); - glClear(GL_COLOR_BUFFER_BIT); - glFinish(); - }else - { - ftex->FitTexViewPort(); - //glFinish(); - } - - - ShaderMan::UseShaderGenListStart((float)ftex->GetImgWidth(), htex->GetTexID()); - - ftex->DrawQuad(); - //make sure it finishes before the next step - ftex->DetachFBO(0); - - //pass on each pyramid level - htex++; - }else - { - - int tw = htex[1].GetDrawWidth(), th = htex[1].GetDrawHeight(); - int fc = 0; - glReadPixels(0, 0, tw, th, GL_RGBA , GL_FLOAT, _histo_buffer); - _keypoint_buffer.resize(0); - for(int y = 0, pos = 0; y < th; y++) - { - for(int x= 0; x < tw; x++) - { - for(int c = 0; c < 4; c++, pos++) - { - int ss = (int) _histo_buffer[pos]; - if(ss == 0) continue; - float ft[4] = {2 * x + (c%2? 1.5f: 0.5f), 2 * y + (c>=2? 1.5f: 0.5f), 0, 1 }; - for(int t = 0; t < ss; t++) - { - ft[2] = (float) t; - _keypoint_buffer.insert(_keypoint_buffer.end(), ft, ft+4); - } - fc += (int)ss; - } - } - } - _levelFeatureNum[ idx] = fc; - SetLevelFeatureNum(idx, fc); - if(fc == 0) return; - _featureNum += fc; - ///////////////////// - ftex->AttachToFBO(0); - if(GlobalUtil::_MaxOrientation>1) - { - ftex->FitRealTexViewPort(); - glClear(GL_COLOR_BUFFER_BIT); - glFlush(); - }else - { - ftex->FitTexViewPort(); - glFlush(); - } - _keypoint_buffer.resize(ftex->GetDrawWidth() * ftex->GetDrawHeight()*4, 0); - /////////// - glActiveTexture(GL_TEXTURE0); - ftex->BindTex(); - glTexSubImage2D(GlobalUtil::_texTarget, 0, 0, 0, ftex->GetDrawWidth(), - ftex->GetDrawHeight(), GL_RGBA, GL_FLOAT, &_keypoint_buffer[0]); - htex += 2; - } - - for(int lev = 1 + hist_skip_gpu; lev < hist_level_num - i; lev++, htex++) - { - - glActiveTexture(GL_TEXTURE0); - ftex->BindTex(); - ftex->AttachToFBO(0); - glActiveTexture(GL_TEXTURE1); - htex->BindTex(); - ShaderMan::UseShaderGenListStep(ftex->GetTexID(), htex->GetTexID()); - ftex->DrawQuad(); - ftex->DetachFBO(0); - } - GLTexImage::UnbindMultiTex(2); - -} - -//generate feature list on GPU -void PyramidNaive::GenerateFeatureList() -{ - //generate the histogram0pyramid - FrameBufferObject fbo; - glReadBuffer(GL_COLOR_ATTACHMENT0_EXT); - glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); - double t1, t2; - int ocount, reverse = (GlobalUtil::_TruncateMethod == 1); - _featureNum = 0; - - FitHistogramPyramid(); - - //for(int i = 0, idx = 0; i < _octave_num; i++) - FOR_EACH_OCTAVE(i, reverse) - { - //output - if(GlobalUtil::_timingO) - { - t1= CLOCK(); - ocount = 0; - std::cout<<"#"<<i+_octave_min + _down_sample_factor<<":\t"; - } - //for(int j = 0; j < param._dog_level_num; j++, idx++) - FOR_EACH_LEVEL(j, reverse) - { - - if(GlobalUtil::_TruncateMethod && GlobalUtil::_FeatureCountThreshold > 0 - && _featureNum > GlobalUtil::_FeatureCountThreshold) - { - _levelFeatureNum[i * param._dog_level_num + j] = 0; - continue; - }else - { - GenerateFeatureList(i, j); - if(GlobalUtil::_timingO) - { - int idx = i * param._dog_level_num + j; - std::cout<< _levelFeatureNum[idx] <<"\t"; - ocount += _levelFeatureNum[idx]; - } - } - } - if(GlobalUtil::_timingO) - { - t2 = CLOCK(); - std::cout << "| \t" << int(ocount) << " :\t(" << (t2 - t1) << ")\n"; - } - } - if(GlobalUtil::_timingS)glFinish(); - if(GlobalUtil::_verbose) - { - std::cout<<"#Features:\t"<<_featureNum<<"\n"; - } -} - - -void PyramidGL::GenerateFeatureDisplayVBO() -{ - //use a big VBO to save all the SIFT box vertices - int w, h, esize; GLint bsize; - int nvbo = _octave_num * param._dog_level_num; - //initialize the vbos - if(_featureDisplayVBO==NULL) - { - _featureDisplayVBO = new GLuint[nvbo]; - glGenBuffers( nvbo, _featureDisplayVBO ); - } - if(_featurePointVBO == NULL) - { - _featurePointVBO = new GLuint[nvbo]; - glGenBuffers(nvbo, _featurePointVBO); - } - - FrameBufferObject fbo; - glReadBuffer(GL_COLOR_ATTACHMENT0_EXT); - glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); - glActiveTexture(GL_TEXTURE0); - // - GLTexImage & tempTex = *_descriptorTex; - // - for(int i = 0, idx = 0; i < _octave_num; i++) - { - for(int j = 0; j < param._dog_level_num; j ++, idx++) - { - GLTexImage * ftex = _featureTex + idx; - if(_levelFeatureNum[idx]<=0)continue; - - //copy the texture into vbo - fbo.BindFBO(); - tempTex.AttachToFBO(0); - - ftex->BindTex(); - ftex->FitTexViewPort(); - ShaderMan::UseShaderCopyKeypoint(); - ftex->DrawQuad(); - - glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, _featurePointVBO[ idx]); - glGetBufferParameteriv(GL_PIXEL_PACK_BUFFER_ARB, GL_BUFFER_SIZE, &bsize); - esize = ftex->GetImgHeight() * ftex->GetImgWidth()*sizeof(float) *4; - - //increase size when necessary - if(bsize < esize) - { - glBufferData(GL_PIXEL_PACK_BUFFER_ARB, esize*3/2 , NULL, GL_STATIC_DRAW_ARB); - glGetBufferParameteriv(GL_PIXEL_PACK_BUFFER_ARB, GL_BUFFER_SIZE, &bsize); - } - - //read back if we have enough buffer - if(bsize >= esize) glReadPixels(0, 0, ftex->GetImgWidth(), ftex->GetImgHeight(), GL_RGBA, GL_FLOAT, 0); - else glBufferData(GL_PIXEL_PACK_BUFFER_ARB, 0, NULL, GL_STATIC_DRAW_ARB); - glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0); - - - //box display vbo - int count = _levelFeatureNum[idx]* 10; - GetAlignedStorageSize(count, _alignment, w, h); - w = (int)ceil(double(count)/ h); - - //input - fbo.BindFBO(); - ftex->BindTex(); - - //output - tempTex.AttachToFBO(0); - GlobalUtil::FitViewPort(w, h); - //shader - ShaderMan::UseShaderGenVBO( (float)ftex->GetImgWidth(), (float) w, - param.GetLevelSigma(j + param._level_min + 1)); - GLTexImage::DrawQuad(0, (float)w, 0, (float)h); - - // - glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, _featureDisplayVBO[ idx]); - glGetBufferParameteriv(GL_PIXEL_PACK_BUFFER_ARB, GL_BUFFER_SIZE, &bsize); - esize = w*h * sizeof(float)*4; - //increase size when necessary - if(bsize < esize) - { - glBufferData(GL_PIXEL_PACK_BUFFER_ARB, esize*3/2, NULL, GL_STATIC_DRAW_ARB); - glGetBufferParameteriv(GL_PIXEL_PACK_BUFFER_ARB, GL_BUFFER_SIZE, &bsize); - } - - //read back if we have enough buffer - if(bsize >= esize) glReadPixels(0, 0, w, h, GL_RGBA, GL_FLOAT, 0); - else glBufferData(GL_PIXEL_PACK_BUFFER_ARB, 0, NULL, GL_STATIC_DRAW_ARB); - glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0); - - - - - } - } - glReadBuffer(GL_NONE); - glFinish(); - -} - - - - - -void PyramidNaive::GetFeatureOrientations() -{ - GLTexImage * gtex; - GLTexImage * stex = NULL; - GLTexImage * ftex = _featureTex; - GLTexImage * otex = _orientationTex; - int sid = 0; - int * count = _levelFeatureNum; - float sigma, sigma_step = powf(2.0f, 1.0f/param._dog_level_num); - FrameBufferObject fbo; - if(_orientationTex) - { - GLenum buffers[] = { GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT }; - glDrawBuffers(2, buffers); - }else - { - glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); - } - for(int i = 0; i < _octave_num; i++) - { - gtex = GetLevelTexture(i+_octave_min, param._level_min + 1); - if(GlobalUtil::_SubpixelLocalization || GlobalUtil::_KeepExtremumSign) - stex = GetBaseLevel(i+_octave_min, DATA_KEYPOINT) + 2; - - for(int j = 0; j < param._dog_level_num; j++, ftex++, otex++, count++, gtex++, stex++) - { - if(*count<=0)continue; - - sigma = param.GetLevelSigma(j+param._level_min+1); - - // - ftex->FitTexViewPort(); - - glActiveTexture(GL_TEXTURE0); - ftex->BindTex(); - glActiveTexture(GL_TEXTURE1); - gtex->BindTex(); - // - ftex->AttachToFBO(0); - if(_orientationTex) otex->AttachToFBO(1); - if(!_existing_keypoints && (GlobalUtil::_SubpixelLocalization|| GlobalUtil::_KeepExtremumSign)) - { - glActiveTexture(GL_TEXTURE2); - stex->BindTex(); - sid = * stex; - } - ShaderMan::UseShaderOrientation(gtex->GetTexID(), - gtex->GetImgWidth(), gtex->GetImgHeight(), - sigma, sid, sigma_step, _existing_keypoints); - ftex->DrawQuad(); - // glFinish(); - - } - } - - GLTexImage::UnbindMultiTex(3); - if(GlobalUtil::_timingS)glFinish(); - - if(_orientationTex) fbo.UnattachTex(GL_COLOR_ATTACHMENT1_EXT); - -} - - - -//to compare with GPU feature list generation -void PyramidNaive::GenerateFeatureListCPU() -{ - - FrameBufferObject fbo; - _featureNum = 0; - GLTexImage * tex = GetBaseLevel(_octave_min); - float * mem = new float [tex->GetTexWidth()*tex->GetTexHeight()]; - vector<float> list; - int idx = 0; - for(int i = 0; i < _octave_num; i++) - { - for(int j = 0; j < param._dog_level_num; j++, idx++) - { - tex = GetBaseLevel(_octave_min + i, DATA_KEYPOINT) + j + 2; - tex->BindTex(); - glGetTexImage(GlobalUtil::_texTarget, 0, GL_RED, GL_FLOAT, mem); - //tex->AttachToFBO(0); - //tex->FitTexViewPort(); - //glReadPixels(0, 0, tex->GetTexWidth(), tex->GetTexHeight(), GL_RED, GL_FLOAT, mem); - // - //make a list of - list.resize(0); - float * p = mem; - int fcount = 0 ; - for(int k = 0; k < tex->GetTexHeight(); k++) - { - for( int m = 0; m < tex->GetTexWidth(); m ++, p++) - { - if(*p==0)continue; - if(m ==0 || k ==0 || k >= tex->GetImgHeight() -1 || m >= tex->GetImgWidth() -1 ) continue; - list.push_back(m+0.5f); - list.push_back(k+0.5f); - list.push_back(0); - list.push_back(1); - fcount ++; - - - } - } - if(fcount==0)continue; - - - - GLTexImage * ftex = _featureTex+idx; - _levelFeatureNum[idx] = (fcount); - SetLevelFeatureNum(idx, fcount); - - _featureNum += (fcount); - - - int fw = ftex->GetImgWidth(); - int fh = ftex->GetImgHeight(); - - list.resize(4*fh*fw); - - ftex->BindTex(); - ftex->AttachToFBO(0); - // glTexImage2D(GlobalUtil::_texTarget, 0, GlobalUtil::_iTexFormat, fw, fh, 0, GL_BGRA, GL_FLOAT, &list[0]); - glTexSubImage2D(GlobalUtil::_texTarget, 0, 0, 0, fw, fh, GL_RGBA, GL_FLOAT, &list[0]); - // - } - } - GLTexImage::UnbindTex(); - delete[] mem; - if(GlobalUtil::_verbose) - { - std::cout<<"#Features:\t"<<_featureNum<<"\n"; - } -} - -#define FEATURELIST_USE_PBO - -void PyramidGL::ReshapeFeatureListCPU() -{ - //make a compact feature list, each with only one orientation - //download orientations and the featue list - //reshape it and upload it - - FrameBufferObject fbo; - int i, szmax =0, sz; - int n = param._dog_level_num*_octave_num; - for( i = 0; i < n; i++) - { - sz = _featureTex[i].GetImgWidth() * _featureTex[i].GetImgHeight(); - if(sz > szmax ) szmax = sz; - } - float * buffer = new float[szmax*24]; - float * buffer1 = buffer; - float * buffer2 = buffer + szmax*4; - float * buffer3 = buffer + szmax*8; - - glReadBuffer(GL_COLOR_ATTACHMENT0_EXT); - glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); - -#ifdef FEATURELIST_USE_PBO - GLuint ListUploadPBO; - glGenBuffers(1, &ListUploadPBO); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, ListUploadPBO); - glBufferData(GL_PIXEL_UNPACK_BUFFER_ARB, szmax * 8 * sizeof(float), NULL, GL_STREAM_DRAW); -#endif - - _featureNum = 0; - -#ifdef NO_DUPLICATE_DOWNLOAD - const double twopi = 2.0*3.14159265358979323846; - _keypoint_buffer.resize(0); - float os = _octave_min>=0? float(1<<_octave_min): 1.0f/(1<<(-_octave_min)); - if(_down_sample_factor>0) os *= float(1<<_down_sample_factor); - float offset = GlobalUtil::_LoweOrigin? 0 : 0.5f; -#endif - - for(i = 0; i < n; i++) - { - if(_levelFeatureNum[i]==0)continue; - - _featureTex[i].AttachToFBO(0); - _featureTex[i].FitTexViewPort(); - glReadPixels(0, 0, _featureTex[i].GetImgWidth(), _featureTex[i].GetImgHeight(),GL_RGBA, GL_FLOAT, buffer1); - - int fcount =0, ocount; - float * src = buffer1; - float * orientation = buffer2; - float * des = buffer3; - if(GlobalUtil::_OrientationPack2 == 0) - { - //read back orientations from another texture - _orientationTex[i].AttachToFBO(0); - glReadPixels(0, 0, _orientationTex[i].GetImgWidth(), _orientationTex[i].GetImgHeight(),GL_RGBA, GL_FLOAT, buffer2); - //make the feature list - for(int j = 0; j < _levelFeatureNum[i]; j++, src+=4, orientation+=4) - { - if(_existing_keypoints) - { - des[0] = src[0]; - des[1] = src[1]; - des[2] = orientation[0]; - des[3] = src[3]; - fcount++; - des += 4; - }else - { - ocount = (int)src[2]; - for(int k = 0 ; k < ocount; k++, des+=4) - { - des[0] = src[0]; - des[1] = src[1]; - des[2] = orientation[k]; - des[3] = src[3]; - fcount++; - } - } - } - }else - { - _featureTex[i].DetachFBO(0); - const static double factor = 2.0*3.14159265358979323846/65535.0; - for(int j = 0; j < _levelFeatureNum[i]; j++, src+=4) - { - unsigned short * orientations = (unsigned short*) (&src[2]); - if(_existing_keypoints) - { - des[0] = src[0]; - des[1] = src[1]; - des[2] = float( factor* orientations[0]); - des[3] = src[3]; - fcount++; - des += 4; - }else - { - if(orientations[0] != 65535) - { - des[0] = src[0]; - des[1] = src[1]; - des[2] = float( factor* orientations[0]); - des[3] = src[3]; - fcount++; - des += 4; - - if(orientations[1] != 65535) - { - des[0] = src[0]; - des[1] = src[1]; - des[2] = float(factor* orientations[1]); - des[3] = src[3]; - fcount++; - des += 4; - } - } - } - } - } - - if (fcount == 0){ _levelFeatureNum[i] = 0; continue; } - - //texture size -------------- - SetLevelFeatureNum(i, fcount); - int nfw = _featureTex[i].GetImgWidth(); - int nfh = _featureTex[i].GetImgHeight(); - int sz = nfh * nfw; - if(sz > fcount) memset(des, 0, sizeof(float) * (sz - fcount) * 4); - -#ifndef FEATURELIST_USE_PBO - _featureTex[i].BindTex(); - glTexSubImage2D(GlobalUtil::_texTarget, 0, 0, 0, nfw, nfh, GL_RGBA, GL_FLOAT, buffer3); - _featureTex[i].UnbindTex(); -#else - float* mem = (float*) glMapBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY); - memcpy(mem, buffer3, sz * 4 * sizeof(float) ); - glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER_ARB); - _featureTex[i].BindTex(); - glTexSubImage2D(GlobalUtil::_texTarget, 0, 0, 0, nfw, nfh, GL_RGBA, GL_FLOAT, 0); - _featureTex[i].UnbindTex(); -#endif - -#ifdef NO_DUPLICATE_DOWNLOAD - if(fcount > 0) - { - float oss = os * (1 << (i / param._dog_level_num)); - _keypoint_buffer.resize((_featureNum + fcount) * 4); - float* ds = &_keypoint_buffer[_featureNum * 4]; - float* fs = buffer3; - for(int k = 0; k < fcount; k++, ds+=4, fs+=4) - { - ds[0] = oss*(fs[0]-0.5f) + offset; //x - ds[1] = oss*(fs[1]-0.5f) + offset; //y - ds[3] = (float)fmod(twopi-fs[2], twopi); //orientation, mirrored - ds[2] = oss*fs[3]; //scale - } - } -#endif - _levelFeatureNum[i] = fcount; - _featureNum += fcount; - } - - delete[] buffer; - if(GlobalUtil::_verbose) - { - std::cout<<"#Features MO:\t"<<_featureNum<<endl; - } - /////////////////////////////////// -#ifdef FEATURELIST_USE_PBO - glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0); - glDeleteBuffers(1, &ListUploadPBO); -#endif -} - - - -inline void PyramidGL::SetLevelFeatureNum(int idx, int fcount) -{ - int fw, fh; - GLTexImage * ftex = _featureTex + idx; - //set feature texture size. normally fh will be one - GetTextureStorageSize(fcount, fw, fh); - if(fcount > ftex->GetTexWidth()*ftex->GetTexHeight()) - { - ftex->InitTexture(fw, fh, 0); - if(_orientationTex) _orientationTex[idx].InitTexture(fw, fh, 0); - - } - if(GlobalUtil::_NarrowFeatureTex) - fh = fcount ==0? 0:(int)ceil(double(fcount)/fw); - else - fw = fcount ==0? 0:(int)ceil(double(fcount)/fh); - ftex->SetImageSize(fw, fh); - if(_orientationTex) _orientationTex[idx].SetImageSize(fw, fh); -} - -void PyramidGL::CleanUpAfterSIFT() -{ - GLTexImage::UnbindMultiTex(3); - ShaderMan::UnloadProgram(); - FrameBufferObject::DeleteGlobalFBO(); - GlobalUtil::CleanupOpenGL(); -} - -void PyramidNaive::GetSimplifiedOrientation() -{ - // - int idx = 0; -// int n = _octave_num * param._dog_level_num; - float sigma, sigma_step = powf(2.0f, 1.0f/param._dog_level_num); - GLTexImage * ftex = _featureTex; - - FrameBufferObject fbo; - glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); - for(int i = 0; i < _octave_num; i++) - { - GLTexImage *gtex = GetLevelTexture(i+_octave_min, 2+param._level_min); - for(int j = 0; j < param._dog_level_num; j++, ftex++, gtex++, idx ++) - { - if(_levelFeatureNum[idx]<=0)continue; - sigma = param.GetLevelSigma(j+param._level_min+1); - - // - ftex->AttachToFBO(0); - ftex->FitTexViewPort(); - - glActiveTexture(GL_TEXTURE0); - ftex->BindTex(); - glActiveTexture(GL_TEXTURE1); - gtex->BindTex(); - - ShaderMan::UseShaderSimpleOrientation(gtex->GetTexID(), sigma, sigma_step); - ftex->DrawQuad(); - } - } - - GLTexImage::UnbindMultiTex(2); - -} - - -#ifdef USE_SSE_FOR_SIFTGPU - static inline float dotproduct_128d(float * p) - { - float z = 0.0f; - __m128 sse =_mm_load_ss(&z); - float* pf = (float*) (&sse); - for( int i = 0; i < 32; i++, p+=4) - { - __m128 ps = _mm_loadu_ps(p); - sse = _mm_add_ps(sse, _mm_mul_ps(ps, ps)); - } - return pf[0] + pf[1] + pf[2] + pf[3]; - - } - static inline void multiply_and_truncate_128d(float* p, float m) - { - float z = 0.2f; - __m128 t = _mm_load_ps1(&z); - __m128 r = _mm_load_ps1(&m); - for(int i = 0; i < 32; i++, p+=4) - { - __m128 ps = _mm_loadu_ps(p); - _mm_storeu_ps(p, _mm_min_ps(_mm_mul_ps(ps, r), t)); - } - } - static inline void multiply_128d(float* p, float m) - { - __m128 r = _mm_load_ps1(&m); - for(int i = 0; i < 32; i++, p+=4) - { - __m128 ps = _mm_loadu_ps(p); - _mm_storeu_ps(p, _mm_mul_ps(ps, r)); - } - } -#endif - - -inline void PyramidGL::NormalizeDescriptor(int num, float*pd) -{ - -#ifdef USE_SSE_FOR_SIFTGPU - for(int k = 0; k < num; k++, pd +=128) - { - float sq; - //normalize and truncate to .2 - sq = dotproduct_128d(pd); sq = 1.0f / sqrtf(sq); - multiply_and_truncate_128d(pd, sq); - - //renormalize - sq = dotproduct_128d(pd); sq = 1.0f / sqrtf(sq); - multiply_128d(pd, sq); - } -#else - //descriptor normalization runs on cpu for OpenGL implemenations - for(int k = 0; k < num; k++, pd +=128) - { - int v; - float* ppd, sq = 0; - //int v; - //normalize - ppd = pd; - for(v = 0 ; v < 128; v++, ppd++) sq += (*ppd)*(*ppd); - sq = 1.0f / sqrtf(sq); - //truncate to .2 - ppd = pd; - for(v = 0; v < 128; v ++, ppd++) *ppd = min(*ppd*sq, 0.2f); - - //renormalize - ppd = pd; sq = 0; - for(v = 0; v < 128; v++, ppd++) sq += (*ppd)*(*ppd); - sq = 1.0f / sqrtf(sq); - - ppd = pd; - for(v = 0; v < 128; v ++, ppd++) *ppd = *ppd*sq; - } - -#endif -} - -inline void PyramidGL::InterlaceDescriptorF2(int w, int h, float* buf, float* pd, int step) -{ - /* - if(GlobalUtil::_DescriptorPPR == 8) - { - const int dstep = w * 128; - float* pp1 = buf; - float* pp2 = buf + step; - - for(int u = 0; u < h ; u++, pd+=dstep) - { - int v; - float* ppd = pd; - for(v= 0; v < w; v++) - { - for(int t = 0; t < 8; t++) - { - *ppd++ = *pp1++;*ppd++ = *pp1++;*ppd++ = *pp1++;*ppd++ = *pp1++; - *ppd++ = *pp2++;*ppd++ = *pp2++;*ppd++ = *pp2++;*ppd++ = *pp2++; - } - ppd += 64; - } - ppd = pd + 64; - for(v= 0; v < w; v++) - { - for(int t = 0; t < 8; t++) - { - *ppd++ = *pp1++;*ppd++ = *pp1++;*ppd++ = *pp1++;*ppd++ = *pp1++; - *ppd++ = *pp2++;*ppd++ = *pp2++;*ppd++ = *pp2++;*ppd++ = *pp2++; - } - ppd += 64; - } - } - - }else */ - if(GlobalUtil::_DescriptorPPR == 8) - { - //interlace - for(int k = 0; k < 2; k++) - { - float* pp = buf + k * step; - float* ppd = pd + k * 4; - for(int u = 0; u < h ; u++) - { - int v; - for(v= 0; v < w; v++) - { - for(int t = 0; t < 8; t++) - { - ppd[0] = pp[0]; - ppd[1] = pp[1]; - ppd[2] = pp[2]; - ppd[3] = pp[3]; - ppd += 8; - pp+= 4; - } - ppd += 64; - } - ppd += ( 64 - 128 * w ); - for(v= 0; v < w; v++) - { - for(int t = 0; t < 8; t++) - { - ppd[0] = pp[0]; - ppd[1] = pp[1]; - ppd[2] = pp[2]; - ppd[3] = pp[3]; - - ppd += 8; - pp+= 4; - } - ppd += 64; - } - ppd -=64; - } - } - }else if(GlobalUtil::_DescriptorPPR == 4) - { - - } - - - -} -void PyramidGL::GetFeatureDescriptors() -{ - //descriptors... - float sigma; - int idx, i, j, k, w, h; - int ndf = 32 / GlobalUtil::_DescriptorPPT; //number of textures - int block_width = GlobalUtil::_DescriptorPPR; - int block_height = GlobalUtil::_DescriptorPPT/GlobalUtil::_DescriptorPPR; - float* pd = &_descriptor_buffer[0], * pbuf = NULL; - vector<float>read_buffer, descriptor_buffer2; - - //use another buffer, if we need to re-order the descriptors - if(_keypoint_index.size() > 0) - { - descriptor_buffer2.resize(_descriptor_buffer.size()); - pd = &descriptor_buffer2[0]; - } - FrameBufferObject fbo; - - GLTexImage * gtex, *otex, * ftex; - GLenum buffers[8] = { - GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT , - GL_COLOR_ATTACHMENT2_EXT, GL_COLOR_ATTACHMENT3_EXT , - GL_COLOR_ATTACHMENT4_EXT, GL_COLOR_ATTACHMENT5_EXT , - GL_COLOR_ATTACHMENT6_EXT, GL_COLOR_ATTACHMENT7_EXT , - }; - - glDrawBuffers(ndf, buffers); - glReadBuffer(GL_COLOR_ATTACHMENT0_EXT); - - - for( i = 0, idx = 0, ftex = _featureTex; i < _octave_num; i++) - { - gtex = GetBaseLevel(i + _octave_min, DATA_GRAD) + 1; - otex = GetBaseLevel(i + _octave_min, DATA_ROT) + 1; - for( j = 0; j < param._dog_level_num; j++, ftex++, idx++, gtex++, otex++) - { - if(_levelFeatureNum[idx]==0)continue; - - sigma = IsUsingRectDescription()? 0 : param.GetLevelSigma(j+param._level_min+1); - int count = _levelFeatureNum[idx] * block_width; - GetAlignedStorageSize(count, block_width, w, h); - h = ((int)ceil(double(count) / w)) * block_height; - - //not enought space for holding the descriptor data - if(w > _descriptorTex[0].GetTexWidth() || h > _descriptorTex[0].GetTexHeight()) - { - for(k = 0; k < ndf; k++)_descriptorTex[k].InitTexture(w, h); - } - for(k = 0; k < ndf; k++) _descriptorTex[k].AttachToFBO(k); - GlobalUtil::FitViewPort(w, h); - glActiveTexture(GL_TEXTURE0); - ftex->BindTex(); - glActiveTexture(GL_TEXTURE1); - gtex->BindTex(); - if(otex!=gtex) - { - glActiveTexture(GL_TEXTURE2); - otex->BindTex(); - } - - ShaderMan::UseShaderDescriptor(gtex->GetTexID(), otex->GetTexID(), - w, ftex->GetImgWidth(), gtex->GetImgWidth(), gtex->GetImgHeight(), sigma); - GLTexImage::DrawQuad(0, (float)w, 0, (float)h); - - //read back float format descriptors and do normalization on CPU - int step = w*h*4; - if((unsigned int)step*ndf > read_buffer.size()) - { - read_buffer.resize(ndf*step); - } - pbuf = &read_buffer[0]; - - //read back - for(k = 0; k < ndf; k++, pbuf+=step) - { - glReadBuffer(GL_COLOR_ATTACHMENT0_EXT + k); - if(GlobalUtil::_IsNvidia || w * h <= 16384) //were - { - glReadPixels(0, 0, w, h, GL_RGBA, GL_FLOAT, pbuf); - }else - { - int hstep = 16384 / w; - for(int kk = 0; kk < h; kk += hstep) - glReadPixels(0, kk, w, min(hstep, h - kk), GL_RGBA, GL_FLOAT, pbuf + w * kk * 4); - } - } - - //the following two steps run on cpu, so better cpu better speed - //and release version can be a lot faster than debug version - //interlace data on the two texture to get the descriptor - InterlaceDescriptorF2(w / block_width, h / block_height, &read_buffer[0], pd, step); - - //need to do normalization - //the new version uses SSE to speed up this part - if(GlobalUtil::_NormalizedSIFT) NormalizeDescriptor(_levelFeatureNum[idx], pd); - - pd += 128*_levelFeatureNum[idx]; - glReadBuffer(GL_NONE); - } - } - - - //finally, put the descriptor back to their original order for existing keypoint list. - if(_keypoint_index.size() > 0) - { - for(i = 0; i < _featureNum; ++i) - { - int index = _keypoint_index[i]; - memcpy(&_descriptor_buffer[index*128], &descriptor_buffer2[i*128], 128 * sizeof(float)); - } - } - - //////////////////////// - GLTexImage::UnbindMultiTex(3); - glDrawBuffer(GL_NONE); - ShaderMan::UnloadProgram(); - if(GlobalUtil::_timingS)glFinish(); - for(i = 0; i < ndf; i++) fbo.UnattachTex(GL_COLOR_ATTACHMENT0_EXT +i); - -} - - -void PyramidGL::DownloadKeypoints() -{ - const double twopi = 2.0*3.14159265358979323846; - int idx = 0; - float * buffer = &_keypoint_buffer[0]; - vector<float> keypoint_buffer2; - //use a different keypoint buffer when processing with an exisint features list - //without orientation information. - if(_keypoint_index.size() > 0) - { - keypoint_buffer2.resize(_keypoint_buffer.size()); - buffer = &keypoint_buffer2[0]; - } - float * p = buffer, *ps, sigma; - GLTexImage * ftex = _featureTex; - FrameBufferObject fbo; - ftex->FitRealTexViewPort(); - ///////////////////// - float os = _octave_min>=0? float(1<<_octave_min): 1.0f/(1<<(-_octave_min)); - if(_down_sample_factor>0) os *= float(1<<_down_sample_factor); - float offset = GlobalUtil::_LoweOrigin? 0 : 0.5f; - ///////////////////// - for(int i = 0; i < _octave_num; i++, os *= 2.0f) - { - - for(int j = 0; j < param._dog_level_num; j++, idx++, ftex++) - { - - if(_levelFeatureNum[idx]>0) - { - ftex->AttachToFBO(0); - glReadPixels(0, 0, ftex->GetImgWidth(), ftex->GetImgHeight(),GL_RGBA, GL_FLOAT, p); - ps = p; - for(int k = 0; k < _levelFeatureNum[idx]; k++, ps+=4) - { - ps[0] = os*(ps[0]-0.5f) + offset; //x - ps[1] = os*(ps[1]-0.5f) + offset; //y - sigma = os*ps[3]; - ps[3] = (float)fmod(twopi-ps[2], twopi); //orientation, mirrored - ps[2] = sigma; //scale - } - p+= 4* _levelFeatureNum[idx]; - } - } - } - - //put the feature into their original order - - if(_keypoint_index.size() > 0) - { - for(int i = 0; i < _featureNum; ++i) - { - int index = _keypoint_index[i]; - memcpy(&_keypoint_buffer[index*4], &keypoint_buffer2[i*4], 4 * sizeof(float)); - } - } -} - - -void PyramidGL::GenerateFeatureListTex() -{ - //generate feature list texture from existing keypoints - //do feature sorting in the same time? - - FrameBufferObject fbo; - vector<float> list; - int idx = 0; - const double twopi = 2.0*3.14159265358979323846; - float sigma_half_step = powf(2.0f, 0.5f / param._dog_level_num); - float octave_sigma = _octave_min>=0? float(1<<_octave_min): 1.0f/(1<<(-_octave_min)); - float offset = GlobalUtil::_LoweOrigin? 0 : 0.5f; - if(_down_sample_factor>0) octave_sigma *= float(1<<_down_sample_factor); - - - std::fill(_levelFeatureNum, _levelFeatureNum + _octave_num * param._dog_level_num, 0); - - _keypoint_index.resize(0); // should already be 0 - for(int i = 0; i < _octave_num; i++, octave_sigma*= 2.0f) - { - for(int j = 0; j < param._dog_level_num; j++, idx++) - { - list.resize(0); - float level_sigma = param.GetLevelSigma(j + param._level_min + 1) * octave_sigma; - float sigma_min = level_sigma / sigma_half_step; - float sigma_max = level_sigma * sigma_half_step; - int fcount = 0 ; - for(int k = 0; k < _featureNum; k++) - { - float * key = &_keypoint_buffer[k*4]; - float sigmak = key[2]; - - ////////////////////////////////////// - if(IsUsingRectDescription()) sigmak = min(key[2], key[3]) / 12.0f; - - if( (sigmak >= sigma_min && sigmak < sigma_max) - ||(sigmak < sigma_min && i ==0 && j == 0) - ||(sigmak > sigma_max && j == param._dog_level_num - 1&& - (i == _octave_num -1 || GlobalUtil::_KeyPointListForceLevel0))) - { - //add this keypoint to the list - list.push_back((key[0] - offset) / octave_sigma + 0.5f); - list.push_back((key[1] - offset) / octave_sigma + 0.5f); - if(IsUsingRectDescription()) - { - list.push_back(key[2] / octave_sigma); - list.push_back(key[3] / octave_sigma); - }else - { - list.push_back((float)fmod(twopi-key[3], twopi)); - list.push_back(key[2] / octave_sigma); - } - fcount ++; - //save the index of keypoints - _keypoint_index.push_back(k); - } - } - - _levelFeatureNum[idx] = fcount; - if(fcount==0)continue; - GLTexImage * ftex = _featureTex+idx; - - SetLevelFeatureNum(idx, fcount); - - int fw = ftex->GetImgWidth(); - int fh = ftex->GetImgHeight(); - - list.resize(4*fh*fw); - - ftex->BindTex(); - ftex->AttachToFBO(0); - glTexSubImage2D(GlobalUtil::_texTarget, 0, 0, 0, fw, fh, GL_RGBA, GL_FLOAT, &list[0]); - - if( fcount == _featureNum) _keypoint_index.resize(0); - } - if( GlobalUtil::_KeyPointListForceLevel0 ) break; - } - GLTexImage::UnbindTex(); - if(GlobalUtil::_verbose) - { - std::cout<<"#Features:\t"<<_featureNum<<"\n"; - } -} - - - -PyramidPacked::PyramidPacked(SiftParam& sp): PyramidGL(sp) -{ - _allPyramid = NULL; -} - -PyramidPacked::~PyramidPacked() -{ - DestroyPyramidData(); -} - - -//build the gaussian pyrmaid - -void PyramidPacked::BuildPyramid(GLTexInput * input) -{ - // - USE_TIMING(); - GLTexImage * tex, *tmp; - FilterProgram ** filter; - FrameBufferObject fbo; - - glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); - input->FitTexViewPort(); - - for (int i = _octave_min; i < _octave_min + _octave_num; i++) - { - tex = GetBaseLevel(i); - tmp = GetBaseLevel(i, DATA_DOG) + 2; //use this as a temperory texture - - - filter = ShaderMan::s_bag->f_gaussian_step; - - OCTAVE_START(); - - if( i == _octave_min ) - { - if(i < 0) TextureUpSample(tex, input, 1<<(-i-1)); - else TextureDownSample(tex, input, 1<<(i+1)); - ShaderMan::FilterInitialImage(tex, tmp); - }else - { - TextureDownSample(tex, GetLevelTexture(i-1, param._level_ds)); - ShaderMan::FilterSampledImage(tex, tmp); - } - LEVEL_FINISH(); - - for(int j = param._level_min + 1; j <= param._level_max ; j++, tex++, filter++) - { - // filtering - ShaderMan::FilterImage(*filter, tex+1, tex, tmp); - LEVEL_FINISH(); - } - - OCTAVE_FINISH(); - - } - if(GlobalUtil::_timingS) glFinish(); - UnloadProgram(); -} - -void PyramidPacked::ComputeGradient() -{ - - //first pass, compute dog, gradient, orientation - GLenum buffers[4] = { - GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT , - GL_COLOR_ATTACHMENT2_EXT, GL_COLOR_ATTACHMENT3_EXT - }; - - int i, j; - double ts, t1; - FrameBufferObject fbo; - - if(GlobalUtil::_timingS && GlobalUtil::_verbose)ts = CLOCK(); - - for(i = _octave_min; i < _octave_min + _octave_num; i++) - { - GLTexImage * gus = GetBaseLevel(i) + 1; - GLTexImage * dog = GetBaseLevel(i, DATA_DOG) + 1; - GLTexImage * grd = GetBaseLevel(i, DATA_GRAD) + 1; - GLTexImage * rot = GetBaseLevel(i, DATA_ROT) + 1; - glDrawBuffers(3, buffers); - gus->FitTexViewPort(); - //compute the gradient - for(j = 0; j < param._dog_level_num ; j++, gus++, dog++, grd++, rot++) - { - //gradient, dog, orientation - glActiveTexture(GL_TEXTURE0); - gus->BindTex(); - glActiveTexture(GL_TEXTURE1); - (gus-1)->BindTex(); - //output - dog->AttachToFBO(0); - grd->AttachToFBO(1); - rot->AttachToFBO(2); - ShaderMan::UseShaderGradientPass((gus-1)->GetTexID()); - //compute - dog->DrawQuadMT4(); - } - } - if(GlobalUtil::_timingS) - { - glFinish(); - if(GlobalUtil::_verbose) - { - t1 = CLOCK(); - std::cout <<"<Gradient, DOG >\t"<<(t1-ts)<<"\n"; - } - } - GLTexImage::DetachFBO(1); - GLTexImage::DetachFBO(2); - UnloadProgram(); - GLTexImage::UnbindMultiTex(3); - fbo.UnattachTex(GL_COLOR_ATTACHMENT1_EXT); -} - -void PyramidPacked::DetectKeypointsEX() -{ - - //first pass, compute dog, gradient, orientation - GLenum buffers[4] = { - GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT , - GL_COLOR_ATTACHMENT2_EXT, GL_COLOR_ATTACHMENT3_EXT - }; - - int i, j; - double t0, t, ts, t1, t2; - FrameBufferObject fbo; - - if(GlobalUtil::_timingS && GlobalUtil::_verbose)ts = CLOCK(); - - for(i = _octave_min; i < _octave_min + _octave_num; i++) - { - GLTexImage * gus = GetBaseLevel(i) + 1; - GLTexImage * dog = GetBaseLevel(i, DATA_DOG) + 1; - GLTexImage * grd = GetBaseLevel(i, DATA_GRAD) + 1; - GLTexImage * rot = GetBaseLevel(i, DATA_ROT) + 1; - glDrawBuffers(3, buffers); - gus->FitTexViewPort(); - //compute the gradient - for(j = param._level_min +1; j <= param._level_max ; j++, gus++, dog++, grd++, rot++) - { - //gradient, dog, orientation - glActiveTexture(GL_TEXTURE0); - gus->BindTex(); - glActiveTexture(GL_TEXTURE1); - (gus-1)->BindTex(); - //output - dog->AttachToFBO(0); - grd->AttachToFBO(1); - rot->AttachToFBO(2); - ShaderMan::UseShaderGradientPass((gus-1)->GetTexID()); - //compute - dog->DrawQuadMT4(); - } - } - if(GlobalUtil::_timingS && GlobalUtil::_verbose) - { - glFinish(); - t1 = CLOCK(); - } - GLTexImage::DetachFBO(1); - GLTexImage::DetachFBO(2); - //glDrawBuffers(1, buffers); - glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); - - - GlobalUtil::CheckErrorsGL(); - - for ( i = _octave_min; i < _octave_min + _octave_num; i++) - { - if(GlobalUtil::_timingO) - { - t0 = CLOCK(); - std::cout<<"#"<<(i + _down_sample_factor)<<"\t"; - } - GLTexImage * dog = GetBaseLevel(i, DATA_DOG) + 2; - GLTexImage * key = GetBaseLevel(i, DATA_KEYPOINT) +2; - GLTexImage * gus = GetBaseLevel(i) + 2; - key->FitTexViewPort(); - - for( j = param._level_min +2; j < param._level_max ; j++, dog++, key++, gus++) - { - if(GlobalUtil::_timingL)t = CLOCK(); - key->AttachToFBO(0); - glActiveTexture(GL_TEXTURE0); - dog->BindTex(); - glActiveTexture(GL_TEXTURE1); - (dog+1)->BindTex(); - glActiveTexture(GL_TEXTURE2); - (dog-1)->BindTex(); - if(GlobalUtil::_DarknessAdaption) - { - glActiveTexture(GL_TEXTURE3); - gus->BindTex(); - } - ShaderMan::UseShaderKeypoint((dog+1)->GetTexID(), (dog-1)->GetTexID()); - key->DrawQuadMT8(); - if(GlobalUtil::_timingL) - { - glFinish(); - std::cout<<(CLOCK()-t)<<"\t"; - } - } - if(GlobalUtil::_timingO) - { - glFinish(); - std::cout<<"|\t"<<(CLOCK()-t0)<<"\n"; - } - } - - if(GlobalUtil::_timingS) - { - glFinish(); - if(GlobalUtil::_verbose) - { - t2 = CLOCK(); - std::cout <<"<Gradient, DOG >\t"<<(t1-ts)<<"\n" - <<"<Get Keypoints >\t"<<(t2-t1)<<"\n"; - } - - } - UnloadProgram(); - GLTexImage::UnbindMultiTex(3); - fbo.UnattachTex(GL_COLOR_ATTACHMENT1_EXT); -} - - -void PyramidPacked::GenerateFeatureList(int i, int j) -{ - float fcount = 0.0f; - int hist_skip_gpu = GlobalUtil::_ListGenSkipGPU; - int idx = i * param._dog_level_num + j; - int hist_level_num = _hpLevelNum - _pyramid_octave_first; - GLTexImage * htex, * ftex, * tex; - htex = _histoPyramidTex + hist_level_num - 1 - i; - ftex = _featureTex + idx; - tex = GetBaseLevel(_octave_min + i, DATA_KEYPOINT) + 2 + j; - - - //fill zero to an extra row/col if the height/width is odd - glActiveTexture(GL_TEXTURE0); - tex->BindTex(); - htex->AttachToFBO(0); - int tight = (htex->GetImgWidth() * 4 == tex->GetImgWidth() -1 && htex->GetImgHeight() *4 == tex->GetImgHeight()-1 ); - ShaderMan::UseShaderGenListInit(tex->GetImgWidth(), tex->GetImgHeight(), tight); - htex->FitTexViewPort(); - //this uses the fact that no feature is on the edge. - htex->DrawQuadReduction(); - //reduction.. - htex--; - - //this part might have problems on several GPUS - //because the output of one pass is the input of the next pass - //may require glFinish to make it right, but too much glFinish makes it slow - for(int k = 0; k <hist_level_num - i-1 - hist_skip_gpu; k++, htex--) - { - htex->AttachToFBO(0); - htex->FitTexViewPort(); - (htex+1)->BindTex(); - ShaderMan::UseShaderGenListHisto(); - htex->DrawQuadReduction(); - } - - if(hist_skip_gpu == 0) - { - //read back one pixel - float fn[4]; - glReadPixels(0, 0, 1, 1, GL_RGBA , GL_FLOAT, fn); - fcount = (fn[0] + fn[1] + fn[2] + fn[3]); - if(fcount < 1) fcount = 0; - - _levelFeatureNum[ idx] = (int)(fcount); - SetLevelFeatureNum(idx, (int)fcount); - - //save number of features - _featureNum += int(fcount); - - // - if(fcount < 1.0) return;; - - - ///generate the feature texture - htex= _histoPyramidTex; - - htex->BindTex(); - - //first pass - ftex->AttachToFBO(0); - if(GlobalUtil::_MaxOrientation>1) - { - //this is very important... - ftex->FitRealTexViewPort(); - glClear(GL_COLOR_BUFFER_BIT); - glFinish(); - }else - { - ftex->FitTexViewPort(); - //glFinish(); - } - - - ShaderMan::UseShaderGenListStart((float)ftex->GetImgWidth(), htex->GetTexID()); - - ftex->DrawQuad(); - //make sure it finishes before the next step - ftex->DetachFBO(0); - //pass on each pyramid level - htex++; - }else - { - - int tw = htex[1].GetDrawWidth(), th = htex[1].GetDrawHeight(); - int fc = 0; - glReadPixels(0, 0, tw, th, GL_RGBA , GL_FLOAT, _histo_buffer); - _keypoint_buffer.resize(0); - for(int y = 0, pos = 0; y < th; y++) - { - for(int x= 0; x < tw; x++) - { - for(int c = 0; c < 4; c++, pos++) - { - int ss = (int) _histo_buffer[pos]; - if(ss == 0) continue; - float ft[4] = {2 * x + (c%2? 1.5f: 0.5f), 2 * y + (c>=2? 1.5f: 0.5f), 0, 1 }; - for(int t = 0; t < ss; t++) - { - ft[2] = (float) t; - _keypoint_buffer.insert(_keypoint_buffer.end(), ft, ft+4); - } - fc += (int)ss; - } - } - } - _levelFeatureNum[ idx] = fc; - SetLevelFeatureNum(idx, fc); - if(fc == 0) return; - - fcount = (float) fc; - _featureNum += fc; - ///////////////////// - ftex->AttachToFBO(0); - if(GlobalUtil::_MaxOrientation>1) - { - ftex->FitRealTexViewPort(); - glClear(GL_COLOR_BUFFER_BIT); - glFlush(); - }else - { - ftex->FitTexViewPort(); - glFlush(); - } - _keypoint_buffer.resize(ftex->GetDrawWidth() * ftex->GetDrawHeight()*4, 0); - /////////// - glActiveTexture(GL_TEXTURE0); - ftex->BindTex(); - glTexSubImage2D(GlobalUtil::_texTarget, 0, 0, 0, ftex->GetDrawWidth(), - ftex->GetDrawHeight(), GL_RGBA, GL_FLOAT, &_keypoint_buffer[0]); - htex += 2; - } - - for(int lev = 1 + hist_skip_gpu; lev < hist_level_num - i; lev++, htex++) - { - glActiveTexture(GL_TEXTURE0); - ftex->BindTex(); - ftex->AttachToFBO(0); - glActiveTexture(GL_TEXTURE1); - htex->BindTex(); - ShaderMan::UseShaderGenListStep(ftex->GetTexID(), htex->GetTexID()); - ftex->DrawQuad(); - ftex->DetachFBO(0); - } - - ftex->AttachToFBO(0); - glActiveTexture(GL_TEXTURE1); - tex->BindTex(); - ShaderMan::UseShaderGenListEnd(tex->GetTexID()); - ftex->DrawQuad(); - GLTexImage::UnbindMultiTex(2); - -} - -void PyramidPacked::GenerateFeatureList() -{ - //generate the histogram pyramid - FrameBufferObject fbo; - glReadBuffer(GL_COLOR_ATTACHMENT0_EXT); - glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); - double t1, t2; - int ocount= 0, reverse = (GlobalUtil::_TruncateMethod == 1); - _featureNum = 0; - - FitHistogramPyramid(); - //for(int i = 0, idx = 0; i < _octave_num; i++) - FOR_EACH_OCTAVE(i, reverse) - { - if(GlobalUtil::_timingO) - { - t1= CLOCK(); - ocount = 0; - std::cout<<"#"<<i+_octave_min + _down_sample_factor<<":\t"; - } - //for(int j = 0; j < param._dog_level_num; j++, idx++) - FOR_EACH_LEVEL(j, reverse) - { - if(GlobalUtil::_TruncateMethod && GlobalUtil::_FeatureCountThreshold > 0 - && _featureNum > GlobalUtil::_FeatureCountThreshold) - { - _levelFeatureNum[i * param._dog_level_num + j] = 0; - continue; - } - - GenerateFeatureList(i, j); - - if(GlobalUtil::_timingO) - { - int idx = i * param._dog_level_num + j; - ocount += _levelFeatureNum[idx]; - std::cout<< _levelFeatureNum[idx] <<"\t"; - } - } - if(GlobalUtil::_timingO) - { - t2 = CLOCK(); - std::cout << "| \t" << int(ocount) << " :\t(" << (t2 - t1) << ")\n"; - } - } - if(GlobalUtil::_timingS)glFinish(); - if(GlobalUtil::_verbose) - { - std::cout<<"#Features:\t"<<_featureNum<<"\n"; - } - -} - -void PyramidPacked::GenerateFeatureListCPU() -{ - FrameBufferObject fbo; - _featureNum = 0; - GLTexImage * tex = GetBaseLevel(_octave_min); - float * mem = new float [tex->GetTexWidth()*tex->GetTexHeight()*4]; - vector<float> list; - int idx = 0; - for(int i = 0; i < _octave_num; i++) - { - for(int j = 0; j < param._dog_level_num; j++, idx++) - { - tex = GetBaseLevel(_octave_min + i, DATA_KEYPOINT) + j + 2; - tex->BindTex(); - glGetTexImage(GlobalUtil::_texTarget, 0, GL_RGBA, GL_FLOAT, mem); - //tex->AttachToFBO(0); - //tex->FitTexViewPort(); - //glReadPixels(0, 0, tex->GetTexWidth(), tex->GetTexHeight(), GL_RED, GL_FLOAT, mem); - // - //make a list of - list.resize(0); - float *pl = mem; - int fcount = 0 ; - for(int k = 0; k < tex->GetDrawHeight(); k++) - { - float * p = pl; - pl += tex->GetTexWidth() * 4; - for( int m = 0; m < tex->GetDrawWidth(); m ++, p+=4) - { - // if(m ==0 || k ==0 || k == tex->GetDrawHeight() -1 || m == tex->GetDrawWidth() -1) continue; - // if(*p == 0) continue; - int t = ((int) fabs(p[0])) - 1; - if(t < 0) continue; - int xx = m + m + ( (t %2)? 1 : 0); - int yy = k + k + ( (t <2)? 0 : 1); - if(xx ==0 || yy == 0) continue; - if(xx >= tex->GetImgWidth() - 1 || yy >= tex->GetImgHeight() - 1)continue; - list.push_back(xx + 0.5f + p[1]); - list.push_back(yy + 0.5f + p[2]); - list.push_back(GlobalUtil::_KeepExtremumSign && p[0] < 0 ? -1.0f : 1.0f); - list.push_back(p[3]); - fcount ++; - } - } - if(fcount==0)continue; - - if(GlobalUtil::_timingL) std::cout<<fcount<<"."; - - GLTexImage * ftex = _featureTex+idx; - _levelFeatureNum[idx] = (fcount); - SetLevelFeatureNum(idx, fcount); - - _featureNum += (fcount); - - - int fw = ftex->GetImgWidth(); - int fh = ftex->GetImgHeight(); - - list.resize(4*fh*fw); - - ftex->BindTex(); - ftex->AttachToFBO(0); - // glTexImage2D(GlobalUtil::_texTarget, 0, GlobalUtil::_iTexFormat, fw, fh, 0, GL_BGRA, GL_FLOAT, &list[0]); - glTexSubImage2D(GlobalUtil::_texTarget, 0, 0, 0, fw, fh, GL_RGBA, GL_FLOAT, &list[0]); - // - } - } - GLTexImage::UnbindTex(); - delete[] mem; - if(GlobalUtil::_verbose) - { - std::cout<<"#Features:\t"<<_featureNum<<"\n"; - } -} - - - -void PyramidPacked::GetFeatureOrientations() -{ - GLTexImage * gtex, * otex; - GLTexImage * ftex = _featureTex; - GLTexImage * fotex = _orientationTex; - int * count = _levelFeatureNum; - float sigma, sigma_step = powf(2.0f, 1.0f/param._dog_level_num); - - - FrameBufferObject fbo; - if(_orientationTex) - { - GLenum buffers[] = { GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT }; - glDrawBuffers(2, buffers); - }else - { - glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); - } - - for(int i = 0; i < _octave_num; i++) - { - gtex = GetBaseLevel(i+_octave_min, DATA_GRAD) + 1; - otex = GetBaseLevel(i+_octave_min, DATA_ROT) + 1; - - for(int j = 0; j < param._dog_level_num; j++, ftex++, otex++, count++, gtex++, fotex++) - { - if(*count<=0)continue; - - sigma = param.GetLevelSigma(j+param._level_min+1); - - - ftex->FitTexViewPort(); - - glActiveTexture(GL_TEXTURE0); - ftex->BindTex(); - glActiveTexture(GL_TEXTURE1); - gtex->BindTex(); - glActiveTexture(GL_TEXTURE2); - otex->BindTex(); - // - ftex->AttachToFBO(0); - if(_orientationTex) fotex->AttachToFBO(1); - - GlobalUtil::CheckFramebufferStatus(); - - ShaderMan::UseShaderOrientation(gtex->GetTexID(), - gtex->GetImgWidth(), gtex->GetImgHeight(), - sigma, otex->GetTexID(), sigma_step, _existing_keypoints); - ftex->DrawQuad(); - } - } - - GLTexImage::UnbindMultiTex(3); - if(GlobalUtil::_timingS)glFinish(); - if(_orientationTex) fbo.UnattachTex(GL_COLOR_ATTACHMENT1_EXT); - -} - - -void PyramidPacked::GetSimplifiedOrientation() -{ - // - int idx = 0; -// int n = _octave_num * param._dog_level_num; - float sigma, sigma_step = powf(2.0f, 1.0f/param._dog_level_num); - GLTexImage * ftex = _featureTex; - - FrameBufferObject fbo; - glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); - for(int i = 0; i < _octave_num; i++) - { - GLTexImage *otex = GetBaseLevel(i + _octave_min, DATA_ROT) + 2; - for(int j = 0; j < param._dog_level_num; j++, ftex++, otex++, idx ++) - { - if(_levelFeatureNum[idx]<=0)continue; - sigma = param.GetLevelSigma(j+param._level_min+1); - // - ftex->AttachToFBO(0); - ftex->FitTexViewPort(); - - glActiveTexture(GL_TEXTURE0); - ftex->BindTex(); - glActiveTexture(GL_TEXTURE1); - otex->BindTex(); - - ShaderMan::UseShaderSimpleOrientation(otex->GetTexID(), sigma, sigma_step); - ftex->DrawQuad(); - } - } - GLTexImage::UnbindMultiTex(2); -} - -void PyramidPacked::InitPyramid(int w, int h, int ds) -{ - int wp, hp, toobig = 0; - if(ds == 0) - { - _down_sample_factor = 0; - if(GlobalUtil::_octave_min_default>=0) - { - wp = w >> GlobalUtil::_octave_min_default; - hp = h >> GlobalUtil::_octave_min_default; - }else - { - wp = w << (-GlobalUtil::_octave_min_default); - hp = h << (-GlobalUtil::_octave_min_default); - } - _octave_min = _octave_min_default; - }else - { - //must use 0 as _octave_min; - _octave_min = 0; - _down_sample_factor = ds; - w >>= ds; - h >>= ds; - wp = w; - hp = h; - } - - while(wp > GlobalUtil::_texMaxDim || hp > GlobalUtil::_texMaxDim ) - { - _octave_min ++; - wp >>= 1; - hp >>= 1; - toobig = 1; - } - - while(GlobalUtil::_MemCapGPU > 0 && GlobalUtil::_FitMemoryCap && (wp >_pyramid_width || hp > _pyramid_height) && - max(max(wp, hp), max(_pyramid_width, _pyramid_height)) > 1024 * sqrt(GlobalUtil::_MemCapGPU / 96.0) ) - { - _octave_min ++; - wp >>= 1; - hp >>= 1; - toobig = 2; - } - - if(toobig && GlobalUtil::_verbose) - { - std::cout<<(toobig == 2 ? "[**SKIP OCTAVES**]:\tExceeding Memory Cap (-nomc)\n" : - "[**SKIP OCTAVES**]:\tReaching the dimension limit (-maxd)!\n"); - } - - if( wp == _pyramid_width && hp == _pyramid_height && _allocated ) - { - FitPyramid(wp, hp); - }else if(GlobalUtil::_ForceTightPyramid || _allocated ==0) - { - ResizePyramid(wp, hp); - } - else if( wp > _pyramid_width || hp > _pyramid_height ) - { - ResizePyramid(max(wp, _pyramid_width), max(hp, _pyramid_height)); - if(wp < _pyramid_width || hp < _pyramid_height) FitPyramid(wp, hp); - } - else - { - //try use the pyramid allocated for large image on small input images - FitPyramid(wp, hp); - } - - //select the initial smoothing filter according to the new _octave_min - ShaderMan::SelectInitialSmoothingFilter(_octave_min + _down_sample_factor, param); -} - - - -void PyramidPacked::FitPyramid(int w, int h) -{ - //(w, h) <= (_pyramid_width, _pyramid_height); - - _pyramid_octave_first = 0; - // - _octave_num = GlobalUtil::_octave_num_default; - - int _octave_num_max = GetRequiredOctaveNum(min(w, h)); - - if(_octave_num < 1 || _octave_num > _octave_num_max) - { - _octave_num = _octave_num_max; - } - - - int pw = _pyramid_width>>1, ph = _pyramid_height>>1; - while(_pyramid_octave_first + _octave_num < _pyramid_octave_num && - pw >= w && ph >= h) - { - _pyramid_octave_first++; - pw >>= 1; - ph >>= 1; - } - - for(int i = 0; i < _octave_num; i++) - { - GLTexImage * tex = GetBaseLevel(i + _octave_min); - GLTexImage * dog = GetBaseLevel(i + _octave_min, DATA_DOG); - GLTexImage * grd = GetBaseLevel(i + _octave_min, DATA_GRAD); - GLTexImage * rot = GetBaseLevel(i + _octave_min, DATA_ROT); - GLTexImage * key = GetBaseLevel(i + _octave_min, DATA_KEYPOINT); - for(int j = param._level_min; j <= param._level_max; j++, tex++, dog++, grd++, rot++, key++) - { - tex->SetImageSize(w, h); - if(j == param._level_min) continue; - dog->SetImageSize(w, h); - grd->SetImageSize(w, h); - rot->SetImageSize(w, h); - if(j == param._level_min + 1 || j == param._level_max) continue; - key->SetImageSize(w, h); - } - w>>=1; - h>>=1; - } -} - - -void PyramidPacked::ResizePyramid( int w, int h) -{ - // - unsigned int totalkb = 0; - int _octave_num_new, input_sz, i, j; - // - - if(_pyramid_width == w && _pyramid_height == h && _allocated) return; - - if(w > GlobalUtil::_texMaxDim || h > GlobalUtil::_texMaxDim) return ; - - if(GlobalUtil::_verbose && GlobalUtil::_timingS) std::cout<<"[Allocate Pyramid]:\t" <<w<<"x"<<h<<endl; - //first octave does not change - _pyramid_octave_first = 0; - - - //compute # of octaves - - input_sz = min(w,h) ; - _pyramid_width = w; - _pyramid_height = h; - - //reset to preset parameters - _octave_num_new = GlobalUtil::_octave_num_default; - - if(_octave_num_new < 1) _octave_num_new = GetRequiredOctaveNum(input_sz) ; - - - if(_pyramid_octave_num != _octave_num_new) - { - //destroy the original pyramid if the # of octave changes - if(_octave_num >0) - { - DestroyPerLevelData(); - DestroyPyramidData(); - } - _pyramid_octave_num = _octave_num_new; - } - - _octave_num = _pyramid_octave_num; - - int noct = _octave_num; - int nlev = param._level_num; - - // //initialize the pyramid - if(_allPyramid==NULL) _allPyramid = new GLTexPacked[ noct* nlev * DATA_NUM]; - - - GLTexPacked * gus = (GLTexPacked *) GetBaseLevel(_octave_min, DATA_GAUSSIAN); - GLTexPacked * dog = (GLTexPacked *) GetBaseLevel(_octave_min, DATA_DOG); - GLTexPacked * grd = (GLTexPacked *) GetBaseLevel(_octave_min, DATA_GRAD); - GLTexPacked * rot = (GLTexPacked *) GetBaseLevel(_octave_min, DATA_ROT); - GLTexPacked * key = (GLTexPacked *) GetBaseLevel(_octave_min, DATA_KEYPOINT); - - - ////////////there could be "out of memory" happening during the allocation - - for(i = 0; i< noct; i++) - { - for( j = 0; j< nlev; j++, gus++, dog++, grd++, rot++, key++) - { - gus->InitTexture(w, h); - if(j==0)continue; - dog->InitTexture(w, h); - grd->InitTexture(w, h, 0); - rot->InitTexture(w, h); - if(j<=1 || j >=nlev -1) continue; - key->InitTexture(w, h, 0); - } - int tsz = (gus -1)->GetTexPixelCount() * 16; - totalkb += ((nlev *5 -6)* tsz / 1024); - //several auxilary textures are not actually required - w>>=1; - h>>=1; - } - - totalkb += ResizeFeatureStorage(); - - _allocated = 1; - - if(GlobalUtil::_verbose && GlobalUtil::_timingS) std::cout<<"[Allocate Pyramid]:\t" <<(totalkb/1024)<<"MB\n"; - -} - -void PyramidPacked::DestroyPyramidData() -{ - if(_allPyramid) - { - delete [] _allPyramid; - _allPyramid = NULL; - } -} - - -GLTexImage* PyramidPacked::GetLevelTexture(int octave, int level, int dataName) -{ - return _allPyramid+ (_pyramid_octave_first + octave - _octave_min) * param._level_num - + param._level_num * _pyramid_octave_num * dataName - + (level - param._level_min); - -} - -GLTexImage* PyramidPacked::GetLevelTexture(int octave, int level) -{ - return _allPyramid+ (_pyramid_octave_first + octave - _octave_min) * param._level_num - + (level - param._level_min); -} - -//in the packed implementation( still in progress) -// DATA_GAUSSIAN, DATA_DOG, DATA_GAD will be stored in different textures. - -GLTexImage* PyramidPacked::GetBaseLevel(int octave, int dataName) -{ - if(octave <_octave_min || octave > _octave_min + _octave_num) return NULL; - int offset = (_pyramid_octave_first + octave - _octave_min) * param._level_num; - int num = param._level_num * _pyramid_octave_num; - return _allPyramid + num *dataName + offset; -} - - -void PyramidPacked::FitHistogramPyramid() -{ - GLTexImage * tex, *htex; - int hist_level_num = _hpLevelNum - _pyramid_octave_first; - - tex = GetBaseLevel(_octave_min , DATA_KEYPOINT) + 2; - htex = _histoPyramidTex + hist_level_num - 1; - int w = (tex->GetImgWidth() + 2) >> 2; - int h = (tex->GetImgHeight() + 2)>> 2; - - - //4n+1 -> n; 4n+2,2, 3 -> n+1 - for(int k = 0; k <hist_level_num -1; k++, htex--) - { - if(htex->GetImgHeight()!= h || htex->GetImgWidth() != w) - { - htex->SetImageSize(w, h); - htex->ZeroHistoMargin(); - } - - w = (w + 1)>>1; h = (h + 1) >> 1; - } -} - diff --git a/3rdparty/SiftGPU/src/SiftGPU/PyramidGL.h b/3rdparty/SiftGPU/src/SiftGPU/PyramidGL.h deleted file mode 100644 index a5baafc2..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/PyramidGL.h +++ /dev/null @@ -1,119 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: PyramidGL.h -// Author: Changchang Wu -// Description : interface for the PyramdGL -// class PyramidNaive and PyramidPacked are derived from PyramidGL -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - - - -#ifndef _PYRAMID_GL_H -#define _PYRAMID_GL_H - -class GLTexImage; -class SiftParam; -class ProgramGPU; -class ShaderMan; -class GlobalUtil; -class SiftPyramid; - -class PyramidGL:public SiftPyramid -{ -protected: - GLTexImage* _histoPyramidTex; - GLTexImage* _featureTex; - GLTexImage* _descriptorTex; - GLTexImage* _orientationTex; -public: - void InitializeContext(); - void SetLevelFeatureNum(int idx, int num); - void GetTextureStorageSize(int num, int &fw, int& fh); - void GetAlignedStorageSize(int num, int align, int &fw, int &fh); - static void InterlaceDescriptorF2(int w, int h, float* buf, float* pd, int step); - static void NormalizeDescriptor(int num, float*pd); - virtual void DownloadKeypoints(); - virtual int ResizeFeatureStorage(); - //////////////////////////// - virtual void DestroyPerLevelData(); - virtual void DestroySharedData(); - virtual void GetFeatureDescriptors(); - virtual void GenerateFeatureListTex(); - virtual void ReshapeFeatureListCPU(); - virtual void GenerateFeatureDisplayVBO(); - virtual void CleanUpAfterSIFT(); - virtual GLTexImage* GetBaseLevel(int octave, int dataName = DATA_GAUSSIAN)=0; -public: - PyramidGL(SiftParam&sp); - virtual ~PyramidGL(); -}; - -class PyramidNaive:public PyramidGL, public ShaderMan -{ -protected: - GLTexImage * _texPyramid; - GLTexImage * _auxPyramid; -public: - void DestroyPyramidData(); - void GetSimplifiedOrientation(); - void GenerateFeatureListCPU(); - virtual void GetFeatureOrientations(); - virtual void GenerateFeatureList(); - void DetectKeypointsEX(); - void ComputeGradient(); - GLTexImage* GetLevelTexture(int octave, int level); - GLTexImage* GetBaseLevel(int octave, int dataName = DATA_GAUSSIAN); - GLTexImage* GetLevelTexture(int octave, int level, int dataName); - void BuildPyramid(GLTexInput * input); - void InitPyramid(int w, int h, int ds); - void FitPyramid(int w, int h); - void ResizePyramid(int w, int h); - void FitHistogramPyramid(); - PyramidNaive(SiftParam & sp); - ~PyramidNaive(); -private: - void GenerateFeatureList(int i, int j); -}; - - -class PyramidPacked:public PyramidGL, public ShaderMan -{ - GLTexPacked * _allPyramid; -public: - PyramidPacked(SiftParam& sp); - ~PyramidPacked(); - void DestroyPyramidData(); - void DetectKeypointsEX(); - void ComputeGradient(); - void BuildPyramid(GLTexInput * input); - void InitPyramid(int w, int h, int ds); - void FitPyramid(int w, int h); - void ResizePyramid(int w, int h); - void FitHistogramPyramid(); - void GenerateFeatureListCPU(); - void GenerateFeatureList(); - void GetSimplifiedOrientation(); - void GetFeatureOrientations(); - GLTexImage* GetBaseLevel(int octave, int dataName = DATA_GAUSSIAN); - GLTexImage* GetLevelTexture(int octave, int level); - GLTexImage* GetLevelTexture(int octave, int level, int dataName); - virtual int IsUsingRectDescription(){return _existing_keypoints & SIFT_RECT_DESCRIPTION; } -private: - void GenerateFeatureList(int i, int j); -}; - -#endif diff --git a/3rdparty/SiftGPU/src/SiftGPU/ShaderMan.cpp b/3rdparty/SiftGPU/src/SiftGPU/ShaderMan.cpp deleted file mode 100644 index bddc0b2e..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/ShaderMan.cpp +++ /dev/null @@ -1,349 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: ShaderMan.cpp -// Author: Changchang Wu -// Description : implementation of the ShaderMan class. -// A Shader Manager that calls different implementation of shaders -// -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - - -#include "GL/glew.h" -#include <vector> -#include <iostream> -#include <stdlib.h> -#include <math.h> -using std::vector; -using std::ostream; -using std::endl; - - -#include "ProgramGLSL.h" -#include "GlobalUtil.h" -#include "GLTexImage.h" -#include "SiftGPU.h" -#include "ShaderMan.h" - -/// -ShaderBag * ShaderMan::s_bag = NULL; - -////////////////////////////////////////////////////////////////////// -// Construction/Destruction -////////////////////////////////////////////////////////////////////// - -void ShaderMan::InitShaderMan(SiftParam&param) -{ - if(s_bag) return; - - if(GlobalUtil::_usePackedTex ) s_bag = new ShaderBagPKSL; - else s_bag =new ShaderBagGLSL; - - GlobalUtil::StartTimer("Load Programs"); - s_bag->LoadFixedShaders(); - s_bag->LoadDynamicShaders(param); - if(GlobalUtil::_UseSiftGPUEX) s_bag->LoadDisplayShaders(); - GlobalUtil::StopTimer(); - - GlobalUtil::CheckErrorsGL("InitShaderMan"); -} - - -void ShaderMan::DestroyShaders() -{ - if(s_bag) delete s_bag; - s_bag = NULL; -} - -void ShaderMan::UnloadProgram() -{ - if(s_bag) s_bag->UnloadProgram(); -} - -void ShaderMan::FilterImage(FilterProgram* filter, GLTexImage *dst, GLTexImage *src, GLTexImage*tmp) -{ - if(filter == NULL) return; - - ////////////////////////////// - src->FillMargin(filter->_size, 0); - - //output parameter - if(tmp) tmp->AttachToFBO(0); - else dst->AttachToFBO(0); - - - //input parameter - src->BindTex(); - dst->FitTexViewPort(); - - //horizontal filter - filter->s_shader_h->UseProgram(); - dst->DrawQuad(); - - //parameters - if(tmp) - { - // fill margin for out-of-boundary lookup - tmp->DetachFBO(0); - tmp->AttachToFBO(0); - tmp->FillMargin(0, filter->_size); - tmp->DetachFBO(0); - dst->AttachToFBO(0); - tmp->BindTex(); - } - else - { - glFinish(); - // fill margin for out-of-boundary lookup - dst->FillMargin(0, filter->_size); - dst->BindTex(); - } - - //vertical filter - filter->s_shader_v->UseProgram(); - dst->DrawQuad(); - - - //clean up - dst->UnbindTex(); - dst->DetachFBO(0); - - // - ShaderMan::UnloadProgram(); -} - - -void ShaderMan::FilterInitialImage(GLTexImage* tex, GLTexImage* buf) -{ - if(s_bag->f_gaussian_skip0) FilterImage(s_bag->f_gaussian_skip0, tex, tex, buf); -} - -void ShaderMan::FilterSampledImage(GLTexImage* tex, GLTexImage* buf) -{ - if(s_bag->f_gaussian_skip1) FilterImage(s_bag->f_gaussian_skip1, tex, tex, buf); -} - -void ShaderMan::TextureCopy(GLTexImage*dst, GLTexImage*src) -{ - - dst->AttachToFBO(0); - - src->BindTex(); - - dst->FitTexViewPort(); - - dst->DrawQuad(); - - dst->UnbindTex(); -// ShaderMan::UnloadProgram(); - dst->DetachFBO(0); - return; -} -void ShaderMan::TextureDownSample(GLTexImage *dst, GLTexImage *src, int scale) -{ - //output parameter - - dst->AttachToFBO(0); - - //input parameter - src->BindTex(); - - // - dst->FitTexViewPort(); - - s_bag->s_sampling->UseProgram(); - - dst->DrawQuadDS(scale); - src->UnbindTex(); - - UnloadProgram(); - - dst->DetachFBO(0); -} - -void ShaderMan::TextureUpSample(GLTexImage *dst, GLTexImage *src, int scale) -{ - - //output parameter - dst->AttachToFBO(0); - //input parameter - src->BindTex(); - - dst->FitTexViewPort(); - - GlobalUtil::SetTextureParameterUS(); - - if(GlobalUtil::_usePackedTex) - { - s_bag->s_sampling->UseProgram(); - } - - dst->DrawQuadUS(scale); - src->UnbindTex(); - - UnloadProgram(); - - dst->DetachFBO(0); - - GlobalUtil::SetTextureParameter(); -} - - - -void ShaderMan::UseShaderDisplayGaussian() -{ - if(s_bag && s_bag->s_display_gaussian) s_bag->s_display_gaussian->UseProgram(); -} - -void ShaderMan::UseShaderDisplayDOG() -{ - if(s_bag && s_bag->s_display_dog) s_bag->s_display_dog->UseProgram(); -} - - - -void ShaderMan::UseShaderRGB2Gray() -{ - if(s_bag && s_bag->s_gray)s_bag->s_gray->UseProgram(); -} - - -void ShaderMan::UseShaderDisplayGrad() -{ - if(s_bag && s_bag->s_display_grad) s_bag->s_display_grad->UseProgram(); -} - - -void ShaderMan::UseShaderDisplayKeypoints() -{ - if(s_bag && s_bag->s_display_keys) s_bag->s_display_keys->UseProgram(); -} - - - - - -void ShaderMan::UseShaderGradientPass(int texP) -{ - s_bag->s_grad_pass->UseProgram(); - s_bag->SetGradPassParam(texP); -} - - -void ShaderMan::UseShaderKeypoint(int texU, int texD) -{ - s_bag->s_keypoint->UseProgram(); - s_bag->SetDogTexParam(texU, texD); -} - - - -void ShaderMan::UseShaderGenListInit(int w, int h, int tight) -{ - if(tight) - { - s_bag->s_genlist_init_tight->UseProgram(); - }else - { - s_bag->s_genlist_init_ex->UseProgram(); - s_bag->SetGenListInitParam(w, h); - } -} - -void ShaderMan::UseShaderGenListHisto() -{ - s_bag->s_genlist_histo->UseProgram(); - -} - - - - -void ShaderMan::UseShaderGenListStart(float fw, int tex0) -{ - s_bag->s_genlist_start->UseProgram(); - s_bag->SetGenListStartParam(fw, tex0); -} - -void ShaderMan::UseShaderGenListStep(int tex, int tex0) -{ - s_bag->s_genlist_step->UseProgram(); - s_bag->SetGenListStepParam( tex, tex0); -} - -void ShaderMan::UseShaderGenListEnd(int ktex) -{ - s_bag->s_genlist_end->UseProgram(); - s_bag->SetGenListEndParam(ktex); -} - -void ShaderMan::UseShaderDebug() -{ - if(s_bag->s_debug) s_bag->s_debug->UseProgram(); -} - -void ShaderMan::UseShaderZeroPass() -{ - if(s_bag->s_zero_pass) s_bag->s_zero_pass->UseProgram(); -} - -void ShaderMan::UseShaderGenVBO( float width, float fwidth, float size) -{ - s_bag->s_vertex_list->UseProgram(); - s_bag->SetGenVBOParam(width, fwidth, size); -} -void ShaderMan::UseShaderMarginCopy(int xmax, int ymax) -{ - s_bag->s_margin_copy->UseProgram(); - s_bag->SetMarginCopyParam(xmax, ymax); - -} -void ShaderMan::UseShaderCopyKeypoint() -{ - s_bag->s_copy_key->UseProgram(); -} - -void ShaderMan::UseShaderSimpleOrientation(int oTex, float sigma, float sigma_step) -{ - s_bag->s_orientation->UseProgram(); - s_bag->SetSimpleOrientationInput(oTex, sigma, sigma_step); -} - - - -void ShaderMan::UseShaderOrientation(int gtex, int width, int height, float sigma, int auxtex, float step, int keypoint_list) -{ - s_bag->s_orientation->UseProgram(); - - //changes in v345. - //set sigma to 0 to identify keypoit list mode - //set sigma to negative to identify fixed_orientation - if(keypoint_list) sigma = 0.0f; - else if(GlobalUtil::_FixedOrientation) sigma = - sigma; - - s_bag->SetFeatureOrientationParam(gtex, width, height, sigma, auxtex, step); -} - -void ShaderMan::UseShaderDescriptor(int gtex, int otex, int dwidth, int fwidth, int width, int height, float sigma) -{ - s_bag->s_descriptor_fp->UseProgram(); - s_bag->SetFeatureDescirptorParam(gtex, otex, (float)dwidth, (float)fwidth, (float)width, (float)height, sigma); -} - -void ShaderMan::SelectInitialSmoothingFilter(int octave_min, SiftParam&param) -{ - s_bag->SelectInitialSmoothingFilter(octave_min, param); -} diff --git a/3rdparty/SiftGPU/src/SiftGPU/ShaderMan.h b/3rdparty/SiftGPU/src/SiftGPU/ShaderMan.h deleted file mode 100644 index b5e49a9a..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/ShaderMan.h +++ /dev/null @@ -1,80 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: ShaderMan.h -// Author: Changchang Wu -// Description : interface for the ShaderMan class. -// This is a class that manages all the shaders for SIFT -// -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - - - -#ifndef _SIFT_SHADER_MAN_H -#define _SIFT_SHADER_MAN_H - - -#include "ProgramGPU.h" -#include "ProgramGLSL.h" -/////////////////////////////////////////////////////////////////// -//class ShaderMan -//description: pure static class -// wrapper of shaders from different GPU languages -/////////////////////////////////////////////////////////////////// -class SiftParam; -class FilterGLSL; - -class ShaderMan -{ -public: - static ShaderBag* s_bag; -public: - static void SelectInitialSmoothingFilter(int octave_min, SiftParam&param); - static void UseShaderMarginCopy(int xmax, int ymax); - static void UseShaderOrientation(int gtex, int width, int height, float sigma, int auxtex, float step, int keypoint_list); - static void UseShaderDescriptor(int gtex, int otex, int dwidth, int fwidth, int width, int height, float sigma); - static void UseShaderSimpleOrientation(int oTex, float sigma, float sigma_step); - static void UseShaderCopyKeypoint(); - static void UseShaderGenVBO( float width, float fwidth, float size); - static void UseShaderDebug(); - static void UseShaderZeroPass(); - static void UseShaderGenListStart(float fw, int tex0); - static void UseShaderGenListStep(int tex, int tex0); - static void UseShaderGenListEnd(int ktex); - static void UseShaderGenListHisto(); - static void UseShaderGenListInit(int w, int h, int tight = 1); - static void UseShaderKeypoint(int texU, int texD); - static void UseShaderGradientPass(int texP = 0); - static void UseShaderDisplayKeypoints(); - static void UseShaderDisplayGrad(); - static void UseShaderRGB2Gray(); - static void UseShaderDisplayDOG(); - static void UseShaderDisplayGaussian(); - /////////////////////////////////////////// - static void FilterInitialImage(GLTexImage* tex, GLTexImage* buf); - static void FilterSampledImage(GLTexImage* tex, GLTexImage* buf); - static void FilterImage(FilterProgram* filter, GLTexImage *dst, GLTexImage *src, GLTexImage*tmp); - static void TextureCopy(GLTexImage*dst, GLTexImage*src); - static void TextureDownSample(GLTexImage* dst, GLTexImage*src, int scale = 2); - static void TextureUpSample(GLTexImage* dst, GLTexImage*src, int scale); - /////////////////////////////////////////////// - static void InitShaderMan(SiftParam&param); - static void DestroyShaders(); - static int HaveShaderMan(){return s_bag != NULL;} - static void UnloadProgram(); -}; - -#endif diff --git a/3rdparty/SiftGPU/src/SiftGPU/SiftGPU.cpp b/3rdparty/SiftGPU/src/SiftGPU/SiftGPU.cpp deleted file mode 100644 index 3827a6d1..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/SiftGPU.cpp +++ /dev/null @@ -1,1440 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: SiftGPU.cpp -// Author: Changchang Wu -// Description : Implementation of the SIFTGPU classes. -// SiftGPU: The SiftGPU Tool. -// SiftGPUEX: SiftGPU + viewer -// SiftParam: Sift Parameters -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - - -#include "GL/glew.h" -#include <iostream> -#include <fstream> -#include <string> -#include <iomanip> -#include <vector> -#include <algorithm> -#include <math.h> - -#include <time.h> -using namespace std; - - -#include "GlobalUtil.h" -#include "SiftGPU.h" -#include "GLTexImage.h" -#include "ShaderMan.h" -#include "FrameBufferObject.h" -#include "SiftPyramid.h" -#include "PyramidGL.h" - -//CUDA works only with vc8 or higher -#if defined(CUDA_SIFTGPU_ENABLED) -#include "PyramidCU.h" -#endif - -#if defined(CL_SIFTGPU_ENABLED) -#include "PyramidCL.h" -#endif - - -//// -#if defined(_WIN32) - #include "direct.h" - #pragma warning (disable : 4786) - #pragma warning (disable : 4996) -#else - //compatible with linux - #define _stricmp strcasecmp - #include <stdlib.h> - #include <string.h> - #include <unistd.h> -#endif - -#if !defined(_MAX_PATH) - #if defined (PATH_MAX) - #define _MAX_PATH PATH_MAX - #else - #define _MAX_PATH 512 - #endif -#endif - -////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////// -// -//just want to make this class invisible -class ImageList:public std::vector<std::string> {}; - -SiftGPU::SiftGPU(int np) -{ - _texImage = new GLTexInput; - _imgpath = new char[_MAX_PATH]; - _outpath = new char[_MAX_PATH]; - _imgpath[0] = _outpath[0] = 0; - _initialized = 0; - _image_loaded = 0; - _current = 0; - _list = new ImageList(); - _pyramid = NULL; -} - - - -SiftGPUEX::SiftGPUEX() -{ - _view = _sub_view = 0; - _view_debug = 0; - GlobalUtil::_UseSiftGPUEX = 1; - srand((unsigned int)time(NULL)); - RandomizeColor(); -} - -void* SiftGPU::operator new (size_t size){ - void * p = malloc(size); - if (p == 0) - { - const std::bad_alloc ba; - throw ba; - } - return p; -} - - -void SiftGPUEX::RandomizeColor() -{ - float hsv[3] = {0, 0.8f, 1.0f}; - for(int i = 0; i < COLOR_NUM*3; i+=3) - { - hsv[0] = (rand()%100)*0.01f; //i/float(COLOR_NUM); - HSVtoRGB(hsv, _colors+i); - } -} - -SiftGPU::~SiftGPU() -{ - if(_pyramid) delete _pyramid; - delete _texImage; - delete _list; - delete[] _imgpath; - delete[] _outpath; -} - - -inline void SiftGPU::InitSiftGPU() -{ - if(_initialized || GlobalUtil::_GoodOpenGL ==0) return; - - //Parse sift parameters - ParseSiftParam(); - -#if !defined(CUDA_SIFTGPU_ENABLED) - if(GlobalUtil::_UseCUDA) - { - GlobalUtil::_UseCUDA = 0; - std::cerr << "---------------------------------------------------------------------------\n" - << "CUDA not supported in this binary! To enable it, please use SiftGPU_CUDA_Enable\n" - << "solution for VS2005+ or set siftgpu_enable_cuda to 1 in makefile\n" - << "----------------------------------------------------------------------------\n"; - } -#else - if(GlobalUtil::_UseCUDA == 0 && GlobalUtil::_UseOpenCL == 0) - { - GlobalUtil::InitGLParam(0); - } - if(GlobalUtil::_GoodOpenGL == 0) - { - GlobalUtil::_UseCUDA = 1; - std::cerr << "Switch from OpenGL to CUDA\n"; - } - - if(GlobalUtil::_UseCUDA && !PyramidCU::CheckCudaDevice(GlobalUtil::_DeviceIndex)) - { - std::cerr << "Switch from CUDA to OpenGL\n"; - GlobalUtil::_UseCUDA = 0; - } -#endif - - if(GlobalUtil::_verbose) std::cout <<"\n[SiftGPU Language]:\t" - << (GlobalUtil::_UseCUDA? "CUDA" : - (GlobalUtil::_UseOpenCL? "OpenCL" : "GLSL")) <<"\n"; - -#if defined(CUDA_SIFTGPU_ENABLED) - if(GlobalUtil::_UseCUDA) - _pyramid = new PyramidCU(*this); - else -#endif -#if defined(CL_SIFTGPU_ENABLED) - if(GlobalUtil::_UseOpenCL) - _pyramid = new PyramidCL(*this); - else -#endif - if(GlobalUtil::_usePackedTex) - _pyramid = new PyramidPacked(*this); - else - _pyramid = new PyramidNaive(*this); - - - if(GlobalUtil::_GoodOpenGL && GlobalUtil::_InitPyramidWidth > 0 && GlobalUtil::_InitPyramidHeight > 0) - { - GlobalUtil::StartTimer("Initialize Pyramids"); - _pyramid->InitPyramid(GlobalUtil::_InitPyramidWidth, GlobalUtil::_InitPyramidHeight, 0); - GlobalUtil::StopTimer(); - } - - ClockTimer::InitHighResolution(); - _initialized = 1; -} - -int SiftGPU::RunSIFT(int index) -{ - if(_list->size()>0 ) - { - index = index % _list->size(); - if(strcmp(_imgpath, _list->at(index).data())) - { - strcpy(_imgpath, _list->at(index).data()); - _image_loaded = 0; - _current = index; - } - return RunSIFT(); - }else - { - return 0; - } - -} - -int SiftGPU::RunSIFT( int width, int height, const void * data, unsigned int gl_format, unsigned int gl_type) -{ - - if(GlobalUtil::_GoodOpenGL ==0 ) return 0; - if(!_initialized) InitSiftGPU(); - else GlobalUtil::SetGLParam(); - if(GlobalUtil::_GoodOpenGL ==0 ) return 0; - - if(width > 0 && height >0 && data != NULL) - { - _imgpath[0] = 0; - //try downsample the image on CPU - GlobalUtil::StartTimer("Upload Image data"); - if(_texImage->SetImageData(width, height, data, gl_format, gl_type)) - { - _image_loaded = 2; //gldata; - GlobalUtil::StopTimer(); - _timing[0] = GlobalUtil::GetElapsedTime(); - - //if the size of image is different, the pyramid need to be reallocated. - GlobalUtil::StartTimer("Initialize Pyramid"); - _pyramid->InitPyramid(width, height, _texImage->_down_sampled); - GlobalUtil::StopTimer(); - _timing[1] = GlobalUtil::GetElapsedTime(); - - return RunSIFT(); - }else - { - return 0; - } - }else - { - return 0; - } - -} - -int SiftGPU::RunSIFT(const char * imgpath) -{ - if(imgpath && imgpath[0]) - { - //set the new image - strcpy(_imgpath, imgpath); - _image_loaded = 0; - return RunSIFT(); - }else - { - return 0; - } - - -} - -int SiftGPU::RunSIFT(int num, const SiftKeypoint * keys, int keys_have_orientation) -{ - if(num <=0) return 0; - _pyramid->SetKeypointList(num, (const float*) keys, 1, keys_have_orientation); - return RunSIFT(); -} - -int SiftGPU::RunSIFT() -{ - //check image data - if(_imgpath[0]==0 && _image_loaded == 0) return 0; - - //check OpenGL support - if(GlobalUtil::_GoodOpenGL ==0 ) return 0; - - ClockTimer timer; - - if(!_initialized) - { - //initialize SIFT GPU for once - InitSiftGPU(); - if(GlobalUtil::_GoodOpenGL ==0 ) return 0; - }else - { - //in case some OpenGL parameters are changed by users - GlobalUtil::SetGLParam(); - } - - timer.StartTimer("RUN SIFT"); - //process input image file - if( _image_loaded ==0) - { - int width, height; - //load and try down-sample on cpu - GlobalUtil::StartTimer("Load Input Image"); - if(!_texImage->LoadImageFile(_imgpath, width, height)) return 0; - _image_loaded = 1; - GlobalUtil::StopTimer(); - _timing[0] = GlobalUtil::GetElapsedTime(); - - //make sure the pyrmid can hold the new image. - GlobalUtil::StartTimer("Initialize Pyramid"); - _pyramid->InitPyramid(width, height, _texImage->_down_sampled); - GlobalUtil::StopTimer(); - _timing[1] = GlobalUtil::GetElapsedTime(); - - }else - { - //change some global states - if(!GlobalUtil::_UseCUDA && !GlobalUtil::_UseOpenCL) - { - GlobalUtil::FitViewPort(1,1); - _texImage->FitTexViewPort(); - } - if(_image_loaded == 1) - { - _timing[0] = _timing[1] = 0; - }else - {//2 - _image_loaded = 1; - } - } - - if(_pyramid->_allocated ==0 ) return 0; - - -#ifdef DEBUG_SIFTGPU - _pyramid->BeginDEBUG(_imgpath); -#endif - - //process the image - _pyramid->RunSIFT(_texImage); - - //read back the timing - _pyramid->GetPyramidTiming(_timing + 2); - - //write output once if there is only one input - if(_outpath[0] ){ SaveSIFT(_outpath); _outpath[0] = 0;} - - //terminate the process when -exit is provided. - if(GlobalUtil::_ExitAfterSIFT && GlobalUtil::_UseSiftGPUEX) exit(0); - - timer.StopTimer(); - if(GlobalUtil::_verbose)std::cout<<endl; - - return _pyramid->GetSucessStatus(); -} - - -void SiftGPU::SetKeypointList(int num, const SiftKeypoint * keys, int keys_have_orientation) -{ - _pyramid->SetKeypointList(num, (const float*)keys, 0, keys_have_orientation); -} - -void SiftGPUEX::DisplayInput() -{ - if(_texImage==NULL) return; - _texImage->VerifyTexture(); - _texImage->BindTex(); - _texImage->DrawImage(); - _texImage->UnbindTex(); - -} - -void SiftGPU::SetVerbose(int verbose) -{ - GlobalUtil::_timingO = verbose>2; - GlobalUtil::_timingL = verbose>3; - if(verbose == -1) - { - //Loop between verbose level 0, 1, 2 - if(GlobalUtil::_verbose) - { - GlobalUtil::_verbose = GlobalUtil::_timingS; - GlobalUtil::_timingS = 0; - if(GlobalUtil::_verbose ==0 && GlobalUtil::_UseSiftGPUEX) - std::cout << "Console ouput disabled, press Q/V to enable\n\n"; - }else - { - GlobalUtil::_verbose = 1; - GlobalUtil::_timingS = 1; - } - }else if(verbose == -2) - { - //trick for disabling all output (still keeps the timing level) - GlobalUtil::_verbose = 0; - GlobalUtil::_timingS = 1; - }else - { - GlobalUtil::_verbose = verbose>0; - GlobalUtil::_timingS = verbose>1; - } -} - - -SiftParam::SiftParam() -{ - - _level_min = -1; - _dog_level_num = 3; - _level_max = 0; - _sigma0 = 0; - _sigman = 0; - _edge_threshold = 0; - _dog_threshold = 0; - - -} - -float SiftParam::GetInitialSmoothSigma(int octave_min) -{ - float sa = _sigma0 * powf(2.0f, float(_level_min)/float(_dog_level_num)) ; - float sb = _sigman / powf(2.0f, float(octave_min)) ;// - float sigma_skip0 = sa > sb + 0.001?sqrt(sa*sa - sb*sb): 0.0f; - return sigma_skip0; -} - -void SiftParam::ParseSiftParam() -{ - - if(_dog_level_num ==0) _dog_level_num = 3; - if(_level_max ==0) _level_max = _dog_level_num + 1; - if(_sigma0 ==0.0f) _sigma0 = 1.6f * powf(2.0f, 1.0f / _dog_level_num) ; - if(_sigman == 0.0f) _sigman = 0.5f; - - - _level_num = _level_max -_level_min + 1; - - _level_ds = _level_min + _dog_level_num; - if(_level_ds > _level_max ) _level_ds = _level_max ; - - /// - float _sigmak = powf(2.0f, 1.0f / _dog_level_num) ; - float dsigma0 = _sigma0 * sqrt (1.0f - 1.0f / (_sigmak*_sigmak) ) ; - float sa, sb; - - - sa = _sigma0 * powf(_sigmak, (float)_level_min) ; - sb = _sigman / powf(2.0f, (float)GlobalUtil::_octave_min_default) ;// - - _sigma_skip0 = sa>sb+ 0.001?sqrt(sa*sa - sb*sb): 0.0f; - - sa = _sigma0 * powf(_sigmak, float(_level_min )) ; - sb = _sigma0 * powf(_sigmak, float(_level_ds - _dog_level_num)) ; - - _sigma_skip1 = sa>sb + 0.001? sqrt(sa*sa - sb*sb): 0.0f; - - _sigma_num = _level_max - _level_min; - _sigma = new float[_sigma_num]; - - for(int i = _level_min + 1; i <= _level_max; i++) - { - _sigma[i-_level_min -1] = dsigma0 * powf(_sigmak, float(i)) ; - } - - if(_dog_threshold ==0) _dog_threshold = 0.02f / _dog_level_num ; - if(_edge_threshold==0) _edge_threshold = 10.0f; -} - - -void SiftGPUEX::DisplayOctave(void (*UseDisplayShader)(), int i) -{ - if(_pyramid == NULL)return; - const int grid_sz = (int)ceil(_level_num/2.0); - double scale = 1.0/grid_sz ; - int gx=0, gy=0, dx, dy; - - if(_pyramid->_octave_min >0) scale *= (1<<_pyramid->_octave_min); - else if(_pyramid->_octave_min < 0) scale /= (1<<(-_pyramid->_octave_min)); - - - i = i% _pyramid->_octave_num; // - if(i<0 ) i+= _pyramid->_octave_num; - - scale *= ( 1<<(i)); - - - - - UseDisplayShader(); - - glPushMatrix(); - glScaled(scale, scale, scale); - for(int level = _level_min; level<= _level_max; level++) - { - GLTexImage * tex = _pyramid->GetLevelTexture(i+_pyramid->_octave_min, level); - - dx = tex->GetImgWidth(); - dy = tex->GetImgHeight(); - - glPushMatrix(); - - glTranslated(dx*gx, dy*gy, 0); - - tex->BindTex(); - - tex->DrawImage(); - tex->UnbindTex(); - - glPopMatrix(); - - gx++; - if(gx>=grid_sz) - { - gx =0; - gy++; - } - - } - - glPopMatrix(); - ShaderMan::UnloadProgram(); -} - -void SiftGPUEX::DisplayPyramid( void (*UseDisplayShader)(), int dataName, int nskip1, int nskip2) -{ - - if(_pyramid == NULL)return; - int grid_sz = (_level_num -nskip1 - nskip2); - if(grid_sz > 4) grid_sz = (int)ceil(grid_sz*0.5); - double scale = 1.0/grid_sz; - int stepx = 0, stepy = 0, dx, dy=0, nstep; - - if(_pyramid->_octave_min >0) scale *= (1<<_pyramid->_octave_min); - else if(_pyramid->_octave_min < 0) scale /= (1<<(-_pyramid->_octave_min)); - - - glPushMatrix(); - glScaled(scale, scale, scale); - - for(int i = _pyramid->_octave_min; i < _pyramid->_octave_min+_pyramid->_octave_num; i++) - { - - nstep = i==_pyramid->_octave_min? grid_sz: _level_num; - dx = 0; - UseDisplayShader(); - for(int j = _level_min + nskip1; j <= _level_max-nskip2; j++) - { - GLTexImage * tex = _pyramid->GetLevelTexture(i, j, dataName); - if(tex->GetImgWidth() == 0 || tex->GetImgHeight() == 0) continue; - stepx = tex->GetImgWidth(); - stepy = tex->GetImgHeight(); - //// - if(j == _level_min + nskip1 + nstep) - { - dy += stepy; - dx = 0; - } - - glPushMatrix(); - glTranslated(dx, dy, 0); - tex->BindTex(); - tex->DrawImage(); - tex->UnbindTex(); - glPopMatrix(); - - dx += stepx; - - } - - ShaderMan::UnloadProgram(); - - dy+= stepy; - } - - glPopMatrix(); -} - - -void SiftGPUEX::DisplayLevel(void (*UseDisplayShader)(), int i) -{ - if(_pyramid == NULL)return; - - i = i%(_level_num * _pyramid->_octave_num); - if (i<0 ) i+= (_level_num * _pyramid->_octave_num); - int octave = _pyramid->_octave_min + i/_level_num; - int level = _level_min + i%_level_num; - double scale = 1.0; - - if(octave >0) scale *= (1<<octave); - else if(octave < 0) scale /= (1<<(-octave)); - - GLTexImage * tex = _pyramid->GetLevelTexture(octave, level); - - UseDisplayShader(); - - glPushMatrix(); - glScaled(scale, scale, scale); - tex->BindTex(); - tex->DrawImage(); - tex->UnbindTex(); - glPopMatrix(); - ShaderMan::UnloadProgram(); -} - -void SiftGPUEX::DisplaySIFT() -{ - if(_pyramid == NULL) return; - glEnable(GlobalUtil::_texTarget); - switch(_view) - { - case 0: - DisplayInput(); - DisplayFeatureBox(_sub_view); - break; - case 1: - DisplayPyramid(ShaderMan::UseShaderDisplayGaussian, SiftPyramid::DATA_GAUSSIAN); - break; - case 2: - DisplayOctave(ShaderMan::UseShaderDisplayGaussian, _sub_view); - break; - case 3: - DisplayLevel(ShaderMan::UseShaderDisplayGaussian, _sub_view); - break; - case 4: - DisplayPyramid(ShaderMan::UseShaderDisplayDOG, SiftPyramid::DATA_DOG, 1); - break; - case 5: - DisplayPyramid(ShaderMan::UseShaderDisplayGrad, SiftPyramid::DATA_GRAD, 1); - break; - case 6: - DisplayPyramid(ShaderMan::UseShaderDisplayDOG, SiftPyramid::DATA_DOG,2, 1); - DisplayPyramid(ShaderMan::UseShaderDisplayKeypoints, SiftPyramid::DATA_KEYPOINT, 2,1); - } -} - - -void SiftGPUEX::SetView(int view, int sub_view, char *title) -{ - const char* view_titles[] = - { - "Original Image", - "Gaussian Pyramid", - "Octave Images", - "Level Image", - "Difference of Gaussian", - "Gradient", - "Keypoints" - }; - const int view_num = 7; - _view = view % view_num; - if(_view <0) _view +=view_num; - _sub_view = sub_view; - - if(_view_debug) - strcpy(title, "Debug..."); - else - strcpy(title, view_titles[_view]); - -} - - -void SiftGPU::PrintUsage() -{ - std::cout - <<"SiftGPU Usage:\n" - <<"-h -help : Parameter information\n" - <<"-i <strings> : Filename(s) of the input image(s)\n" - <<"-il <string> : Filename of an image list file\n" - <<"-o <string> : Where to save SIFT features\n" - <<"-f <float> : Filter width factor; Width will be 2*factor+1 (default : 4.0)\n" - <<"-w <float> : Orientation sample window factor (default: 2.0)\n" - <<"-dw <float> * : Descriptor grid size factor (default : 3.0)\n" - <<"-fo <int> * : First octave to detect DOG keypoints(default : 0)\n" - <<"-no <int> : Maximum number of Octaves (default : no limit)\n" - <<"-d <int> : Number of DOG levels in an octave (default : 3)\n" - <<"-t <float> : DOG threshold (default : 0.02/3)\n" - <<"-e <float> : Edge Threshold (default : 10.0)\n" - <<"-m <int=2> : Multi Feature Orientations (default : 1)\n" - <<"-m2p : 2 Orientations packed as one float\n" - <<"-s <int=1> : Sub-Pixel, Sub-Scale Localization, Multi-Refinement(num)\n" - <<"-lcpu -lc <int> : CPU/GPU mixed Feature List Generation (defaut : 6)\n" - <<" Use GPU first, and use CPU when reduction size <= pow(2,num)\n" - <<" When <num> is missing or equals -1, no GPU will be used\n" - <<"-noprep : Upload raw data to GPU (default: RGB->LUM and down-sample on CPU)\n" - <<"-sd : Skip descriptor computation if specified\n" - <<"-unn * : Write unnormalized descriptor if specified\n" - <<"-b * : Write binary sift file if specified\n" - <<"-fs <int> : Block Size for freature storage <default : 4>\n" - <<"-cuda <int=0> : Use CUDA SiftGPU, and specifiy the device index\n" - <<"-tight : Automatically resize pyramid to fit new images tightly\n" - <<"-p <W>x<H> : Inititialize the pyramids to contain image of WxH (eg -p 1024x768)\n" - <<"-tc[1|2|3] <int> *: Threshold for limiting the overall number of features (3 methods)\n" - <<"-v <int> : Level of timing details. Same as calling Setverbose() function\n" - <<"-loweo : (0, 0) at center of top-left pixel (defaut: corner)\n" - <<"-maxd <int> * : Max working dimension (default : 2560 (unpacked) / 3200 (packed))\n" - <<"-nomc : Disabling auto-downsamping that try to fit GPU memory cap\n" - <<"-exit : Exit program after processing the input image\n" - <<"-unpack : Use the old unpacked implementation\n" - <<"-di : Use dynamic array indexing if available (defualt : no)\n" - <<" It could make computation faster on cards like GTX 280\n" - <<"-ofix * : use 0 as feature orientations.\n" - <<"-ofix-not * : disable -ofix.\n" - <<"-winpos <X>x<Y> * : Screen coordinate used in Win32 to select monitor/GPU.\n" - <<"-display <string>*: Display name used in Linux/Mac to select monitor/GPU.\n" - <<"\n" - <<"NOTE: parameters marked with * can be changed after initialization\n" - <<"\n"; -} - -void SiftGPU::ParseParam(int argc, char **argv) -{ - #define CHAR1_TO_INT(x) ((x >= 'A' && x <= 'Z') ? x + 32 : x) - #define CHAR2_TO_INT(str, i) (str[i] ? CHAR1_TO_INT(str[i]) + (CHAR1_TO_INT(str[i+1]) << 8) : 0) - #define CHAR3_TO_INT(str, i) (str[i] ? CHAR1_TO_INT(str[i]) + (CHAR2_TO_INT(str, i + 1) << 8) : 0) - #define STRING_TO_INT(str) (CHAR1_TO_INT(str[0]) + (CHAR3_TO_INT(str, 1) << 8)) - -#ifdef _MSC_VER - //charizing is microsoft only - #define MAKEINT1(a) (#@a ) -#else - #define mychar0 '0' - #define mychar1 '1' - #define mychar2 '2' - #define mychar3 '3' - #define mychara 'a' - #define mycharb 'b' - #define mycharc 'c' - #define mychard 'd' - #define mychare 'e' - #define mycharf 'f' - #define mycharg 'g' - #define mycharh 'h' - #define mychari 'i' - #define mycharj 'j' - #define mychark 'k' - #define mycharl 'l' - #define mycharm 'm' - #define mycharn 'n' - #define mycharo 'o' - #define mycharp 'p' - #define mycharq 'q' - #define mycharr 'r' - #define mychars 's' - #define mychart 't' - #define mycharu 'u' - #define mycharv 'v' - #define mycharw 'w' - #define mycharx 'x' - #define mychary 'y' - #define mycharz 'z' - #define MAKEINT1(a) (mychar##a ) -#endif - #define MAKEINT2(a, b) (MAKEINT1(a) + (MAKEINT1(b) << 8)) - #define MAKEINT3(a, b, c) (MAKEINT1(a) + (MAKEINT2(b, c) << 8)) - #define MAKEINT4(a, b, c, d) (MAKEINT1(a) + (MAKEINT3(b, c, d) << 8)) - - - char* arg, *param, * opt; - int setMaxD = 0, opti; - for(int i = 0; i< argc; i++) - { - arg = argv[i]; - if(arg == NULL || arg[0] != '-' || !arg[1])continue; - opt = arg+1; - opti = STRING_TO_INT(opt); - param = argv[i+1]; - - //////////////////////////////// - switch(opti) - { - case MAKEINT1(h): - case MAKEINT4(h, e, l, p): - PrintUsage(); - break; - case MAKEINT4(c, u, d, a): -#if defined(CUDA_SIFTGPU_ENABLED) - - if(!_initialized) - { - GlobalUtil::_UseCUDA = 1; - int device = -1; - if(i+1 <argc && sscanf(param, "%d", &device) && device >=0) - { - GlobalUtil::_DeviceIndex = device; - i++; - } - } -#else - std::cerr << "---------------------------------------------------------------------------\n" - << "CUDA not supported in this binary! To enable it, please use SiftGPU_CUDA_Enable\n" - << "solution for VS2005+ or set siftgpu_enable_cuda to 1 in makefile\n" - << "----------------------------------------------------------------------------\n"; -#endif - break; - case MAKEINT2(c, l): -#if defined(CL_SIFTGPU_ENABLED) - if(!_initialized) GlobalUtil::_UseOpenCL = 1; -#else - std::cerr << "---------------------------------------------------------------------------\n" - << "OpenCL not supported in this binary! Define CL_CUDA_SIFTGPU_ENABLED to..\n" - << "----------------------------------------------------------------------------\n"; -#endif - break; - - case MAKEINT4(p, a, c, k): - if(!_initialized) GlobalUtil::_usePackedTex = 1; - break; - case MAKEINT4(u, n, p, a): //unpack - if(!_initialized) - { - GlobalUtil::_usePackedTex = 0; - if(!setMaxD) GlobalUtil::_texMaxDim = 2560; - } - break; - case MAKEINT4(l, c, p, u): - case MAKEINT2(l, c): - if(!_initialized) - { - int gskip = -1; - if(i+1 <argc) sscanf(param, "%d", &gskip); - if(gskip >= 0) - { - GlobalUtil::_ListGenSkipGPU = gskip; - }else - { - GlobalUtil::_ListGenGPU = 0; - } - } - break; - case MAKEINT4(p, r, e, p): - GlobalUtil::_PreProcessOnCPU = 1; - break; - case MAKEINT4(n, o, p, r): //noprep - GlobalUtil::_PreProcessOnCPU = 0; - break; - case MAKEINT4(f, b, o, 1): - FrameBufferObject::UseSingleFBO =1; - break; - case MAKEINT4(f, b, o, s): - FrameBufferObject::UseSingleFBO = 0; - break; - case MAKEINT2(s, d): - if(!_initialized) GlobalUtil::_DescriptorPPT =0; - break; - case MAKEINT3(u, n, n): - GlobalUtil::_NormalizedSIFT =0; - break; - case MAKEINT4(n, d, e, s): - GlobalUtil::_NormalizedSIFT =1; - break; - case MAKEINT1(b): - GlobalUtil::_BinarySIFT = 1; - break; - case MAKEINT4(t, i, g, h): //tight - GlobalUtil::_ForceTightPyramid = 1; - break; - case MAKEINT4(e, x, i, t): - GlobalUtil::_ExitAfterSIFT = 1; - break; - case MAKEINT2(d, i): - GlobalUtil::_UseDynamicIndexing = 1; - break; - case MAKEINT4(s, i, g, n): - if(!_initialized || GlobalUtil::_UseCUDA) GlobalUtil::_KeepExtremumSign = 1; - break; - case MAKEINT1(m): - case MAKEINT2(m, o): - if(!_initialized) - { - int mo = 2; //default multi-orientation - if(i+1 <argc) sscanf(param, "%d", &mo); - //at least two orientation - GlobalUtil::_MaxOrientation = min(max(1, mo), 4); - } - break; - case MAKEINT3(m, 2, p): - if(!_initialized) - { - GlobalUtil::_MaxOrientation = 2; - GlobalUtil::_OrientationPack2 = 1; - } - break; - case MAKEINT1(s): - if(!_initialized) - { - int sp = 1; //default refinement - if(i+1 <argc) sscanf(param, "%d", &sp); - //at least two orientation - GlobalUtil::_SubpixelLocalization = min(max(0, sp),5); - } - break; - case MAKEINT4(o, f, i, x): - GlobalUtil::_FixedOrientation = (_stricmp(opt, "ofix")==0); - break; - case MAKEINT4(l, o, w, e): // loweo - GlobalUtil::_LoweOrigin = 1; - break; - case MAKEINT4(n, a, r, r): // narrow - GlobalUtil::_NarrowFeatureTex = 1; - break; - case MAKEINT4(d, e, b, u): // debug - GlobalUtil::_debug = 1; - break; - case MAKEINT2(k, 0): - GlobalUtil::_KeyPointListForceLevel0 = 1; - break; - case MAKEINT2(k, x): - GlobalUtil::_KeyPointListForceLevel0 = 0; - break; - case MAKEINT2(d, a): - GlobalUtil::_DarknessAdaption = 1; - break; - case MAKEINT3(f, m, c): - GlobalUtil::_FitMemoryCap = 1; - break; - case MAKEINT4(n, o, m, c): - GlobalUtil::_FitMemoryCap = 0; - break; - default: - if(i + 1 >= argc) break; - switch(opti) - { - case MAKEINT1(i): - strcpy(_imgpath, param); - i++; - //get the file list.. - _list->push_back(param); - while( i+1 < argc && argv[i+1][0] !='-') - { - _list->push_back(argv[++i]); - } - break; - case MAKEINT2(i, l): - LoadImageList(param); - i++; - break; - case MAKEINT1(o): - strcpy(_outpath, param); - i++; - break; - case MAKEINT1(f): - { - float factor = 0.0f; - if(sscanf(param, "%f", &factor) && factor > 0 ) - { - GlobalUtil::_FilterWidthFactor = factor; - i++; - } - } - break; - case MAKEINT2(o, t): - { - float factor = 0.0f; - if(sscanf(param, "%f", &factor) && factor>0 ) - { - GlobalUtil::_MulitiOrientationThreshold = factor; - i++; - } - break; - } - case MAKEINT1(w): - { - float factor = 0.0f; - if(sscanf(param, "%f", &factor) && factor>0 ) - { - GlobalUtil::_OrientationWindowFactor = factor; - i++; - } - break; - } - case MAKEINT2(d, w): - { - float factor = 0.0f; - if(sscanf(param, "%f", &factor) && factor > 0 ) - { - GlobalUtil::_DescriptorWindowFactor = factor; - i++; - } - break; - } - case MAKEINT2(f, o): - { - int first_octave = -3; - if(sscanf(param, "%d", &first_octave) && first_octave >=-2 ) - { - GlobalUtil::_octave_min_default = first_octave; - i++; - } - break; - } - case MAKEINT2(n, o): - if(!_initialized) - { - int octave_num=-1; - if(sscanf(param, "%d", &octave_num)) - { - octave_num = max(-1, octave_num); - if(octave_num ==-1 || octave_num >=1) - { - GlobalUtil::_octave_num_default = octave_num; - i++; - } - } - } - break; - case MAKEINT1(t): - { - float threshold = 0.0f; - if(sscanf(param, "%f", &threshold) && threshold >0 && threshold < 0.5f) - { - SiftParam::_dog_threshold = threshold; - i++; - } - break; - } - case MAKEINT1(e): - { - float threshold = 0.0f; - if(sscanf(param, "%f", &threshold) && threshold >0 ) - { - SiftParam::_edge_threshold = threshold; - i++; - } - break; - } - case MAKEINT1(d): - { - int num = 0; - if(sscanf(param, "%d", &num) && num >=1 && num <=10) - { - SiftParam::_dog_level_num = num; - i++; - } - break; - } - case MAKEINT2(f, s): - { - int num = 0; - if(sscanf(param, "%d", &num) && num >=1) - { - GlobalParam::_FeatureTexBlock = num; - i++; - } - break; - } - case MAKEINT1(p): - { - int w =0, h=0; - if(sscanf(param, "%dx%d", &w, &h) == 2 && w >0 && h>0) - { - GlobalParam::_InitPyramidWidth = w; - GlobalParam::_InitPyramidHeight = h; - i++; - } - break; - } - case MAKEINT4(w, i, n, p): //winpos - { - int x =0, y=0; - if(sscanf(param, "%dx%d", &x, &y) == 2) - { - GlobalParam::_WindowInitX = x; - GlobalParam::_WindowInitY = y; - i++; - } - break; - } - case MAKEINT4(d, i, s, p): //display - { - GlobalParam::_WindowDisplay = param; - i++; - break; - } - case MAKEINT2(l, m): - { - int num = 0; - if(sscanf(param, "%d", &num) && num >=1000) - { - GlobalParam::_MaxLevelFeatureNum = num; - i++; - } - break; - } - case MAKEINT3(l, m, p): - { - float num = 0.0f; - if(sscanf(param, "%f", &num) && num >=0.001) - { - GlobalParam::_MaxFeaturePercent = num; - i++; - } - break; - } - case MAKEINT3(t, c, 2): //downward - case MAKEINT3(t, c, 3): - case MAKEINT2(t, c): //tc - case MAKEINT3(t, c, 1): // - { - switch (opti) - { - case MAKEINT3(t, c, 2): GlobalUtil::_TruncateMethod = 1; break; - case MAKEINT3(t, c, 3): GlobalUtil::_TruncateMethod = 2; break; - default: GlobalUtil::_TruncateMethod = 0; break; - } - int num = -1; - if(sscanf(param, "%d", &num) && num > 0) - { - GlobalParam::_FeatureCountThreshold = num; - i++; - } - break; - } - case MAKEINT1(v): - { - int num = 0; - if(sscanf(param, "%d", &num) && num >=0 && num <= 4) - { - SetVerbose(num); - } - break; - } - case MAKEINT4(m, a, x, d): - { - int num = 0; - if(sscanf(param, "%d", &num) && num > 0) - { - GlobalUtil::_texMaxDim = num; - setMaxD = 1; - } - break; - } - case MAKEINT4(m, i, n, d): - { - int num = 0; - if(sscanf(param, "%d", &num) && num >= 8) - { - GlobalUtil::_texMinDim = num; - } - break; - } - default: - break; - } - break; - } - } - - //////////////////////// - GlobalUtil::SelectDisplay(); - - - //do not write result if there are more than one input images - if(_outpath[0] && _list->size()>1) _outpath[0] = 0; - -} - -void SiftGPU::SetImageList(int nimage, const char** filelist) -{ - _list->resize(0); - for(int i = 0; i < nimage; i++) - { - _list->push_back(filelist[i]); - } - _current = 0; - -} -void SiftGPU:: LoadImageList(const char *imlist) -{ - char filename[_MAX_PATH]; - ifstream in(imlist); - while(in>>filename) - { - _list->push_back(filename); - } - in.close(); - - - if(_list->size()>0) - { - strcpy(_imgpath, _list->at(0).data()); - strcpy(filename, imlist); - char * slash = strrchr(filename, '\\'); - if(slash == 0) slash = strrchr(filename, '/'); - if(slash ) - { - slash[1] = 0; - chdir(filename); - } - } - _image_loaded = 0; - - -} -float SiftParam::GetLevelSigma( int lev) -{ - return _sigma0 * powf( 2.0f, float(lev) / float(_dog_level_num )); //bug fix 9/12/2007 -} - - - -void SiftGPUEX::DisplayFeatureBox(int view ) -{ - view = view%3; - if(view<0)view+=3; - if(view ==2) return; - int idx = 0; - const int *fnum = _pyramid->GetLevelFeatureNum(); - const GLuint *vbo = _pyramid->GetFeatureDipslayVBO(); - const GLuint *vbop = _pyramid->GetPointDisplayVBO(); - if(vbo == NULL || vbop == NULL) return; - //int nvbo = _dog_level_num * _pyramid->_octave_num; - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - glEnableClientState(GL_VERTEX_ARRAY); - glPushMatrix(); -// glTranslatef(0.0f, 0.0f, -1.0f); - glPointSize(2.0f); - - float scale = 1.0f; - if(_pyramid->_octave_min >0) scale *= (1<<_pyramid->_octave_min); - else if(_pyramid->_octave_min < 0) scale /= (1<<(-_pyramid->_octave_min)); - glScalef(scale, scale, 1.0f); - - - for(int i = 0; i < _pyramid->_octave_num; i++) - { - - for(int j = 0; j < _dog_level_num; j++, idx++) - { - if(fnum[idx]>0) - { - if(view ==0) - { - glColor3f(0.2f, 1.0f, 0.2f); - glBindBuffer(GL_ARRAY_BUFFER_ARB, vbop[idx]); - glVertexPointer( 4, GL_FLOAT,4*sizeof(float), (char *) 0); - glDrawArrays( GL_POINTS, 0, fnum[idx]); - glFlush(); - }else - { - - //glColor3f(1.0f, 0.0f, 0.0f); - glColor3fv(_colors+ (idx%COLOR_NUM)*3); - glBindBuffer(GL_ARRAY_BUFFER_ARB, vbo[idx]); - glVertexPointer( 4, GL_FLOAT,4*sizeof(float), (char *) 0); - glDrawArrays( GL_LINES, 0, fnum[idx]*10 ); - glFlush(); - } - - } - - } - glTranslatef(-.5f, -.5f, 0.0f); - glScalef(2.0f, 2.0f, 1.0f); - - } - glPopMatrix(); - glDisableClientState(GL_VERTEX_ARRAY); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - glPointSize(1.0f); - -} - -void SiftGPUEX::ToggleDisplayDebug() -{ - _view_debug = !_view_debug; -} - -void SiftGPUEX::DisplayDebug() -{ - glPointSize(1.0f); - glColor3f(1.0f, 0.0f, 0.0f); - ShaderMan::UseShaderDebug(); - glBegin(GL_POINTS); - for(int i = 0; i < 100; i++) - { - glVertex2f(i*4.0f+0.5f, i*4.0f+0.5f); - } - glEnd(); - ShaderMan::UnloadProgram(); -} - -int SiftGPU::CreateContextGL() -{ - if(GlobalUtil::_UseOpenCL || GlobalUtil::_UseCUDA) - { - //do nothing - } - else if(!GlobalUtil::CreateWindowEZ()) - { -#if CUDA_SIFTGPU_ENABLED - GlobalUtil::_UseCUDA = 1; -#else - return 0; -#endif - } - - return VerifyContextGL(); -} - -int SiftGPU::VerifyContextGL() -{ - InitSiftGPU(); - return (GlobalUtil::_GoodOpenGL > 0) + GlobalUtil::_FullSupported; -} - -int SiftGPU::IsFullSupported() -{ - return GlobalUtil::_GoodOpenGL > 0 && GlobalUtil::_FullSupported; -} - -void SiftGPU::SaveSIFT(const char * szFileName) -{ - _pyramid->SaveSIFT(szFileName); -} - -int SiftGPU::GetFeatureNum() -{ - return _pyramid->GetFeatureNum(); -} - -void SiftGPU::GetFeatureVector(SiftKeypoint * keys, float * descriptors) -{ -// keys.resize(_pyramid->GetFeatureNum()); - if(GlobalUtil::_DescriptorPPT) - { - // descriptors.resize(128*_pyramid->GetFeatureNum()); - _pyramid->CopyFeatureVector((float*) (&keys[0]), &descriptors[0]); - }else - { - //descriptors.resize(0); - _pyramid->CopyFeatureVector((float*) (&keys[0]), NULL); - } -} - -void SiftGPU::SetTightPyramid(int tight) -{ - GlobalUtil::_ForceTightPyramid = tight; -} - -int SiftGPU::AllocatePyramid(int width, int height) -{ - _pyramid->_down_sample_factor = 0; - _pyramid->_octave_min = GlobalUtil::_octave_min_default; - if(GlobalUtil::_octave_min_default>=0) - { - width >>= GlobalUtil::_octave_min_default; - height >>= GlobalUtil::_octave_min_default; - }else - { - width <<= (-GlobalUtil::_octave_min_default); - height <<= (-GlobalUtil::_octave_min_default); - } - _pyramid->ResizePyramid(width, height); - return _pyramid->_pyramid_height == height && width == _pyramid->_pyramid_width ; -} - -void SiftGPU::SetMaxDimension(int sz) -{ - if(sz < GlobalUtil::_texMaxDimGL) - { - GlobalUtil::_texMaxDim = sz; - } -} -int SiftGPU::GetImageCount() -{ - return _list->size(); -} - -void SiftGPUEX::HSVtoRGB(float hsv[3],float rgb[3] ) -{ - - int i; - float q, t, p; - float hh,f, v = hsv[2]; - if(hsv[1]==0.0f) - { - rgb[0]=rgb[1]=rgb[2]=v; - } - else - { - ////////////// - hh =hsv[0]*6.0f ; // sector 0 to 5 - i =(int)hh ; - f = hh- i; // factorial part of h - ////////// - p= v * ( 1 - hsv[1] ); - q = v * ( 1 - hsv[1] * f ); - t = v * ( 1 - hsv[1] * ( 1 - f ) ); - switch( i ) { - case 0:rgb[0] = v;rgb[1] = t;rgb[2] = p;break; - case 1:rgb[0] = q;rgb[1] = v;rgb[2] = p;break; - case 2:rgb[0] = p;rgb[1] = v;rgb[2] = t;break; - case 3:rgb[0] = p;rgb[1] = q;rgb[2] = v;break; - case 4:rgb[0] = t;rgb[1] = p;rgb[2] = v;break; - case 5:rgb[0] = v;rgb[1] = p;rgb[2] = q;break; - default:rgb[0]= 0;rgb[1] = 0;rgb[2] = 0; - } - } -} - -void SiftGPUEX::GetImageDimension( int &w, int &h) -{ - w = _texImage->GetImgWidth(); - h = _texImage->GetImgHeight(); - -} - -void SiftGPUEX::GetInitWindowPotition(int&x, int&y) -{ - x = GlobalUtil::_WindowInitX; - y = GlobalUtil::_WindowInitY; -} - -SiftGPU* CreateNewSiftGPU(int np) -{ - return new SiftGPU(np); -} - -///////////////////////////////////////////////////// -void* ComboSiftGPU::operator new (size_t size){ - void * p = malloc(size); - if (p == 0) - { - const std::bad_alloc ba; - throw ba; - } - return p; -} - -ComboSiftGPU* CreateComboSiftGPU() -{ - return new ComboSiftGPU(); -} - diff --git a/3rdparty/SiftGPU/src/SiftGPU/SiftGPU.h b/3rdparty/SiftGPU/src/SiftGPU/SiftGPU.h deleted file mode 100644 index b76c14c4..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/SiftGPU.h +++ /dev/null @@ -1,379 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: SiftGPU.h -// Author: Changchang Wu -// Description : interface for the SIFTGPU class. -// SiftGPU: The SiftGPU Tool. -// SiftGPUEX: SiftGPU + viewer -// SiftParam: Sift Parameters -// SiftMatchGPU: GPU SIFT Matcher; -// -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - - -#ifndef GPU_SIFT_H -#define GPU_SIFT_H - -#if defined(_WIN32) - #ifdef SIFTGPU_DLL - #ifdef DLL_EXPORT - #define SIFTGPU_EXPORT __declspec(dllexport) - #else - #define SIFTGPU_EXPORT __declspec(dllimport) - #endif - #else - #define SIFTGPU_EXPORT - #endif - - #define SIFTGPU_EXPORT_EXTERN SIFTGPU_EXPORT - - #if _MSC_VER > 1000 - #pragma once - #endif -#else - #define SIFTGPU_EXPORT - #define SIFTGPU_EXPORT_EXTERN extern "C" -#endif - -/////////////////////////////////////////////////////////////////// -//clss SiftParam -//description: SIFT parameters -//////////////////////////////////////////////////////////////////// -class GlobalUtil; -class SiftParam -{ -public: - float* _sigma; - float _sigma_skip0; // - float _sigma_skip1; // - - //sigma of the first level - float _sigma0; - float _sigman; - int _sigma_num; - - //how many dog_level in an octave - int _dog_level_num; - int _level_num; - - //starting level in an octave - int _level_min; - int _level_max; - int _level_ds; - //dog threshold - float _dog_threshold; - //edge elimination - float _edge_threshold; - void ParseSiftParam(); -public: - float GetLevelSigma(int lev); - float GetInitialSmoothSigma(int octave_min); - SIFTGPU_EXPORT SiftParam(); -}; - -class LiteWindow; -class GLTexInput; -class ShaderMan; -class SiftPyramid; -class ImageList; -//////////////////////////////////////////////////////////////// -//class SIftGPU -//description: Interface of SiftGPU lib -//////////////////////////////////////////////////////////////// -class SiftGPU:public SiftParam -{ -public: - enum - { - SIFTGPU_NOT_SUPPORTED = 0, - SIFTGPU_PARTIAL_SUPPORTED = 1, // detction works, but not orientation/descriptor - SIFTGPU_FULL_SUPPORTED = 2 - }; - typedef struct SiftKeypoint - { - float x, y, s, o; //x, y, scale, orientation. - }SiftKeypoint; -protected: - //when more than one images are specified - //_current indicates the active one - int _current; - //_initialized indicates if the shaders and OpenGL/SIFT parameters are initialized - //they are initialized only once for one SiftGPU inistance - //that is, SIFT parameters will not be changed - int _initialized; - //_image_loaded indicates if the current images are loaded - int _image_loaded; - //the name of current input image - char* _imgpath; - //_outpath containes the name of the output file - char* _outpath; - //the list of image filenames - ImageList * _list; - //the texture that holds loaded input image - GLTexInput * _texImage; - //the SiftPyramid - SiftPyramid * _pyramid; - //print out the command line options - static void PrintUsage(); - //Initialize OpenGL and SIFT paremeters, and create the shaders accordingly - void InitSiftGPU(); - //load the image list from a file - void LoadImageList(const char *imlist); -public: - //timing results for 10 steps - float _timing[10]; - inline const char* GetCurrentImagePath() {return _imgpath; } -public: - //set the image list for processing - SIFTGPU_EXPORT virtual void SetImageList(int nimage, const char** filelist); - //get the number of SIFT features in current image - SIFTGPU_EXPORT virtual int GetFeatureNum(); - //save the SIFT result as a ANSCII/BINARY file - SIFTGPU_EXPORT virtual void SaveSIFT(const char * szFileName); - //Copy the SIFT result to two vectors - SIFTGPU_EXPORT virtual void GetFeatureVector(SiftKeypoint * keys, float * descriptors); - //Set keypoint list before running sift to get descriptors - SIFTGPU_EXPORT virtual void SetKeypointList(int num, const SiftKeypoint * keys, int keys_have_orientation = 1); - //Enable downloading results to CPU. - //create a new OpenGL context for processing - //call VerifyContextGL instead if you want to crate openGL context yourself, or your are - //mixing mixing siftgpu with other openGL code - SIFTGPU_EXPORT virtual int CreateContextGL(); - //verify the current opengl context.. - //(for example, you call wglmakecurrent yourself and verify the current context) - SIFTGPU_EXPORT virtual int VerifyContextGL(); - //check if all siftgpu functions are supported - SIFTGPU_EXPORT virtual int IsFullSupported(); - //set verbose mode - SIFTGPU_EXPORT virtual void SetVerbose(int verbose = 4); - //set SiftGPU to brief display mode, which is faster - inline void SetVerboseBrief(){SetVerbose(2);}; - //parse SiftGPU parameters - SIFTGPU_EXPORT virtual void ParseParam(int argc, char **argv); - //run SIFT on a new image given filename - SIFTGPU_EXPORT virtual int RunSIFT(const char * imgpath); - //run SIFT on an image in the image list given the file index - SIFTGPU_EXPORT virtual int RunSIFT(int index); - //run SIFT on a new image given the pixel data and format/type; - //gl_format (e.g. GL_LUMINANCE, GL_RGB) is the format of the pixel data - //gl_type (e.g. GL_UNSIGNED_BYTE, GL_FLOAT) is the data type of the pixel data; - //Check glTexImage2D(...format, type,...) for the accepted values - //Using image data of GL_LUMINANCE + GL_UNSIGNED_BYTE can minimize transfer time - SIFTGPU_EXPORT virtual int RunSIFT(int width, int height, const void * data, - unsigned int gl_format, unsigned int gl_type); - //run SIFT on current image (specified by arguments), or processing the current image again - SIFTGPU_EXPORT virtual int RunSIFT(); - //run SIFT with keypoints on current image again. - SIFTGPU_EXPORT virtual int RunSIFT(int num, const SiftKeypoint * keys, int keys_have_orientation = 1); - //constructor, the parameter np is ignored.. - SIFTGPU_EXPORT SiftGPU(int np = 1); - //destructor - SIFTGPU_EXPORT virtual ~SiftGPU(); - //set the active pyramid...dropped function - SIFTGPU_EXPORT virtual void SetActivePyramid(int index) {} - //retrieve the number of images in the image list - SIFTGPU_EXPORT virtual int GetImageCount(); - //set parameter GlobalUtil::_ForceTightPyramid - SIFTGPU_EXPORT virtual void SetTightPyramid(int tight = 1); - //allocate pyramid for a given size of image - SIFTGPU_EXPORT virtual int AllocatePyramid(int width, int height); - //none of the texture in processing can be larger - //automatic down-sample is used if necessary. - SIFTGPU_EXPORT virtual void SetMaxDimension(int sz); - /// -public: - //overload the new operator because delete operator is virtual - //and it is operating on the heap inside the dll (due to the - //compiler setting of /MT and /MTd). Without the overloaded operator - //deleting a SiftGPU object will cause a heap corruption in the - //static link case (but not for the runtime dll loading). - SIFTGPU_EXPORT void* operator new (size_t size); -}; - - - -//////////////////////////////////////////////////////////////// -//class SIftGPUEX -//description: adds some visualization functions to the interface of SiftGPU -//////////////////////////////////////////////////////////////// - -class SiftGPUEX:public SiftGPU -{ - //view mode - int _view; - //sub view mode - int _sub_view; - //whether display a debug view - int _view_debug; - //colors for SIFT feature display - enum{COLOR_NUM = 36}; - float _colors[COLOR_NUM*3]; - //display functions - void DisplayInput(); //display gray level image of input image - void DisplayDebug(); //display debug view - void DisplayFeatureBox(int i); //display SIFT features - void DisplayLevel(void (*UseDisplayShader)(), int i); //display one level image - void DisplayOctave(void (*UseDisplayShader)(), int i); //display all images in one octave - //display different content of Pyramid by specifying different data and display shader - //the first nskip1 levels and the last nskip2 levels are skiped in display - void DisplayPyramid( void (*UseDisplayShader)(), int dataName, int nskip1 = 0, int nskip2 = 0); - //use HSVtoRGB to generate random colors - static void HSVtoRGB(float hsv[3],float rgb[3]); - -public: - SIFTGPU_EXPORT SiftGPUEX(); - //change view mode - SIFTGPU_EXPORT void SetView(int view, int sub_view, char * title); - //display current view - SIFTGPU_EXPORT void DisplaySIFT(); - //toggle debug mode on/off - SIFTGPU_EXPORT void ToggleDisplayDebug(); - //randomize the display colors - SIFTGPU_EXPORT void RandomizeColor(); - //retrieve the size of current input image - SIFTGPU_EXPORT void GetImageDimension(int &w, int&h); - //get the location of the window specified by user - SIFTGPU_EXPORT void GetInitWindowPotition(int& x, int& y); -}; - -///matcher export -//This is a gpu-based sift match implementation. -class SiftMatchGPU -{ -public: - enum SIFTMATCH_LANGUAGE { - SIFTMATCH_SAME_AS_SIFTGPU = 0, //when siftgpu already initialized. - SIFTMATCH_GLSL = 2, - SIFTMATCH_CUDA = 3, - SIFTMATCH_CUDA_DEVICE0 = 3 //to use device i, use SIFTMATCH_CUDA_DEVICE0 + i - }; -private: - int __max_sift; - int __language; - SiftMatchGPU * __matcher; - virtual void InitSiftMatch(){} -protected: - //move the two functions here for derived class - SIFTGPU_EXPORT virtual int _CreateContextGL(); - SIFTGPU_EXPORT virtual int _VerifyContextGL(); -public: - //OpenGL Context creation/verification, initialization is done automatically inside - inline int CreateContextGL() {return _CreateContextGL();} - inline int VerifyContextGL() {return _VerifyContextGL();} - - //Consructor, the argument specifies the maximum number of features to match - SIFTGPU_EXPORT SiftMatchGPU(int max_sift = 4096); - - //change gpu_language, check the enumerants in SIFTMATCH_LANGUAGE. - SIFTGPU_EXPORT virtual void SetLanguage(int gpu_language); - - //after calling SetLanguage, you can call SetDeviceParam to select GPU - //-winpos, -display, -cuda [device_id] - //This is only used when you call CreateContextGL.. - //This function doesn't change the language. - SIFTGPU_EXPORT virtual void SetDeviceParam(int argc, char**argv); - - //change the maximum of features to match whenever you want - SIFTGPU_EXPORT virtual void SetMaxSift(int max_sift); - //desctructor - SIFTGPU_EXPORT virtual ~SiftMatchGPU(); - - //Specifiy descriptors to match, index = [0/1] for two features sets respectively - //Option1, use float descriptors, and they be already normalized to 1.0 - SIFTGPU_EXPORT virtual void SetDescriptors(int index, int num, const float* descriptors, int id = -1); - //Option 2 unsigned char descriptors. They must be already normalized to 512 - SIFTGPU_EXPORT virtual void SetDescriptors(int index, int num, const unsigned char * descriptors, int id = -1); - - //match two sets of features, the function RETURNS the number of matches. - //Given two normalized descriptor d1,d2, the distance here is acos(d1 *d2); - SIFTGPU_EXPORT virtual int GetSiftMatch( - int max_match, // the length of the match_buffer. - int match_buffer[][2], //buffer to receive the matched feature indices - float distmax = 0.7, //maximum distance of sift descriptor - float ratiomax = 0.8, //maximum distance ratio - int mutual_best_match = 1); //mutual best match or one way - - //two functions for guded matching, two constraints can be used - //one homography and one fundamental matrix, the use is as follows - //1. for each image, first call SetDescriptor then call SetFeatureLocation - //2. Call GetGuidedSiftMatch - //input feature location is a vector of [float x, float y, float skip[gap]] - SIFTGPU_EXPORT virtual void SetFeautreLocation(int index, const float* locations, int gap = 0); - inline void SetFeatureLocation(int index, const SiftGPU::SiftKeypoint * keys) - { - SetFeautreLocation(index, (const float*) keys, 2); - } - - //use a guiding Homography H and a guiding Fundamental Matrix F to compute feature matches - //the function returns the number of matches. - SIFTGPU_EXPORT virtual int GetGuidedSiftMatch( - int max_match, int match_buffer[][2], //buffer to recieve - float H[3][3], //homography matrix, (Set NULL to skip) - float F[3][3], //fundamental matrix, (Set NULL to skip) - float distmax = 0.7, //maximum distance of sift descriptor - float ratiomax = 0.8, //maximum distance ratio - float hdistmax = 32, //threshold for |H * x1 - x2|_1 - float fdistmax = 16, //threshold for sampson error of x2'FX1 - int mutual_best_match = 1); //mutual best or one way - -public: - //overload the new operator, the same reason as SiftGPU above - SIFTGPU_EXPORT void* operator new (size_t size); -}; - -typedef SiftGPU::SiftKeypoint SiftKeypoint; - -//Two exported global functions used to create SiftGPU and SiftMatchGPU -SIFTGPU_EXPORT_EXTERN SiftGPU * CreateNewSiftGPU(int np =1); -SIFTGPU_EXPORT_EXTERN SiftMatchGPU* CreateNewSiftMatchGPU(int max_sift = 4096); - - -//////////////////////////////////////////////////////////////////////////// -class ComboSiftGPU: public SiftGPU, public SiftMatchGPU -{ -public: - /////////////////////////////////////////////// - SIFTGPU_EXPORT void* operator new (size_t size); -}; -SIFTGPU_EXPORT_EXTERN ComboSiftGPU* CreateComboSiftGPU(); - -///////////////////////////////////////////////////////////////////////////////////////////// -//Multi-process mode and remote mode -SIFTGPU_EXPORT_EXTERN ComboSiftGPU* CreateRemoteSiftGPU(int port = 7777, char* remote_server = NULL); -//Run SiftGPU computation on a remote computer/process/thread -//if( remote_server == NULL) -// a local server is created in a different process and connected -// multiple-GPU can be used by creating multiple instances -// GPU selection done through SiftGPU::ParseParam function -//otherwise, -// Assumes the existenc of a remote server and connects to it -// GPU selection skipped if already done on the server-end -// RUN server: server_siftgpu -server port [siftgpu_param] -//example: -// ComboSiftGPU * combo = CreateRemoteSiftGPU(7777, "my.gpuserver.com"); -// SiftGPU* siftgpu = combo, SiftMatchGPU * matcher = combo; -// siftgpu->ParseParam... siftgpu->CreateContextGL.. -// matcher->SetLanguage...matcher->VerifyContextGL... -// // GPU-selection is done throught siftgpu->ParseParam, -// // it doesn't really initialize SiftGPU untill you call CreateContextGL/VerifyContextGL -// delete combo; - -//////////////////////////////////////////////////////////////////////// -//two internally used function. -SIFTGPU_EXPORT int CreateLiteWindow(LiteWindow* window); -SIFTGPU_EXPORT void RunServerLoop(int port, int argc, char** argv); -#endif diff --git a/3rdparty/SiftGPU/src/SiftGPU/SiftMatch.cpp b/3rdparty/SiftGPU/src/SiftGPU/SiftMatch.cpp deleted file mode 100644 index ea805591..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/SiftMatch.cpp +++ /dev/null @@ -1,687 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: SiftMatch.cpp -// Author: Changchang Wu -// Description : implementation of SiftMatchGPU and SiftMatchGL -// -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - -#include "GL/glew.h" -#include <iostream> -#include <iomanip> -#include <vector> -#include <sstream> -#include <algorithm> -using namespace std; -#include <string.h> -#include "GlobalUtil.h" - -#include "ProgramGLSL.h" -#include "GLTexImage.h" -#include "SiftGPU.h" -#include "SiftMatch.h" -#include "FrameBufferObject.h" - -#if defined(CUDA_SIFTGPU_ENABLED) -#include "CuTexImage.h" -#include "SiftMatchCU.h" -#endif - - -SiftMatchGL::SiftMatchGL(int max_sift, int use_glsl): SiftMatchGPU() -{ - s_multiply = s_col_max = s_row_max = s_guided_mult = NULL; - _num_sift[0] = _num_sift[1] = 0; - _id_sift[0] = _id_sift[1] = 0; - _have_loc[0] = _have_loc[1] = 0; - _max_sift = max_sift <=0 ? 4096 : ((max_sift + 31)/ 32 * 32) ; - _pixel_per_sift = 32; //must be 32 - _sift_num_stripe = 1; - _sift_per_stripe = 1; - _sift_per_row = _sift_per_stripe * _sift_num_stripe; - _initialized = 0; -} - -SiftMatchGL::~SiftMatchGL() -{ - if(s_multiply) delete s_multiply; - if(s_guided_mult) delete s_guided_mult; - if(s_col_max) delete s_col_max; - if(s_row_max) delete s_row_max; -} - -void SiftMatchGL::SetMaxSift(int max_sift) -{ - - max_sift = ((max_sift + 31)/32)*32; - if(max_sift > GlobalUtil::_texMaxDimGL) max_sift = GlobalUtil::_texMaxDimGL; - if(max_sift > _max_sift) - { - _max_sift = max_sift; - AllocateSiftMatch(); - _have_loc[0] = _have_loc[1] = 0; - _id_sift[0] = _id_sift[1] = -1; - _num_sift[0] = _num_sift[1] = 1; - }else - { - _max_sift = max_sift; - } - -} - -void SiftMatchGL::AllocateSiftMatch() -{ - //parameters, number of sift is limited by the texture size - if(_max_sift > GlobalUtil::_texMaxDimGL) _max_sift = GlobalUtil::_texMaxDimGL; - /// - int h = _max_sift / _sift_per_row; - int n = (GlobalUtil::_texMaxDimGL + h - 1) / GlobalUtil::_texMaxDimGL; - if ( n > 1) {_sift_num_stripe *= n; _sift_per_row *= n; } - - //initialize - - _texDes[0].InitTexture(_sift_per_row * _pixel_per_sift, _max_sift / _sift_per_row, 0,GL_RGBA8); - _texDes[1].InitTexture(_sift_per_row * _pixel_per_sift, _max_sift / _sift_per_row, 0, GL_RGBA8); - _texLoc[0].InitTexture(_sift_per_row , _max_sift / _sift_per_row, 0); - _texLoc[1].InitTexture(_sift_per_row , _max_sift / _sift_per_row, 0); - - if(GlobalUtil::_SupportNVFloat || GlobalUtil::_SupportTextureRG) - { - //use single-component texture to save memory -#ifndef GL_R32F -#define GL_R32F 0x822E -#endif - GLuint format = GlobalUtil::_SupportNVFloat ? GL_FLOAT_R_NV : GL_R32F; - _texDot.InitTexture(_max_sift, _max_sift, 0, format); - _texMatch[0].InitTexture(16, _max_sift / 16, 0, format); - _texMatch[1].InitTexture(16, _max_sift / 16, 0, format); - }else - { - _texDot.InitTexture(_max_sift, _max_sift, 0); - _texMatch[0].InitTexture(16, _max_sift / 16, 0); - _texMatch[1].InitTexture(16, _max_sift / 16, 0); - } - -} -void SiftMatchGL::InitSiftMatch() -{ - if(_initialized) return; - GlobalUtil::InitGLParam(0); - if(GlobalUtil::_GoodOpenGL == 0) return; - AllocateSiftMatch(); - LoadSiftMatchShadersGLSL(); - _initialized = 1; -} - - -void SiftMatchGL::SetDescriptors(int index, int num, const unsigned char* descriptors, int id) -{ - if(_initialized == 0) return; - if (index > 1) index = 1; - if (index < 0) index = 0; - _have_loc[index] = 0; - - //the same feature is already set - if(id !=-1 && id == _id_sift[index]) return ; - _id_sift[index] = id; - - if(num > _max_sift) num = _max_sift; - - sift_buffer.resize(num * 128 /4); - memcpy(&sift_buffer[0], descriptors, 128 * num); - _num_sift[index] = num; - int w = _sift_per_row * _pixel_per_sift; - int h = (num + _sift_per_row - 1)/ _sift_per_row; - sift_buffer.resize(w * h * 4, 0); - _texDes[index].SetImageSize(w , h); - _texDes[index].BindTex(); - if(_sift_num_stripe == 1) - { - glTexSubImage2D(GlobalUtil::_texTarget, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, &sift_buffer[0]); - }else - { - for(int i = 0; i < _sift_num_stripe; ++i) - { - int ws = _sift_per_stripe * _pixel_per_sift; - int x = i * ws; - int pos = i * ws * h * 4; - glTexSubImage2D(GlobalUtil::_texTarget, 0, x, 0, ws, h, GL_RGBA, GL_UNSIGNED_BYTE, &sift_buffer[pos]); - } - } - _texDes[index].UnbindTex(); - -} - -void SiftMatchGL::SetFeautreLocation(int index, const float* locations, int gap) -{ - if(_num_sift[index] <=0) return; - int w = _sift_per_row ; - int h = (_num_sift[index] + _sift_per_row - 1)/ _sift_per_row; - sift_buffer.resize(_num_sift[index] * 2); - if(gap == 0) - { - memcpy(&sift_buffer[0], locations, _num_sift[index] * 2 * sizeof(float)); - }else - { - for(int i = 0; i < _num_sift[index]; ++i) - { - sift_buffer[i*2] = *locations++; - sift_buffer[i*2+1]= *locations ++; - locations += gap; - } - } - sift_buffer.resize(w * h * 2, 0); - _texLoc[index].SetImageSize(w , h); - _texLoc[index].BindTex(); - if(_sift_num_stripe == 1) - { - glTexSubImage2D(GlobalUtil::_texTarget, 0, 0, 0, w, h, GL_LUMINANCE_ALPHA , GL_FLOAT , &sift_buffer[0]); - }else - { - for(int i = 0; i < _sift_num_stripe; ++i) - { - int ws = _sift_per_stripe; - int x = i * ws; - int pos = i * ws * h * 2; - glTexSubImage2D(GlobalUtil::_texTarget, 0, x, 0, ws, h, GL_LUMINANCE_ALPHA , GL_FLOAT, &sift_buffer[pos]); - } - } - _texLoc[index].UnbindTex(); - _have_loc[index] = 1; -} - -void SiftMatchGL::SetDescriptors(int index, int num, const float* descriptors, int id) -{ - if(_initialized == 0) return; - if (index > 1) index = 1; - if (index < 0) index = 0; - _have_loc[index] = 0; - - //the same feature is already set - if(id !=-1 && id == _id_sift[index]) return ; - _id_sift[index] = id; - - if(num > _max_sift) num = _max_sift; - - sift_buffer.resize(num * 128 /4); - unsigned char * pub = (unsigned char*) &sift_buffer[0]; - for(int i = 0; i < 128 * num; ++i) - { - pub[i] = int(512 * descriptors[i] + 0.5); - } - _num_sift[index] = num; - int w = _sift_per_row * _pixel_per_sift; - int h = (num + _sift_per_row - 1)/ _sift_per_row; - sift_buffer.resize(w * h * 4, 0); - _texDes[index].SetImageSize(w, h); - _texDes[index].BindTex(); - if(_sift_num_stripe == 1) - { - glTexSubImage2D(GlobalUtil::_texTarget, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, &sift_buffer[0]); - }else - { - for(int i = 0; i < _sift_num_stripe; ++i) - { - int ws = _sift_per_stripe * _pixel_per_sift; - int x = i * ws; - int pos = i * ws * h * 4; - glTexSubImage2D(GlobalUtil::_texTarget, 0, x, 0, ws, h, GL_RGBA, GL_UNSIGNED_BYTE, &sift_buffer[pos]); - } - } - _texDes[index].UnbindTex(); -} - - -void SiftMatchGL::LoadSiftMatchShadersGLSL() -{ - ProgramGLSL * program; - ostringstream out; - if(GlobalUtil::_IsNvidia) - out << "#pragma optionNV(ifcvt none)\n" - "#pragma optionNV(unroll all)\n"; - - out << "#define SIFT_PER_STRIPE " << _sift_per_stripe << ".0\n" - "#define PIXEL_PER_SIFT " << _pixel_per_sift << "\n" - "uniform sampler2DRect tex1, tex2; uniform vec2 size;\n" - "void main() \n" - "{\n" - << " vec4 val = vec4(0.0, 0.0, 0.0, 0.0), data1, buf;\n" - " vec2 index = gl_FragCoord.yx; \n" - " vec2 stripe_size = size.xy * SIFT_PER_STRIPE;\n" - " vec2 temp_div1 = index / stripe_size;\n" - " vec2 stripe_index = floor(temp_div1);\n" - " index = floor(stripe_size * (temp_div1 - stripe_index));\n" - " vec2 temp_div2 = index * vec2(1.0 / float(SIFT_PER_STRIPE));\n" - " vec2 temp_floor2 = floor(temp_div2);\n" - " vec2 index_v = temp_floor2 + vec2(0.5);\n " - " vec2 index_h = vec2(SIFT_PER_STRIPE)* (temp_div2 - temp_floor2);\n" - " vec2 tx = (index_h + stripe_index * vec2(SIFT_PER_STRIPE))* vec2(PIXEL_PER_SIFT) + 0.5;\n" - " vec2 tpos1, tpos2; \n" - " vec4 tpos = vec4(tx, index_v);\n" - ////////////////////////////////////////////////////// - " for(int i = 0; i < PIXEL_PER_SIFT; ++i){\n" - " buf = texture2DRect(tex2, tpos.yw);\n" - " data1 = texture2DRect(tex1, tpos.xz);\n" - " val += (data1 * buf);\n" - " tpos.xy = tpos.xy + vec2(1.0, 1.0);\n" - " }\n" - " const float factor = 0.248050689697265625; \n" - " gl_FragColor =vec4(dot(val, vec4(factor)), index, 0);\n" - "}" - << '\0'; - - s_multiply = program= new ProgramGLSL(out.str().c_str()); - - _param_multiply_tex1 = glGetUniformLocation(*program, "tex1"); - _param_multiply_tex2 = glGetUniformLocation(*program, "tex2"); - _param_multiply_size = glGetUniformLocation(*program, "size"); - - out.seekp(ios::beg); - if(GlobalUtil::_IsNvidia) - out << "#pragma optionNV(ifcvt none)\n" - "#pragma optionNV(unroll all)\n"; - - out << "#define SIFT_PER_STRIPE " << _sift_per_stripe << ".0\n" - "#define PIXEL_PER_SIFT " << _pixel_per_sift << "\n" - "uniform sampler2DRect tex1, tex2;\n" - "uniform sampler2DRect texL1;\n" - "uniform sampler2DRect texL2; \n" - "uniform mat3 H; \n" - "uniform mat3 F; \n" - "uniform vec4 size; \n" - "void main() \n" - "{\n" - << " vec4 val = vec4(0.0, 0.0, 0.0, 0.0), data1, buf;\n" - " vec2 index = gl_FragCoord.yx; \n" - " vec2 stripe_size = size.xy * SIFT_PER_STRIPE;\n" - " vec2 temp_div1 = index / stripe_size;\n" - " vec2 stripe_index = floor(temp_div1);\n" - " index = floor(stripe_size * (temp_div1 - stripe_index));\n" - " vec2 temp_div2 = index * vec2(1.0/ float(SIFT_PER_STRIPE));\n" - " vec2 temp_floor2 = floor(temp_div2);\n" - " vec2 index_v = temp_floor2 + vec2(0.5);\n " - " vec2 index_h = vec2(SIFT_PER_STRIPE)* (temp_div2 - temp_floor2);\n" - - //read feature location data - " vec4 tlpos = vec4((index_h + stripe_index * vec2(SIFT_PER_STRIPE)) + 0.5, index_v);\n" - " vec3 loc1 = vec3(texture2DRect(texL1, tlpos.xz).xw, 1.0);\n" - " vec3 loc2 = vec3(texture2DRect(texL2, tlpos.yw).xw, 1.0);\n" - - //check the guiding homography - " vec3 hxloc1 = H* loc1;\n" - " vec2 diff = abs(loc2.xy- (hxloc1.xy/hxloc1.z));\n" - " float disth = max(diff.x, diff.y);\n" - " if(disth > size.z ) {gl_FragColor = vec4(0.0, index, 0.0); return;}\n" - - //check the guiding fundamental - " vec3 fx1 = (F * loc1), ftx2 = (loc2 * F);\n" - " float x2tfx1 = dot(loc2, fx1);\n" - " vec4 temp = vec4(fx1.xy, ftx2.xy); \n" - " float sampson_error = (x2tfx1 * x2tfx1) / dot(temp, temp);\n" - " if(sampson_error > size.w) {gl_FragColor = vec4(0.0, index, 0.0); return;}\n" - - //compare feature descriptor - " vec2 tx = (index_h + stripe_index * SIFT_PER_STRIPE)* vec2(PIXEL_PER_SIFT) + 0.5;\n" - " vec2 tpos1, tpos2; \n" - " vec4 tpos = vec4(tx, index_v);\n" - " for(int i = 0; i < PIXEL_PER_SIFT; ++i){\n" - " buf = texture2DRect(tex2, tpos.yw);\n" - " data1 = texture2DRect(tex1, tpos.xz);\n" - " val += data1 * buf;\n" - " tpos.xy = tpos.xy + vec2(1.0, 1.0);\n" - " }\n" - " const float factor = 0.248050689697265625; \n" - " gl_FragColor =vec4(dot(val, vec4(factor)), index, 0.0);\n" - "}" - << '\0'; - - s_guided_mult = program= new ProgramGLSL(out.str().c_str()); - - _param_guided_mult_tex1 = glGetUniformLocation(*program, "tex1"); - _param_guided_mult_tex2= glGetUniformLocation(*program, "tex2"); - _param_guided_mult_texl1 = glGetUniformLocation(*program, "texL1"); - _param_guided_mult_texl2 = glGetUniformLocation(*program, "texL2"); - _param_guided_mult_h = glGetUniformLocation(*program, "H"); - _param_guided_mult_f = glGetUniformLocation(*program, "F"); - _param_guided_mult_param = glGetUniformLocation(*program, "size"); - - //row max - out.seekp(ios::beg); - out << "#define BLOCK_WIDTH 16.0\n" - "uniform sampler2DRect tex; uniform vec3 param;\n" - "void main ()\n" - "{\n" - " float index = gl_FragCoord.x + floor(gl_FragCoord.y) * BLOCK_WIDTH; \n" - " vec2 bestv = vec2(-1.0); float imax = -1.0;\n" - " for(float i = 0.0; i < param.x; i ++){\n " - " float v = texture2DRect(tex, vec2(i + 0.5, index)).r; \n" - " imax = v > bestv.r ? i : imax; \n " - " bestv = v > bestv.r? vec2(v, bestv.r) : max(bestv, vec2(v));\n " - " }\n" - " bestv = acos(min(bestv, 1.0));\n" - " if(bestv.x >= param.y || bestv.x >= param.z * bestv.y) imax = -1.0;\n" - " gl_FragColor = vec4(imax, bestv, index);\n" - "}" - << '\0'; - s_row_max = program= new ProgramGLSL(out.str().c_str()); - _param_rowmax_param = glGetUniformLocation(*program, "param"); - - out.seekp(ios::beg); - out << "#define BLOCK_WIDTH 16.0\n" - "uniform sampler2DRect tex; uniform vec3 param;\n" - "void main ()\n" - "{\n" - " float index = gl_FragCoord.x + floor(gl_FragCoord.y) * BLOCK_WIDTH; \n" - " vec2 bestv = vec2(-1.0); float imax = -1.0;\n" - " for(float i = 0.0; i < param.x; i ++){\n " - " float v = texture2DRect(tex, vec2(index, i + 0.5)).r; \n" - " imax = (v > bestv.r)? i : imax; \n " - " bestv = v > bestv.r? vec2(v, bestv.r) : max(bestv, vec2(v));\n " - " }\n" - " bestv = acos(min(bestv, 1.0));\n" - " if(bestv.x >= param.y || bestv.x >= param.z * bestv.y) imax = -1.0;\n" - " gl_FragColor = vec4(imax, bestv, index);\n" - "}" - << '\0'; - s_col_max = program =new ProgramGLSL(out.str().c_str()); - _param_colmax_param = glGetUniformLocation(*program, "param"); - - -} - -int SiftMatchGL::GetGuidedSiftMatch(int max_match, int match_buffer[][2], float H[3][3], float F[3][3], - float distmax, float ratiomax, float hdistmax, float fdistmax, int mbm) -{ - - int dw = _num_sift[1]; - int dh = _num_sift[0]; - if(_initialized ==0) return 0; - if(dw <= 0 || dh <=0) return 0; - if(_have_loc[0] == 0 || _have_loc[1] == 0) return 0; - - FrameBufferObject fbo; - glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); - _texDot.SetImageSize(dw, dh); - - - //data - _texDot.AttachToFBO(0); - _texDot.FitTexViewPort(); - glActiveTexture(GL_TEXTURE0); - _texDes[0].BindTex(); - glActiveTexture(GL_TEXTURE1); - _texDes[1].BindTex(); - glActiveTexture(GL_TEXTURE2); - _texLoc[0].BindTex(); - glActiveTexture(GL_TEXTURE3); - _texLoc[1].BindTex(); - - //multiply the descriptor matrices - s_guided_mult->UseProgram(); - - - //set parameters glsl - float dot_param[4] = {(float)_texDes[0].GetDrawHeight(), (float) _texDes[1].GetDrawHeight(), hdistmax, fdistmax}; - glUniform1i(_param_guided_mult_tex1, 0); - glUniform1i(_param_guided_mult_tex2, 1); - glUniform1i(_param_guided_mult_texl1, 2); - glUniform1i(_param_guided_mult_texl2, 3); - glUniformMatrix3fv(_param_guided_mult_h, 1, GL_TRUE, H[0]); - glUniformMatrix3fv(_param_guided_mult_f, 1, GL_TRUE, F[0]); - glUniform4fv(_param_guided_mult_param, 1, dot_param); - - _texDot.DrawQuad(); - - GLTexImage::UnbindMultiTex(4); - - return GetBestMatch(max_match, match_buffer, distmax, ratiomax, mbm); -} - -int SiftMatchGL::GetBestMatch(int max_match, int match_buffer[][2], float distmax, float ratiomax, int mbm) -{ - - glActiveTexture(GL_TEXTURE0); - _texDot.BindTex(); - - //readback buffer - sift_buffer.resize(_num_sift[0] + _num_sift[1] + 16); - float * buffer1 = &sift_buffer[0], * buffer2 = &sift_buffer[_num_sift[0]]; - - //row max - _texMatch[0].AttachToFBO(0); - _texMatch[0].SetImageSize(16, ( _num_sift[0] + 15) / 16); - _texMatch[0].FitTexViewPort(); - - ///set parameter glsl - s_row_max->UseProgram(); - glUniform3f(_param_rowmax_param, (float)_num_sift[1], distmax, ratiomax); - - _texMatch[0].DrawQuad(); - glReadPixels(0, 0, 16, (_num_sift[0] + 15)/16, GL_RED, GL_FLOAT, buffer1); - - //col max - if(mbm) - { - _texMatch[1].AttachToFBO(0); - _texMatch[1].SetImageSize(16, (_num_sift[1] + 15) / 16); - _texMatch[1].FitTexViewPort(); - //set parameter glsl - s_col_max->UseProgram(); - glUniform3f(_param_rowmax_param, (float)_num_sift[0], distmax, ratiomax); - _texMatch[1].DrawQuad(); - glReadPixels(0, 0, 16, (_num_sift[1] + 15) / 16, GL_RED, GL_FLOAT, buffer2); - } - - - //unload - glUseProgram(0); - - GLTexImage::UnbindMultiTex(2); - GlobalUtil::CleanupOpenGL(); - - //write back the matches - int nmatch = 0, j ; - for(int i = 0; i < _num_sift[0] && nmatch < max_match; ++i) - { - j = int(buffer1[i]); - if( j>= 0 && (!mbm ||int(buffer2[j]) == i)) - { - match_buffer[nmatch][0] = i; - match_buffer[nmatch][1] = j; - nmatch++; - } - } - return nmatch; -} - -int SiftMatchGL::GetSiftMatch(int max_match, int match_buffer[][2], float distmax, float ratiomax, int mbm) -{ - int dw = _num_sift[1]; - int dh = _num_sift[0]; - if(_initialized ==0) return 0; - if(dw <= 0 || dh <=0) return 0; - - FrameBufferObject fbo; - glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); - _texDot.SetImageSize(dw, dh); - - //data - _texDot.AttachToFBO(0); - _texDot.FitTexViewPort(); - glActiveTexture(GL_TEXTURE0); - _texDes[0].BindTex(); - glActiveTexture(GL_TEXTURE1); - _texDes[1].BindTex(); - - ////////////////// - //multiply the descriptor matrices - s_multiply->UseProgram(); - //set parameters - float heights[2] = {(float)_texDes[0].GetDrawHeight(), (float)_texDes[1].GetDrawHeight()}; - - glUniform1i(_param_multiply_tex1, 0); - glUniform1i(_param_multiply_tex2 , 1); - glUniform2fv(_param_multiply_size, 1, heights); - - _texDot.DrawQuad(); - - glActiveTexture(GL_TEXTURE1); - glBindTexture(GlobalUtil::_texTarget, 0); - - return GetBestMatch(max_match, match_buffer, distmax, ratiomax, mbm); -} - - -int SiftMatchGPU::_CreateContextGL() -{ - //Create an OpenGL Context? - if (__language >= SIFTMATCH_CUDA) {} - else if(!GlobalUtil::CreateWindowEZ()) - { -#if CUDA_SIFTGPU_ENABLED - __language = SIFTMATCH_CUDA; -#else - return 0; -#endif - } - return VerifyContextGL(); -} - - -int SiftMatchGPU::_VerifyContextGL() -{ - if(__matcher) return GlobalUtil::_GoodOpenGL; - -#ifdef CUDA_SIFTGPU_ENABLED - - if(__language >= SIFTMATCH_CUDA) {} - else if(__language == SIFTMATCH_SAME_AS_SIFTGPU && GlobalUtil::_UseCUDA){} - else GlobalUtil::InitGLParam(0); - if(GlobalUtil::_GoodOpenGL == 0) __language = SIFTMATCH_CUDA; - - if(((__language == SIFTMATCH_SAME_AS_SIFTGPU && GlobalUtil::_UseCUDA) || __language >= SIFTMATCH_CUDA) - && SiftMatchCU::CheckCudaDevice (GlobalUtil::_DeviceIndex)) - { - __language = SIFTMATCH_CUDA; - __matcher = new SiftMatchCU(__max_sift); - }else -#else - if((__language == SIFTMATCH_SAME_AS_SIFTGPU && GlobalUtil::_UseCUDA) || __language >= SIFTMATCH_CUDA) - { - std::cerr << "---------------------------------------------------------------------------\n" - << "CUDA not supported in this binary! To enable it, please use SiftGPU_CUDA_Enable\n" - << "Project for VS2005+ or set siftgpu_enable_cuda to 1 in makefile\n" - << "----------------------------------------------------------------------------\n"; - } -#endif - { - __language = SIFTMATCH_GLSL; - __matcher = new SiftMatchGL(__max_sift, 1); - } - - if(GlobalUtil::_verbose) - std::cout << "[SiftMatchGPU]: " << (__language == SIFTMATCH_CUDA? "CUDA" : "GLSL") <<"\n\n"; - - __matcher->InitSiftMatch(); - return GlobalUtil::_GoodOpenGL; -} - -void* SiftMatchGPU::operator new (size_t size){ - void * p = malloc(size); - if (p == 0) - { - const std::bad_alloc ba; - throw ba; - } - return p; -} - - -SiftMatchGPU::SiftMatchGPU(int max_sift) -{ - __max_sift = max(max_sift, 1024); - __language = 0; - __matcher = NULL; -} - -void SiftMatchGPU::SetLanguage(int language) -{ - if(__matcher) return; - //////////////////////// -#ifdef CUDA_SIFTGPU_ENABLED - if(language >= SIFTMATCH_CUDA) GlobalUtil::_DeviceIndex = language - SIFTMATCH_CUDA; -#endif - __language = language > SIFTMATCH_CUDA ? SIFTMATCH_CUDA : language; -} - -void SiftMatchGPU::SetDeviceParam(int argc, char**argv) -{ - if(__matcher) return; - GlobalUtil::SetDeviceParam(argc, argv); -} - -void SiftMatchGPU::SetMaxSift(int max_sift) -{ - if(__matcher) __matcher->SetMaxSift(max(128, max_sift)); - else __max_sift = max(128, max_sift); -} - -SiftMatchGPU::~SiftMatchGPU() -{ - if(__matcher) delete __matcher; -} - -void SiftMatchGPU::SetDescriptors(int index, int num, const unsigned char* descriptors, int id) -{ - __matcher->SetDescriptors(index, num, descriptors, id); -} - -void SiftMatchGPU::SetDescriptors(int index, int num, const float* descriptors, int id) -{ - __matcher->SetDescriptors(index, num, descriptors, id); -} - -void SiftMatchGPU::SetFeautreLocation(int index, const float* locations, int gap) -{ - __matcher->SetFeautreLocation(index, locations, gap); - -} -int SiftMatchGPU::GetGuidedSiftMatch(int max_match, int match_buffer[][2], float H[3][3], float F[3][3], - float distmax, float ratiomax, float hdistmax, float fdistmax, int mutual_best_match) -{ - if(H == NULL && F == NULL) - { - return __matcher->GetSiftMatch(max_match, match_buffer, distmax, ratiomax, mutual_best_match); - }else - { - float Z[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}, ti = (1.0e+20F); - - return __matcher->GetGuidedSiftMatch(max_match, match_buffer, H? H : Z, F? F : Z, - distmax, ratiomax, H? hdistmax: ti, F? fdistmax: ti, mutual_best_match); - } -} - -int SiftMatchGPU::GetSiftMatch(int max_match, int match_buffer[][2], float distmax, float ratiomax, int mutual_best_match) -{ - return __matcher->GetSiftMatch(max_match, match_buffer, distmax, ratiomax, mutual_best_match); -} - -SiftMatchGPU* CreateNewSiftMatchGPU(int max_sift) -{ - return new SiftMatchGPU(max_sift); -} - diff --git a/3rdparty/SiftGPU/src/SiftGPU/SiftMatch.h b/3rdparty/SiftGPU/src/SiftGPU/SiftMatch.h deleted file mode 100644 index 5f913e7f..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/SiftMatch.h +++ /dev/null @@ -1,93 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: SiftMatch.h -// Author: Changchang Wu -// Description : interface for the SiftMatchGL -//// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - - -#ifndef GPU_SIFT_MATCH_H -#define GPU_SIFT_MATCH_H -class GLTexImage; -class ProgramGPU; - -class SiftMatchGL:public SiftMatchGPU -{ - typedef GLint ParameterGL; -private: - //tex storage - GLTexImage _texLoc[2]; - GLTexImage _texDes[2]; - GLTexImage _texDot; - GLTexImage _texMatch[2]; - - //programs - ProgramGPU * s_multiply; - ProgramGPU * s_guided_mult; - ProgramGPU * s_col_max; - ProgramGPU * s_row_max; - - //matching parameters - ParameterGL _param_multiply_tex1; - ParameterGL _param_multiply_tex2; - ParameterGL _param_multiply_size; - ParameterGL _param_rowmax_param; - ParameterGL _param_colmax_param; - - ///guided matching - ParameterGL _param_guided_mult_tex1; - ParameterGL _param_guided_mult_tex2; - ParameterGL _param_guided_mult_texl1; - ParameterGL _param_guided_mult_texl2; - ParameterGL _param_guided_mult_h; - ParameterGL _param_guided_mult_f; - ParameterGL _param_guided_mult_param; - // - int _max_sift; - int _num_sift[2]; - int _id_sift[2]; - int _have_loc[2]; - - //gpu parameter - int _sift_per_stripe; - int _sift_num_stripe; - int _sift_per_row; - int _pixel_per_sift; - int _initialized; - // - vector<float> sift_buffer; -private: - void AllocateSiftMatch(); - void LoadSiftMatchShadersGLSL(); - int GetBestMatch(int max_match, int match_buffer[][2], float distmax, float ratiomax, int mbm); -public: - SiftMatchGL(int max_sift, int use_glsl); - virtual ~SiftMatchGL(); -public: - void InitSiftMatch(); - void SetMaxSift(int max_sift); - void SetDescriptors(int index, int num, const unsigned char * descriptor, int id = -1); - void SetDescriptors(int index, int num, const float * descriptor, int id = -1); - void SetFeautreLocation(int index, const float* locatoins, int gap); - int GetSiftMatch(int max_match, int match_buffer[][2], float distmax, float ratiomax, int mbm); - int GetGuidedSiftMatch(int max_match, int match_buffer[][2], float H[3][3], float F[3][3], - float distmax, float ratiomax, float hdistmax,float fdistmax, int mbm); -}; - - -#endif - diff --git a/3rdparty/SiftGPU/src/SiftGPU/SiftMatchCU.cpp b/3rdparty/SiftGPU/src/SiftGPU/SiftMatchCU.cpp deleted file mode 100644 index 77d333e8..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/SiftMatchCU.cpp +++ /dev/null @@ -1,176 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: SiftMatchCU.cpp -// Author: Changchang Wu -// Description : implementation of the SiftMatchCU class. -// CUDA-based implementation of SiftMatch -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - -#if defined(CUDA_SIFTGPU_ENABLED) - -#include "GL/glew.h" -#include <iostream> -#include <vector> -#include <algorithm> -#include <stdlib.h> -#include <math.h> -using namespace std; - - -#include "GlobalUtil.h" -#include "CuTexImage.h" -#include "SiftGPU.h" -#include "ProgramCU.h" -#include "SiftMatchCU.h" - - -SiftMatchCU::SiftMatchCU(int max_sift):SiftMatchGPU() -{ - _num_sift[0] = _num_sift[1] = 0; - _id_sift[0] = _id_sift[1] = 0; - _have_loc[0] = _have_loc[1] = 0; - _max_sift = max_sift <=0 ? 4096 : ((max_sift + 31)/ 32 * 32) ; - _initialized = 0; -} - -void SiftMatchCU::SetMaxSift(int max_sift) -{ - max_sift = ((max_sift + 31)/32)*32; - if(max_sift > GlobalUtil::_texMaxDimGL) max_sift = GlobalUtil::_texMaxDimGL; - _max_sift = max_sift; -} - - -int SiftMatchCU::CheckCudaDevice(int device) -{ - return ProgramCU::CheckCudaDevice(device); -} - -void SiftMatchCU::InitSiftMatch() -{ - if(_initialized) return; - GlobalUtil::_GoodOpenGL = max(GlobalUtil::_GoodOpenGL, 1); - _initialized = 1; -} - - -void SiftMatchCU::SetDescriptors(int index, int num, const unsigned char* descriptors, int id) -{ - if(_initialized == 0) return; - if (index > 1) index = 1; - if (index < 0) index = 0; - _have_loc[index] = 0; - //the same feature is already set - if(id !=-1 && id == _id_sift[index]) return ; - _id_sift[index] = id; - if(num > _max_sift) num = _max_sift; - _num_sift[index] = num; - _texDes[index].InitTexture(8 * num, 1, 4); - _texDes[index].CopyFromHost((void*)descriptors); -} - - -void SiftMatchCU::SetDescriptors(int index, int num, const float* descriptors, int id) -{ - if(_initialized == 0) return; - if (index > 1) index = 1; - if (index < 0) index = 0; - if(num > _max_sift) num = _max_sift; - - sift_buffer.resize(num * 128 /4); - unsigned char * pub = (unsigned char*) &sift_buffer[0]; - for(int i = 0; i < 128 * num; ++i) - { - pub[i] = int(512 * descriptors[i] + 0.5); - } - SetDescriptors(index, num, pub, id); -} - - -void SiftMatchCU::SetFeautreLocation(int index, const float* locations, int gap) -{ - if(_num_sift[index] <=0) return; - _texLoc[index].InitTexture(_num_sift[index], 1, 2); - if(gap == 0) - { - _texLoc[index].CopyFromHost(locations); - }else - { - sift_buffer.resize(_num_sift[index] * 2); - float* pbuf = (float*) (&sift_buffer[0]); - for(int i = 0; i < _num_sift[index]; ++i) - { - pbuf[i*2] = *locations++; - pbuf[i*2+1]= *locations ++; - locations += gap; - } - _texLoc[index].CopyFromHost(pbuf); - } - _have_loc[index] = 1; -} - -int SiftMatchCU::GetGuidedSiftMatch(int max_match, int match_buffer[][2], float H[3][3], float F[3][3], - float distmax, float ratiomax, float hdistmax, float fdistmax, int mbm) -{ - - if(_initialized ==0) return 0; - if(_num_sift[0] <= 0 || _num_sift[1] <=0) return 0; - if(_have_loc[0] == 0 || _have_loc[1] == 0) return 0; - ProgramCU::MultiplyDescriptorG(_texDes, _texDes+1, _texLoc, _texLoc + 1, - &_texDot, (mbm? &_texCRT: NULL), H, hdistmax, F, fdistmax); - return GetBestMatch(max_match, match_buffer, distmax, ratiomax, mbm); -} - - -int SiftMatchCU::GetSiftMatch(int max_match, int match_buffer[][2], float distmax, float ratiomax, int mbm) -{ - if(_initialized ==0) return 0; - if(_num_sift[0] <= 0 || _num_sift[1] <=0) return 0; - ProgramCU::MultiplyDescriptor(_texDes, _texDes + 1, &_texDot, (mbm? &_texCRT: NULL)); - return GetBestMatch(max_match, match_buffer, distmax, ratiomax, mbm); -} - - -int SiftMatchCU::GetBestMatch(int max_match, int match_buffer[][2], float distmax, float ratiomax, int mbm) -{ - sift_buffer.resize(_num_sift[0] + _num_sift[1]); - int * buffer1 = (int*) &sift_buffer[0], * buffer2 = (int*) &sift_buffer[_num_sift[0]]; - _texMatch[0].InitTexture(_num_sift[0], 1); - ProgramCU::GetRowMatch(&_texDot, _texMatch, distmax, ratiomax); - _texMatch[0].CopyToHost(buffer1); - if(mbm) - { - _texMatch[1].InitTexture(_num_sift[1], 1); - ProgramCU::GetColMatch(&_texCRT, _texMatch + 1, distmax, ratiomax); - _texMatch[1].CopyToHost(buffer2); - } - int nmatch = 0, j ; - for(int i = 0; i < _num_sift[0] && nmatch < max_match; ++i) - { - j = int(buffer1[i]); - if( j>= 0 && (!mbm ||int(buffer2[j]) == i)) - { - match_buffer[nmatch][0] = i; - match_buffer[nmatch][1] = j; - nmatch++; - } - } - return nmatch; -} - -#endif - diff --git a/3rdparty/SiftGPU/src/SiftGPU/SiftMatchCU.h b/3rdparty/SiftGPU/src/SiftGPU/SiftMatchCU.h deleted file mode 100644 index 626e814b..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/SiftMatchCU.h +++ /dev/null @@ -1,68 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: SiftMatchCU.h -// Author: Changchang Wu -// Description : interface for the SiftMatchCU -//// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - - - -#ifndef CU_SIFT_MATCH_H -#define CU_SIFT_MATCH_H -#if defined(CUDA_SIFTGPU_ENABLED) - -class CuTexImage; -class SiftMatchCU:public SiftMatchGPU -{ -private: - //tex storage - CuTexImage _texLoc[2]; - CuTexImage _texDes[2]; - CuTexImage _texDot; - CuTexImage _texMatch[2]; - CuTexImage _texCRT; - - //programs - // - int _max_sift; - int _num_sift[2]; - int _id_sift[2]; - int _have_loc[2]; - - //gpu parameter - int _initialized; - vector<int> sift_buffer; -private: - int GetBestMatch(int max_match, int match_buffer[][2], float distmax, float ratiomax, int mbm); -public: - SiftMatchCU(int max_sift); - virtual ~SiftMatchCU(){}; - void InitSiftMatch(); - void SetMaxSift(int max_sift); - void SetDescriptors(int index, int num, const unsigned char * descriptor, int id = -1); - void SetDescriptors(int index, int num, const float * descriptor, int id = -1); - void SetFeautreLocation(int index, const float* locatoins, int gap); - int GetSiftMatch(int max_match, int match_buffer[][2], float distmax, float ratiomax, int mbm); - int GetGuidedSiftMatch(int max_match, int match_buffer[][2], float H[3][3], float F[3][3], - float distmax, float ratiomax, float hdistmax, float fdistmax, int mbm); - ////////////////////////////// - static int CheckCudaDevice(int device); -}; - -#endif -#endif - diff --git a/3rdparty/SiftGPU/src/SiftGPU/SiftPyramid.cpp b/3rdparty/SiftGPU/src/SiftGPU/SiftPyramid.cpp deleted file mode 100644 index ab8766c4..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/SiftPyramid.cpp +++ /dev/null @@ -1,406 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: SiftPyramid.cpp -// Author: Changchang Wu -// Description : Implementation of the SiftPyramid class. -// -// -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - - -#include "GL/glew.h" -#include <string.h> -#include <iostream> -#include <iomanip> -#include <vector> -#include <algorithm> -#include <fstream> -#include <math.h> -using namespace std; - -#include "GlobalUtil.h" -#include "SiftPyramid.h" -#include "SiftGPU.h" - - -#ifdef DEBUG_SIFTGPU -#include "IL/il.h" -#include "direct.h" -#include "io.h" -#include <sys/stat.h> -#endif - - - -void SiftPyramid::RunSIFT(GLTexInput*input) -{ - CleanupBeforeSIFT(); - - if(_existing_keypoints & SIFT_SKIP_FILTERING) - { - - }else - { - GlobalUtil::StartTimer("Build Pyramid"); - BuildPyramid(input); - GlobalUtil::StopTimer(); - _timing[0] = GetElapsedTime(); - } - - - if(_existing_keypoints) - { - //existing keypoint list should at least have the locations and scale - GlobalUtil::StartTimer("Upload Feature List"); - if(!(_existing_keypoints & SIFT_SKIP_FILTERING)) ComputeGradient(); - GenerateFeatureListTex(); - GlobalUtil::StopTimer(); - _timing[2] = GetElapsedTime(); - }else - { - - GlobalUtil::StartTimer("Detect Keypoints"); - DetectKeypointsEX(); - GlobalUtil::StopTimer(); - _timing[1] = GetElapsedTime(); - - if(GlobalUtil::_ListGenGPU ==1) - { - GlobalUtil::StartTimer("Get Feature List"); - GenerateFeatureList(); - GlobalUtil::StopTimer(); - - }else - { - GlobalUtil::StartTimer("Transfer Feature List"); - GenerateFeatureListCPU(); - GlobalUtil::StopTimer(); - } - LimitFeatureCount(0); - _timing[2] = GetElapsedTime(); - } - - - - if(_existing_keypoints& SIFT_SKIP_ORIENTATION) - { - //use exisitng feature orientation or - }else if(GlobalUtil::_MaxOrientation>0) - { - //some extra tricks are done to handle existing keypoint list - GlobalUtil::StartTimer("Feature Orientations"); - GetFeatureOrientations(); - GlobalUtil::StopTimer(); - _timing[3] = GetElapsedTime(); - - //for existing keypoint list, only the strongest orientation is kept. - if(GlobalUtil::_MaxOrientation >1 && !_existing_keypoints && !GlobalUtil::_FixedOrientation) - { - GlobalUtil::StartTimer("MultiO Feature List"); - ReshapeFeatureListCPU(); - LimitFeatureCount(1); - GlobalUtil::StopTimer(); - _timing[4] = GetElapsedTime(); - } - }else - { - GlobalUtil::StartTimer("Feature Orientations"); - GetSimplifiedOrientation(); - GlobalUtil::StopTimer(); - _timing[3] = GetElapsedTime(); - } - - PrepareBuffer(); - - if(_existing_keypoints & SIFT_SKIP_ORIENTATION) - { - //no need to read back feature if all fields of keypoints are already specified - }else - { - GlobalUtil::StartTimer("Download Keypoints"); -#ifdef NO_DUPLICATE_DOWNLOAD - if(GlobalUtil::_MaxOrientation < 2 || GlobalUtil::_FixedOrientation) -#endif - DownloadKeypoints(); - GlobalUtil::StopTimer(); - _timing[5] = GetElapsedTime(); - } - - - - if(GlobalUtil::_DescriptorPPT) - { - //desciprotrs are downloaded in descriptor computation of each level - GlobalUtil::StartTimer("Get Descriptor"); - GetFeatureDescriptors(); - GlobalUtil::StopTimer(); - _timing[6] = GetElapsedTime(); - } - - //reset the existing keypoints - _existing_keypoints = 0; - _keypoint_index.resize(0); - - if(GlobalUtil::_UseSiftGPUEX) - { - GlobalUtil::StartTimer("Gen. Display VBO"); - GenerateFeatureDisplayVBO(); - GlobalUtil::StopTimer(); - _timing[7] = GlobalUtil::GetElapsedTime(); - } - //clean up - CleanUpAfterSIFT(); -} - - -void SiftPyramid::LimitFeatureCount(int have_keylist) -{ - - if(GlobalUtil::_FeatureCountThreshold <= 0 || _existing_keypoints) return; - /////////////////////////////////////////////////////////////// - //skip the lowest levels to reduce number of features. - - if(GlobalUtil::_TruncateMethod == 2) - { - int i = 0, new_feature_num = 0, level_num = param._dog_level_num * _octave_num; - for(; new_feature_num < _FeatureCountThreshold && i < level_num; ++i) new_feature_num += _levelFeatureNum[i]; - for(; i < level_num; ++i) _levelFeatureNum[i] = 0; - - if(new_feature_num < _featureNum) - { - _featureNum = new_feature_num; - if(GlobalUtil::_verbose ) - { - std::cout<<"#Features Reduced:\t"<<_featureNum<<endl; - } - } - }else - { - int i = 0, num_to_erase = 0; - while(_featureNum - _levelFeatureNum[i] > _FeatureCountThreshold) - { - num_to_erase += _levelFeatureNum[i]; - _featureNum -= _levelFeatureNum[i]; - _levelFeatureNum[i++] = 0; - } - if(num_to_erase > 0 && have_keylist) - { - _keypoint_buffer.erase(_keypoint_buffer.begin(), _keypoint_buffer.begin() + num_to_erase * 4); - } - if(GlobalUtil::_verbose && num_to_erase > 0) - { - std::cout<<"#Features Reduced:\t"<<_featureNum<<endl; - } - } - - -} - -void SiftPyramid::PrepareBuffer() -{ - //when there is no existing keypoint list, the feature list need to be downloaded - //when an existing keypoint list does not have orientaiton, we need to download them again. - if(!(_existing_keypoints & SIFT_SKIP_ORIENTATION)) - { - //_keypoint_buffer.resize(4 * (_featureNum +align)); - _keypoint_buffer.resize(4 * (_featureNum + GlobalUtil::_texMaxDim)); //11/19/2008 - } - if(GlobalUtil::_DescriptorPPT) - { - //_descriptor_buffer.resize(128*(_featureNum + align)); - _descriptor_buffer.resize(128 * _featureNum + 16 * GlobalUtil::_texMaxDim);//11/19/2008 - } - -} - -int SiftPyramid:: GetRequiredOctaveNum(int inputsz) -{ - //[2 ^ i, 2 ^ (i + 1)) -> i - 3... - //768 in [2^9, 2^10) -> 6 -> smallest will be 768 / 32 = 24 - int num = (int) floor (log ( inputsz * 2.0 / GlobalUtil::_texMinDim )/log(2.0)); - return num <= 0 ? 1 : num; -} - -void SiftPyramid::CopyFeatureVector(float*keys, float *descriptors) -{ - if(keys) memcpy(keys, &_keypoint_buffer[0], 4*_featureNum*sizeof(float)); - if(descriptors) memcpy(descriptors, &_descriptor_buffer[0], 128*_featureNum*sizeof(float)); -} - -void SiftPyramid:: SetKeypointList(int num, const float * keys, int run_on_current, int skip_orientation) -{ - //for each input keypoint - //sort the key point list by size, and assign them to corresponding levels - if(num <=0) return; - _featureNum = num; - ///copy the keypoints - _keypoint_buffer.resize(num * 4); - memcpy(&_keypoint_buffer[0], keys, 4 * num * sizeof(float)); - //location and scale can be skipped - _existing_keypoints = SIFT_SKIP_DETECTION; - //filtering is skipped if it is running on the same image - if(run_on_current) _existing_keypoints |= SIFT_SKIP_FILTERING; - //orientation can be skipped if specified - if(skip_orientation) _existing_keypoints |= SIFT_SKIP_ORIENTATION; - //hacking parameter for using rectangle description mode - if(skip_orientation == -1) _existing_keypoints |= SIFT_RECT_DESCRIPTION; -} - - -void SiftPyramid::SaveSIFT(const char * szFileName) -{ - if (_featureNum <=0) return; - float * pk = &_keypoint_buffer[0]; - - if(GlobalUtil::_BinarySIFT) - { - std::ofstream out(szFileName, ios::binary); - out.write((char* )(&_featureNum), sizeof(int)); - - if(GlobalUtil::_DescriptorPPT) - { - int dim = 128; - out.write((char* )(&dim), sizeof(int)); - float * pd = &_descriptor_buffer[0] ; - for(int i = 0; i < _featureNum; i++, pk+=4, pd +=128) - { - out.write((char* )(pk +1), sizeof(float)); - out.write((char* )(pk), sizeof(float)); - out.write((char* )(pk+2), 2 * sizeof(float)); - out.write((char* )(pd), 128 * sizeof(float)); - } - }else - { - int dim = 0; - out.write((char* )(&dim), sizeof(int)); - for(int i = 0; i < _featureNum; i++, pk+=4) - { - out.write((char* )(pk +1), sizeof(float)); - out.write((char* )(pk), sizeof(float)); - out.write((char* )(pk+2), 2 * sizeof(float)); - } - } - }else - { - std::ofstream out(szFileName); - out.flags(ios::fixed); - - if(GlobalUtil::_DescriptorPPT) - { - float * pd = &_descriptor_buffer[0] ; - out<<_featureNum<<" 128"<<endl; - - for(int i = 0; i < _featureNum; i++) - { - //in y, x, scale, orientation order - out<<setprecision(2) << pk[1]<<" "<<setprecision(2) << pk[0]<<" " - <<setprecision(3) << pk[2]<<" " <<setprecision(3) << pk[3]<< endl; - - ////out << setprecision(12) << pk[1] << " " << pk[0] << " " << pk[2] << " " << pk[3] << endl; - pk+=4; - for(int k = 0; k < 128; k ++, pd++) - { - if(GlobalUtil::_NormalizedSIFT) - out<< ((unsigned int)floor(0.5+512.0f*(*pd)))<<" "; - else - out << setprecision(8) << pd[0] << " "; - - if ( (k+1)%20 == 0 ) out<<endl; //suggested by Martin Schneider - - } - out<<endl; - - } - - }else - { - out<<_featureNum<<" 0"<<endl; - for(int i = 0; i < _featureNum; i++, pk+=4) - { - out<<pk[1]<<" "<<pk[0]<<" "<<pk[2]<<" " << pk[3]<<endl; - } - } - } -} - -#ifdef DEBUG_SIFTGPU -void SiftPyramid::BeginDEBUG(const char *imagepath) -{ - if(imagepath && imagepath[0]) - { - strcpy(_debug_path, imagepath); - strcat(_debug_path, ".debug"); - }else - { - strcpy(_debug_path, ".debug"); - } - - mkdir(_debug_path); - chmod(_debug_path, _S_IREAD | _S_IWRITE); -} - -void SiftPyramid::StopDEBUG() -{ - _debug_path[0] = 0; -} - - -void SiftPyramid::WriteTextureForDEBUG(GLTexImage * tex, const char *namet, ...) -{ - char name[_MAX_PATH]; - char * p = name, * ps = _debug_path; - while(*ps) *p++ = *ps ++; - *p++ = '/'; - va_list marker; - va_start(marker, namet); - vsprintf(p, namet, marker); - va_end(marker); - unsigned int imID; - int width = tex->GetImgWidth(); - int height = tex->GetImgHeight(); - float* buffer1 = new float[ width * height * 4]; - float* buffer2 = new float[ width * height * 4]; - - //read data back - glReadBuffer(GL_COLOR_ATTACHMENT0_EXT); - tex->AttachToFBO(0); - tex->FitTexViewPort(); - glReadPixels(0, 0, width, height, GL_RGBA , GL_FLOAT, buffer1); - - //Tiffs saved with IL are flipped - for(int i = 0; i < height; i++) - { - memcpy(buffer2 + i * width * 4, - buffer1 + (height - i - 1) * width * 4, - width * 4 * sizeof(float)); - } - - //save data as floating point tiff file - ilGenImages(1, &imID); - ilBindImage(imID); - ilEnable(IL_FILE_OVERWRITE); - ilTexImage(width, height, 1, 4, IL_RGBA, IL_FLOAT, buffer2); - ilSave(IL_TIF, name); - ilDeleteImages(1, &imID); - - delete buffer1; - delete buffer2; - glReadBuffer(GL_NONE); -} - - -#endif diff --git a/3rdparty/SiftGPU/src/SiftGPU/SiftPyramid.h b/3rdparty/SiftGPU/src/SiftGPU/SiftPyramid.h deleted file mode 100644 index 0b9c58b2..00000000 --- a/3rdparty/SiftGPU/src/SiftGPU/SiftPyramid.h +++ /dev/null @@ -1,190 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: SiftPyramid.h -// Author: Changchang Wu -// Description : interface for the SiftPyramid class. -// SiftPyramid: data storage for SIFT -// |---PyramidGL: OpenGL based implementation -// | |--PyramidNaive: Unpacked version -// | |--PyramidPacked: packed version -// |--PyramidCU: CUDA-based implementation -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - - - -#ifndef _SIFT_PYRAMID_H -#define _SIFT_PYRAMID_H - - -class GLTexImage; -class GLTexInput; -class SiftParam; -class GlobalUtil; - -///////////////////////////////////////////////////////////////////////////// -//class SiftPyramid -//description: virutal class of SIFT data pyramid -// provides functions for SiftPU to run steps of GPU SIFT -// class PyramidNaive is the first implementation -// class PyramidPacked is a better OpenGL implementation -// class PyramidCU is a CUDA based implementation -///////////////////////////////////////////////////////////////////////////// - -#define NO_DUPLICATE_DOWNLOAD - -class SiftPyramid : public GlobalUtil -{ -public: - enum{ - DATA_GAUSSIAN = 0, - DATA_DOG = 1, - DATA_KEYPOINT = 2, - DATA_GRAD = 3, - DATA_ROT = 4, - DATA_NUM = 5 - }; - enum{ - SIFT_SKIP_FILTERING = 0x01, - SIFT_SKIP_DETECTION = 0x02, - SIFT_SKIP_ORIENTATION = 0x04, - SIFT_RECT_DESCRIPTION = 0x08 - }; -protected: - SiftParam& param; - int _hpLevelNum; - int* _levelFeatureNum; - int _featureNum; - float* _histo_buffer; - //keypoint list - int _existing_keypoints; - vector<int> _keypoint_index; - //display vbo - GLuint* _featureDisplayVBO; - GLuint* _featurePointVBO; -public: - // - float _timing[8]; - //image size related - //first octave - int _octave_min; - //how many octaves - int _octave_num; - //pyramid storage - int _pyramid_octave_num; - int _pyramid_octave_first; - int _pyramid_width; - int _pyramid_height; - int _down_sample_factor; - int _allocated; - int _alignment; - int _siftgpu_failed; -public: - vector<float> _keypoint_buffer; - vector<float> _descriptor_buffer; -private: - inline void PrepareBuffer(); - inline void LimitFeatureCount(int have_keylist = 0); -public: - //shared by all implementations - virtual void RunSIFT(GLTexInput*input); - virtual void SaveSIFT(const char * szFileName); - virtual void CopyFeatureVector(float*keys, float *descriptors); - virtual void SetKeypointList(int num, const float * keys, int run_on_current, int skip_orientation); - //implementation-dependent functions - virtual void GetFeatureDescriptors() = 0; - virtual void GenerateFeatureListTex() =0; - virtual void ReshapeFeatureListCPU() =0; - virtual void GenerateFeatureDisplayVBO() =0; - virtual void DownloadKeypoints() = 0; - virtual void GenerateFeatureListCPU()=0; - virtual void GenerateFeatureList()=0; - virtual GLTexImage* GetLevelTexture(int octave, int level)=0; - virtual GLTexImage* GetLevelTexture(int octave, int level, int dataName) = 0; - virtual void BuildPyramid(GLTexInput * input)=0; - virtual void ResizePyramid(int w, int h) = 0; - virtual void InitPyramid(int w, int h, int ds = 0)=0; - virtual void DetectKeypointsEX() = 0; - virtual void ComputeGradient() = 0; - virtual void GetFeatureOrientations() = 0; - virtual void GetSimplifiedOrientation() = 0; - - //////////////////////////////// - virtual void CleanUpAfterSIFT() {} - virtual int IsUsingRectDescription() {return 0; } - static int GetRequiredOctaveNum(int inputsz); - - ///inline functions, shared by all implementations - inline void SetFailStatus() {_siftgpu_failed = 1; } - inline int GetSucessStatus() {return _siftgpu_failed == 0; } - inline int GetFeatureNum(){return _featureNum;} - inline int GetHistLevelNum(){return _hpLevelNum;} - inline const GLuint * GetFeatureDipslayVBO(){return _featureDisplayVBO;} - inline const GLuint * GetPointDisplayVBO(){return _featurePointVBO;} - inline const int * GetLevelFeatureNum(){return _levelFeatureNum;} - inline void GetPyramidTiming(float * timing){ for(int i = 0; i < 8; i++) timing[i] = _timing[i]; } - inline void CleanupBeforeSIFT() - { - _siftgpu_failed = 0; - for(int i = 0; i < 8; ++i) _timing[i] = 0; - } - SiftPyramid(SiftParam&sp):param(sp) - { - _featureNum = 0; - _featureDisplayVBO = 0; - _featurePointVBO = 0; - _levelFeatureNum = NULL; - _histo_buffer = NULL; - _hpLevelNum = 0; - - //image size - _octave_num = 0; - _octave_min = 0; - _alignment = 1; - _pyramid_octave_num = _pyramid_octave_first = 0; - _pyramid_width = _pyramid_height = 0; - _allocated = 0; - _down_sample_factor = 0; - - ///// - _existing_keypoints = 0; - } - virtual ~SiftPyramid() {}; - -#ifdef DEBUG_SIFTGPU -private: - void StopDEBUG(); - void BeginDEBUG(const char* imagepath); - void WriteTextureForDEBUG(GLTexImage * tex, const char * namet, ...); -#endif -}; - -#define SIFTGPU_ENABLE_REVERSE_ORDER -#ifdef SIFTGPU_ENABLE_REVERSE_ORDER -#define FIRST_OCTAVE(R) (R? _octave_num - 1 : 0) -#define NOT_LAST_OCTAVE(i, R) (R? (i >= 0) : (i < _octave_num)) -#define GOTO_NEXT_OCTAVE(i, R) (R? (--i) : (++i)) -#define FIRST_LEVEL(R) (R? param._dog_level_num - 1 : 0) -#define GOTO_NEXT_LEVEL(j, R) (R? (--j) : (++j)) -#define NOT_LAST_LEVEL(j, R) (R? (j >= 0) : (j < param._dog_level_num)) -#define FOR_EACH_OCTAVE(i, R) for(int i = FIRST_OCTAVE(R); NOT_LAST_OCTAVE(i, R); GOTO_NEXT_OCTAVE(i, R)) -#define FOR_EACH_LEVEL(j, R) for(int j = FIRST_LEVEL(R); NOT_LAST_LEVEL(j, R); GOTO_NEXT_LEVEL(j, R)) -#else -#define FOR_EACH_OCTAVE(i, R) for(int i = 0; i < _octave_num; ++i) -#define FOR_EACH_LEVEL(j, R) for(int j = 0; j < param._dog_level_num; ++j) -#endif - -#endif diff --git a/3rdparty/SiftGPU/src/TestWin/BasicTestWin.cpp b/3rdparty/SiftGPU/src/TestWin/BasicTestWin.cpp deleted file mode 100644 index 57774141..00000000 --- a/3rdparty/SiftGPU/src/TestWin/BasicTestWin.cpp +++ /dev/null @@ -1,322 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: BasicTestWin.cpp -// Author: Changchang Wu -// Description : implementation of the BasicTestWin class. -// -// -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// -#ifdef _WIN32 - #define WIN32_LEAN_AND_MEAN - #include <windows.h> - #define SIFTGPU_DLL - #include <time.h> -#else - #include <sys/time.h> -#endif - -#include "stdlib.h" -#include <iostream> -using std::iostream; - -#ifdef __APPLE__ - #include "OpenGL/OpenGL.h" -#else - #include "GL/gl.h" -#endif - -#include "../SiftGPU/SiftGPU.h" -#include "BasicTestWin.h" - -////////////////////////////////////////////////////////////////////// -// Construction/Destruction -////////////////////////////////////////////////////////////////////// - -BasicTestWin::BasicTestWin() -{ - _view = 0; - _sub_view = 0; - _motion = 0; - _looping = 0; - _current = 0; - - - // - _win_w = _win_h = 0; - _imgWidth = _imgHeight = 0; - _displayScale = 1.0f; - _sift = new SiftGPUEX(); -} - -BasicTestWin::~BasicTestWin() -{ - _motion = 0; - _looping = 0; -} - - - -void BasicTestWin::Display() -{ - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glClear(GL_COLOR_BUFFER_BIT); - _transform.transform(_displayScale); - if(_sift) _sift->DisplaySIFT(); - glFlush(); - glFinish(); - -} - -void BasicTestWin::OnIdle() -{ - if(_looping && ! _motion) - { - KeyInput('r'); - UpdateDisplay(); - } - -} -void BasicTestWin::KeyInput(int key) -{ - switch(key) - { - case '+': - case '=': - _transform.scaleup(); - break; - case '-': - _transform.scaledown(); - break; - case '\r': - _view++; - _sub_view =0; - SetView(); - break; - case '\b': - _view--; - _sub_view = 0; - SetView(); - break; - case ' ': - case '.': - _sub_view++; - SetView(); - break; - case ',': - _sub_view--; - SetView(); - break; - case 'o': - case 'O': - _transform.reset(); - break; - case 'd': - case 'D': - if(_sift) _sift->ToggleDisplayDebug(); - break; - case 'r': - case 'R': - if(_sift) - { - _sift->RunSIFT(++_current); - _stat_frames++; - FitWindow(); - } - break; - case 'c': - case 'C': - if(_sift) _sift->RandomizeColor(); - break; - case 'q': - case 'Q': - if(_sift) _sift->SetVerbose(-1); - break; - case 'v': - if(_sift) _sift->SetVerbose(4); - break; - case 'x': - case 'X': - case 27: - exit(0); - break; - case 'l': - case 'L': - _looping = ! _looping; - if(_looping) - { - _stat_tstart = myclock(); - _stat_frames = 0; - }else - { - float t = (myclock() - _stat_tstart); - float fps = _stat_frames/t; - std::cout<<"************************************\n" - <<fps << " Hz : " << _stat_frames << " frames in " << t << " sec \n" - <<"************************************\n"; - } - break; - } -} - - - -void BasicTestWin::MoveMouse(int x, int y) -{ - if(_motion==0)return; - _transform.translate(x-_motion_x, y-_motion_y); - _motion_x = x; - _motion_y = y; - UpdateDisplay(); -} - -void BasicTestWin::ReShape(int w, int h) -{ - glViewport(0, 0, w, h); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0, w, h, 0,0,1); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - _win_w = w; - _win_h = h; -} - -void BasicTestWin::RunSiftGPU() -{ - if(_sift->RunSIFT()) - { - _sift->SetVerbose(2); - FitWindow(); - }else - { - exit(0); - } -} - -void BasicTestWin::ParseSiftParam(int argc, char** argv) -{ - _sift->ParseParam(argc, argv); - _sift->SetVerbose(5); - _win_x = _win_y = -1; - _sift->GetInitWindowPotition(_win_x, _win_y); -} - - - - -void BasicTestWin::FitWindow() -{ - int w, h , dw, dh; - _sift->GetImageDimension(w, h); - - - if(w <=0 || h <=0 ) return; - - - if( w == _imgWidth || h == _imgHeight) - { - ReShape(_win_w, _win_h); - return; - } - - - _transform.setcenter(w*0.5, h*0.5); - - /// - - dw =_imgWidth = w; - dh =_imgHeight = h; - - _displayScale = 1.0; - - while(dw>1024 || dh >1024) - { - dw>>=1; - dh>>=1; - _displayScale *= 0.5; - } - - while(dw < 512 && dh < 512) - { - dw <<= 1; - dh <<= 1; - _displayScale*= 2.0; - } - - if ( dw > _win_w || dh > _win_h) - { - _win_w = dw; - _win_h = dh; - SetDisplaySize(dw, dh); - }else - { - ReShape(_win_w, _win_h); - } -} - - - - -void BasicTestWin::SetView() -{ - if(_sift) - { - _sift->SetView(_view, _sub_view, _title); - SetWindowTitle(_title); - } -} - -void BasicTestWin::StartMotion(int x, int y) -{ - _motion = 1; - _motion_x = x; - _motion_y = y; -} - -void BasicTestWin::EndMotion() -{ - _motion = 0; -} - - -void BasicTestWin::SetVerbose() -{ - _sift->SetVerbose(); -} - -float BasicTestWin::myclock() -{ -#if defined(_WIN32) - return clock() * 1.0 / CLOCKS_PER_SEC; -#else - static int started = 0; - static struct timeval tstart; - if(started == 0) - { - gettimeofday(&tstart, NULL); - started = 1; - return 0; - }else - { - struct timeval now; - gettimeofday(&now, NULL) ; - return (now.tv_usec - tstart.tv_usec + (now.tv_sec - tstart.tv_sec) * 1000000)/1000000.f; - } -#endif -} diff --git a/3rdparty/SiftGPU/src/TestWin/BasicTestWin.h b/3rdparty/SiftGPU/src/TestWin/BasicTestWin.h deleted file mode 100644 index 51aaf35e..00000000 --- a/3rdparty/SiftGPU/src/TestWin/BasicTestWin.h +++ /dev/null @@ -1,97 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: BasicTestWin.h -// Author: Changchang Wu -// Description : -// BasicTestWin: basic viewer of SiftGPU -// both TestWinGlut and GLTestWndare derived from this -// SiftDriver: A simple driver of SiftGPU -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - - -#if !defined(BASIC_TEST_WIN_H) -#define BASIC_TEST_WIN_H - -#if _WIN32 && _MSC_VER > 1000 -#pragma once -#endif // _MSC_VER > 1000 - - -#include "GLTransform.h" - -class SiftParam; -class SiftGPUEX; - -////////////////////////////////////////////////////////////////////////// -//class TestDriver -//description: simple SiftGPU driver -///////////////////////////////////////////////////////////////////////// - - -////////////////////////////////////////////////////////////////////////// -//class BasicTestWin -//description: basic SiftGPU viewer -// two implementations are GLTestWnd and TestWinGlut -/////////////////////////////////////////////////////////////////////////// - - -class BasicTestWin -{ - GlTransform _transform; - int _looping; - int _motion; - int _motion_x, _motion_y; - char _title[512]; - int _view; - int _sub_view; - int _win_w, _win_h; - -protected: - float _displayScale; - int _imgWidth, _imgHeight; - int _win_x, _win_y; - int _current; -private: - // - float _stat_tstart; - int _stat_frames; -protected: - SiftGPUEX* _sift; -public: - void SetVerbose(); - void FitWindow(); - void OnIdle(); - void EndMotion(); - void StartMotion(int x, int y); - void SetView(); - void ReShape(int w, int h); - void MoveMouse(int x, int y); - void KeyInput(int key); - void Display(); - virtual void UpdateDisplay()=0; - BasicTestWin(); - virtual ~BasicTestWin(); - void ParseSiftParam(int argc, char** argv); - void RunSiftGPU(); - static float myclock(); -protected: - virtual void SetWindowTitle(char * title)=0; - virtual void SetDisplaySize(int w, int h)=0; -}; - -#endif // !defined(BASIC_TEST_WIN_H) - diff --git a/3rdparty/SiftGPU/src/TestWin/CMakeLists.txt b/3rdparty/SiftGPU/src/TestWin/CMakeLists.txt deleted file mode 100644 index 0bbaa8f2..00000000 --- a/3rdparty/SiftGPU/src/TestWin/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -find_package(OpenGL REQUIRED) -find_package(GLUT REQUIRED) -find_package(Glew REQUIRED) - -include_directories( ${GLEW_INCLUDE_DIRS} ) - -ADD_EXECUTABLE(SimpleSIFT SimpleSIFT.cpp) -TARGET_LINK_LIBRARIES(SimpleSIFT siftgpu) - -ADD_EXECUTABLE(TestWinGlut TestWinGlut.cpp BasicTestWin.cpp ) -TARGET_LINK_LIBRARIES(TestWinGlut siftgpu) - -set(CMAKE_VERBOSE_MAKEFILE ON) diff --git a/3rdparty/SiftGPU/src/TestWin/GLTestWnd.cpp b/3rdparty/SiftGPU/src/TestWin/GLTestWnd.cpp deleted file mode 100644 index 46e0fe9d..00000000 --- a/3rdparty/SiftGPU/src/TestWin/GLTestWnd.cpp +++ /dev/null @@ -1,305 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: GLTestWnd.cpp -// Author: Changchang Wu -// Description : implementation of the GLTestWnd class. -// -// -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - - -#define WIN32_LEAN_AND_MEAN - -#include <windows.h> -#include <windowsx.h> -#include "GL/gl.h" -#include <stdlib.h> - -///////////////////////// -#include "BasicTestWin.h" -#include "GLTestWnd.h" - -#ifdef _WINDOWS -int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, - LPSTR lpCmdLine, int nCmdShow) -{ - ////////////////////////////////////////// - //create a window - GLTestWnd win(lpCmdLine); -#else -int main(int argc, char** argv) -{ - GLTestWnd win(argc, argv); -#endif - - /////////////////////////////////////// - MSG msg; // - while (GetMessage(&msg, NULL, 0, 0)) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - //on idle - if(PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE)==0)SendMessage(GetActiveWindow(), WM_MY_IDLE, 0, 0); - } - return (int) msg.wParam; -} - -////////////////////////////////////////////////////////////////////// -// Construction/Destruction -////////////////////////////////////////////////////////////////////// - - -GLTestWnd::GLTestWnd(LPSTR cmd) -{ - /////////////////////// - //Parse Command Line - if(cmd) ParseCommandLine(cmd); - - ////////////////////// - //create the window - CreateWindowGL(); -} - -GLTestWnd::GLTestWnd(int argc, char**argv) -{ - BasicTestWin::ParseSiftParam(argc, argv); - CreateWindowGL(); -} - -GLTestWnd::~GLTestWnd() -{ - -} - -void GLTestWnd::CreateWindowGL() -{ - _hWndMain = NULL; - RegisterWindowClass(); - HWND hWnd = CreateWindow("SIFT_GPU_WND", "SIFT_GPU", - WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN, - CW_USEDEFAULT, CW_USEDEFAULT, - 600, 450, NULL, NULL, NULL, this); - ShowWindow(hWnd, SW_SHOW); - UpdateWindow(hWnd); - -} -LRESULT CALLBACK GLTestWnd::___WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - static int nWnd=0; - GLTestWnd *pWnd; - if(message==WM_CREATE) - { - LPCREATESTRUCT cs = (LPCREATESTRUCT)lParam; - pWnd = (GLTestWnd*)cs->lpCreateParams; - pWnd->_hWndMain = hWnd; - SetWindowLong(hWnd,0,(long)(pWnd)); - nWnd++; - - }else if(message== WM_DESTROY) - { - //pWnd=(GLTestWnd*)GetWindowLong(hWnd,0); - //delete pWnd; - SetWindowLong(hWnd,0,0); - nWnd--; - if(nWnd==0) PostQuitMessage(0); - pWnd = NULL; - }else - { - pWnd=(GLTestWnd*)GetWindowLong(hWnd,0); - } - if(pWnd) - return pWnd->_WndProc(message,wParam,lParam); - else - return DefWindowProc(hWnd, message, wParam, lParam); - -} - -void GLTestWnd::RegisterWindowClass() -{ - static int registered = 0; - if(registered) return; - WNDCLASSEX wcex; - wcex.cbSize = sizeof(WNDCLASSEX); - wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; - wcex.lpfnWndProc = (WNDPROC)___WndProc; - wcex.cbClsExtra = 0; - wcex.cbWndExtra = 4; - wcex.hInstance = 0; - wcex.hIcon = NULL; - wcex.hCursor = LoadCursor(NULL, IDC_ARROW); - wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); - wcex.lpszMenuName = NULL;; - wcex.lpszClassName = "SIFT_GPU_WND"; - wcex.hIconSm = NULL; - RegisterClassEx(&wcex); - registered = 1; -} - - -LRESULT GLTestWnd::_WndProc(UINT message, WPARAM wParam, LPARAM lParam) -{ - - - switch (message) - { - case WM_CREATE: - { - if(_win_x !=-1) - { - LPCREATESTRUCT cs = (LPCREATESTRUCT)lParam; - MoveWindow(_hWndMain, _win_x, _win_y, cs->cx, cs->cy, 0); - } - SetDisplaySize(600, 450); - HDC hdc = GetDC(_hWndMain); - glCreateRC(hdc); - if(_hglrc == NULL) exit(0); - ReleaseDC(_hWndMain,hdc); - - } - break; - case WM_SIZE: - { - glResize(LOWORD(lParam),HIWORD(lParam)); - } - break; - case WM_PAINT: - { - PAINTSTRUCT ps; - HDC hdc = BeginPaint(_hWndMain, &ps); - /// - glPaint(hdc); - /// - EndPaint(_hWndMain, &ps); - } - break; - case WM_CHAR: - KeyInput((int) wParam); - InvalidateRect(_hWndMain, NULL, FALSE); - break; - case WM_LBUTTONDOWN: - { - int xPos = GET_X_LPARAM(lParam); - int yPos = GET_Y_LPARAM(lParam); - StartMotion(xPos, yPos); - } - break; - case WM_LBUTTONUP: - EndMotion(); - break; - case WM_MOUSEMOVE: - if( wParam & MK_LBUTTON) - { - int xPos = GET_X_LPARAM(lParam); - int yPos = GET_Y_LPARAM(lParam); - MoveMouse(xPos, yPos); - } - case WM_ERASEBKGND: - return TRUE; - case WM_MY_IDLE: - OnIdle(); - return TRUE; - case WM_DESTROY: - PostQuitMessage(0); - break; - default: - return DefWindowProc(_hWndMain, message, wParam, lParam); - } - return 0; -} -void GLTestWnd::glCreateRC(HDC hdc) -{ - int pixelformat; - PIXELFORMATDESCRIPTOR pfd = - { - sizeof(PIXELFORMATDESCRIPTOR),1, - PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER, - PFD_TYPE_RGBA,24,0, 0, 0, 0, 0, 0,0,0,0,0, 0, 0, 0,16,0,0, - PFD_MAIN_PLANE,0,0, 0, 0 - }; - if ((pixelformat = ChoosePixelFormat(hdc, &pfd)) ==0)return ; - if (SetPixelFormat(hdc, pixelformat, &pfd) == FALSE)return ; - pixelformat =::GetPixelFormat(hdc); - ::DescribePixelFormat(hdc, pixelformat, sizeof(pfd), &pfd); - - // - _hglrc = wglCreateContext(hdc); - wglMakeCurrent(hdc, _hglrc); - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - RunSiftGPU(); - - wglMakeCurrent(NULL, NULL); - -} - -void GLTestWnd::glResize(int w, int h) -{ - ReShape(w, h); -} - -void GLTestWnd::glPaint(HDC hdc) -{ - wglMakeCurrent(hdc,HGLRC(_hglrc)); - Display(); - SwapBuffers(hdc); -} - -void GLTestWnd::ParseCommandLine(LPSTR cmd) -{ - int argc=0; - char**argv = new char*[256]; - if(*cmd == 0) return; - do - { - while(*cmd ==' ') cmd++; - if(*cmd) - { - argv[argc++] = cmd; - } - while(*cmd && *cmd != ' ') cmd++; - if(*cmd==' ') *cmd++ = 0; - - }while(*cmd && argc <256); - BasicTestWin::ParseSiftParam(argc, argv); -} - -void GLTestWnd::SetDisplaySize(int w, int h) -{ - RECT rc; int dw, dh; - GetClientRect(_hWndMain, &rc); - - dw = w - rc.right; - dh = h - rc.bottom; - GetWindowRect(_hWndMain, &rc); - - MoveWindow(_hWndMain, rc.left, rc.top, rc.right - rc.left + dw, - rc.bottom - rc.top + dh, TRUE); - -} - -void GLTestWnd::SetWindowTitle(char *title) -{ - SetWindowText(_hWndMain, title); -} - -void GLTestWnd::UpdateDisplay() -{ - InvalidateRect(_hWndMain, NULL,0); -} - diff --git a/3rdparty/SiftGPU/src/TestWin/GLTestWnd.h b/3rdparty/SiftGPU/src/TestWin/GLTestWnd.h deleted file mode 100644 index 5eabdad0..00000000 --- a/3rdparty/SiftGPU/src/TestWin/GLTestWnd.h +++ /dev/null @@ -1,61 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: GLTestWnd.h -// Author: Changchang Wu -// Description : interface for the GLTestWnd class. -// Win32-based SiftGPU viewer -// -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - - -#if !defined(GL_TEST_WND_H) -#define GL_TEST_WND_H - -#if _WIN32 && _MSC_VER > 1000 -#pragma once -#endif // _MSC_VER > 1000 - -#define WM_MY_IDLE WM_USER+1 - - - - -class BasicTestWin; -class GLTestWnd : public BasicTestWin -{ - HGLRC _hglrc; - HWND _hWndMain; -private: - static LRESULT CALLBACK ___WndProc(HWND, UINT, WPARAM, LPARAM); - inline LRESULT _WndProc(UINT, WPARAM, LPARAM); - void CreateWindowGL(); - static void RegisterWindowClass(); -public: - void UpdateDisplay(); - void SetWindowTitle(char *title); - void SetDisplaySize(int w, int h); - void ParseCommandLine(LPSTR cmd); - void glPaint(HDC ); - void glResize(int w, int h); - void glCreateRC(HDC hdc); - GLTestWnd(LPSTR cmd); - GLTestWnd(int argc, char**argv); - virtual ~GLTestWnd(); - -}; - -#endif // !defined(GL_TEST_WND_H) diff --git a/3rdparty/SiftGPU/src/TestWin/GLTransform.h b/3rdparty/SiftGPU/src/TestWin/GLTransform.h deleted file mode 100644 index 57507d01..00000000 --- a/3rdparty/SiftGPU/src/TestWin/GLTransform.h +++ /dev/null @@ -1,113 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: GLTransform.h -// Author: Changchang Wu -// Description : GLTransform tookit for opengl display -// -// -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// -#if !defined(GL_TRANSFORM_H) -#define GL_TRANSFORM_H - -#include <math.h> -class GlTransform -{ -public: - double cx, cy; - double q[4]; - double t[3]; - double sc, ds; - GlTransform() - { - q[0] = 1.0; - q[1] = q[2] = q[3] =0; - t[0] = t[1] = t[2] =0; - sc = 1.0; - cx = cy = 0; - } - void reset() - { - q[0] = 1.0; - q[1] = q[2] = q[3] =0; - t[0] = t[1] = t[2] =0; - sc = 1.0; - } - void operator=(GlTransform& v) - { - q[0] = v.q[0]; - q[1] = v.q[1]; - q[2] = v.q[2]; - q[3] = v.q[3]; - t[0] = v.t[0]; - t[1] = v.t[1]; - t[2] = v.t[2]; - sc = v.sc; - } - void operator *=(double scale) - { - sc *= scale; - t[0]*= scale; - t[1]*= scale; - t[2]*= scale; - } - void scaleset(double scale) - { - double ds = scale/sc; - t[0]*= ds; - t[1]*= ds; - t[2]*= ds; - sc = scale; - } - void scaleup() - { - double scale; - if(sc < 6) scale = float(int(sc))+1; - else scale = sc * 2.0; - scaleset(scale); - } - void scaledown() - { - double scale; - if(sc >1.0 &&sc < 2.0) scale = 1.0; - else scale = sc*0.5; - scaleset(scale); - } - void translate(int dx, int dy, int dz =0) - { - t[0] += dx; - t[1] += dy; - t[2] += dz; - } - void setcenter(double x, double y) - { - cx = x; - cy = y; - t[0] = t[1] = t[2] = 0; - } - - void transform(double es = 1.0) - { - double s = sc* es; - glTranslated(cx*es, cy*es, 0.0); - glTranslated(t[0] ,t[1] ,t[2]); - glScaled(s,s,s); - glTranslated(-cx, - cy, 0); - } -}; - -#endif - diff --git a/3rdparty/SiftGPU/src/TestWin/MultiThreadSIFT.cpp b/3rdparty/SiftGPU/src/TestWin/MultiThreadSIFT.cpp deleted file mode 100644 index 1f133175..00000000 --- a/3rdparty/SiftGPU/src/TestWin/MultiThreadSIFT.cpp +++ /dev/null @@ -1,258 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: MultiThreadSIFT.cpp -// Author: Changchang Wu -// Description : An example to show how to use SiftGPU in multi-threading -// with each thread using different GPU device. -// The same idea also applies to SiftMatchGPU. -// -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - - -#include <stdlib.h> -#include <vector> -#include <iostream> -using std::vector; -using std::iostream; -#include <time.h> - - -#ifdef _WIN32 - #include <windows.h> - //define this to get dll import definition for win32 - #define SIFTGPU_DLL - #ifdef _DEBUG - #pragma comment(lib, "../../lib/siftgpu_d.lib") - #else - #pragma comment(lib, "../../lib/siftgpu.lib") - #endif - #define thread_t HANDLE -#else - #include <stdio.h> - #include <pthread.h> - #define thread_t pthread_t - pthread_mutex_t global_mutex = PTHREAD_MUTEX_INITIALIZER; -#endif - - -#include "../SiftGPU/SiftGPU.h" - -class ScopedMutex -{ -#ifdef _WIN32 -private: - HANDLE hmutex; -public: - ScopedMutex(const char* name) { - hmutex = CreateMutex(NULL, FALSE, name); - WaitForSingleObject(hmutex, INFINITE); - } - ~ScopedMutex() - { - ReleaseMutex(hmutex); - CloseHandle(hmutex); - } -#else -public: - ScopedMutex(const char* name) { - pthread_mutex_lock(&global_mutex); - } - ~ScopedMutex() - { - pthread_mutex_unlock(&global_mutex); - } -#endif -}; - - - -class MultiThreadSIFT -{ -protected: - SiftGPU* _sift; - const void * _thread_param; - int _device_id; -private: - void Initialize(int device_id) - { - ScopedMutex mutex("siftgpu_initialize"); - printf("#%d: Initialize MultiThreadSIFT...", device_id); - InitializeSIFT(); - printf("done\n"); - - //The initialization part should be protected by a mutex in - //single-process-multi-thread mode. For now many parameters - //are still global static variables. - } -public: - MultiThreadSIFT(int device_id = 0, const void* thread_param = NULL) - { - _thread_param = thread_param; - _device_id = device_id; - } - virtual ~MultiThreadSIFT() - { - if(_sift) delete _sift; - } - - thread_t RunThread() - { -#ifdef _WIN32 - return CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)RunMultiThreadSIFT, this, 0, 0); -#else - thread_t threadid; - pthread_create(&threadid, NULL, RunMultiThreadSIFT, this); - return threadid; -#endif - } -#ifdef _WIN32 - static DWORD -#else - static void* -#endif - RunMultiThreadSIFT(void* mts) - { - MultiThreadSIFT* mtsift = (MultiThreadSIFT*) mts; - mtsift->Initialize(mtsift->_device_id); - mtsift->RunTask(); - return 0; - } -public: - //Two Functions to overload for specific task - virtual void RunTask() - { - - } - //set parameters and initizlze SIFT - virtual void InitializeSIFT() - { - //////////////////////////// - char device[2] = {'0' + _device_id, '\0'}; //works for 0-9...use sprintf if you have more than 10 - char * argv[] = {"-fo", "-1", "-v", "0", "-cuda", device}; - //-nogl was previously required, but now default - int argc = sizeof(argv)/sizeof(char*); - - ///////////////////////////////////// - _sift = new SiftGPU; - _sift->ParseParam(argc, argv); - - //create an OpenGL context for computation, and SiftGPU will be initialized automatically - if(_sift->CreateContextGL() != SiftGPU::SIFTGPU_FULL_SUPPORTED) exit(0); - } -}; - - -//Multi-threading demo. -//Each thread will load a file, and repeat the processing 100 times. - -class MultiThreadDemo: public MultiThreadSIFT -{ -public: - MultiThreadDemo(int deviceid, const char* filename): MultiThreadSIFT(deviceid, filename) - { - } - virtual void RunTask() - { - printf("#%d, running task\n", _device_id); - time_t t1, t2; - t1 = time(NULL); - for(int i = 0; i < 100; ++i) _sift->RunSIFT(); - t2 = time(NULL); - printf("#%d: %dhz\n", _device_id, int(100/(t2-t1))); - } - virtual void InitializeSIFT() - { - MultiThreadSIFT::InitializeSIFT(); - const char* filename = (const char*) _thread_param; - _sift->RunSIFT(filename); - - ////////////////////////////////////////////////////////////////////// - //WARNING: the image loader (DeviL) used by SiftGPU is not thread-safe. - //This demo will put the file loading part in InitializeSIFT, which - //is protected by a mutex. In your multi-thread application, you should - //load the image data outside SiftGPU, and specify the memory data to - //SiftGPU directly. - ///////////////////////////////////////////////////////////////////// - } -}; - -//Multi-process demo -//Each process will repeat the processing of a file for 100 times. -class MultiProcessDemo: public MultiThreadSIFT -{ -public: - MultiProcessDemo(int deviceid, const char* filename): MultiThreadSIFT(deviceid, filename) - { - } - virtual void RunTask() - { - char* filename = (char*) _thread_param; - time_t t1, t2; - t1 = time(NULL); - for(int i = 0; i < 100; ++i) _sift->RunSIFT(filename); - t2 = time(NULL); - printf("Speed on device %d : %dhz\n", _device_id, int(100/(t2-t1))); - } - virtual void InitializeSIFT() - { - //Although the multi-process demo here uses CUDA, - //if multiple GPUs are mapped to multiple monitors/displayes - //it is possible to use OpenGL (not CUDA)for this. - //Also, the mutex protection is not necessary - char device[2] = {'0' + _device_id, '\0'}; //works for 0-9...use sprintf if you have more than 10 - char * argv[] = {"-fo", "-1", "-v", "0", "-cuda", device}; - int argc = sizeof(argv)/sizeof(char*); - - ///////////////////////////////////// - //create two server with differ socket ports - _sift = CreateRemoteSiftGPU(7777 + _device_id, NULL); - _sift->ParseParam(argc, argv); - - //create an OpenGL context for computation, and SiftGPU will be initialized automatically - if(_sift->CreateContextGL() != SiftGPU::SIFTGPU_FULL_SUPPORTED) exit(0); - } -}; - - -int main() -{ - //NOTE that SiftGPU must be compiled with CUDA for this demo - MultiThreadDemo thread1(0, "../data/640-1.jpg"); - MultiThreadDemo thread2(1, "../data/800-1.jpg"); - - - //Use MultiProcessDemo for multi-process mode - //MultiProcessDemo thread1(0, "../data/640-1.jpg"); - //MultiProcessDemo thread2(1, "../data/800-1.jpg"); - - printf("Starting two threads...\n"); - thread_t t1 = thread1.RunThread(); - thread_t t2 = thread2.RunThread(); - -#ifdef _WIN32 - HANDLE handles[2] = {t1, t2}; - WaitForMultipleObjects(2, handles, TRUE, INFINITE); - //////////////////////////////////////////////////////////////// - CloseHandle(t1); - CloseHandle(t2); -#else - pthread_join(t1, NULL); - pthread_join(t2, NULL); -#endif - return 1; -} - diff --git a/3rdparty/SiftGPU/src/TestWin/SimpleSIFT.cpp b/3rdparty/SiftGPU/src/TestWin/SimpleSIFT.cpp deleted file mode 100644 index 882c4280..00000000 --- a/3rdparty/SiftGPU/src/TestWin/SimpleSIFT.cpp +++ /dev/null @@ -1,290 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: SimpleSIFT.cpp -// Author: Changchang Wu -// Description : A simple example shows how to use SiftGPU and SiftMatchGPU -// -// -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - - -#include <stdlib.h> -#include <vector> -#include <iostream> -using std::vector; -using std::iostream; - - -//////////////////////////////////////////////////////////////////////////// -#if !defined(SIFTGPU_STATIC) && !defined(SIFTGPU_DLL_RUNTIME) -// SIFTGPU_STATIC comes from compiler -#define SIFTGPU_DLL_RUNTIME -// Load at runtime if the above macro defined -// comment the macro above to use static linking -#endif - -//////////////////////////////////////////////////////////////////////////// -// define REMOTE_SIFTGPU to run computation in multi-process (Or remote) mode -// in order to run on a remote machine, you need to start the server manually -// This mode allows you use Multi-GPUs by creating multiple servers -// #define REMOTE_SIFTGPU -// #define REMOTE_SERVER NULL -// #define REMOTE_SERVER_PORT 7777 - - -/////////////////////////////////////////////////////////////////////////// -//#define DEBUG_SIFTGPU //define this to use the debug version in windows - -#ifdef _WIN32 - #ifdef SIFTGPU_DLL_RUNTIME - #define WIN32_LEAN_AND_MEAN - #include <windows.h> - #define FREE_MYLIB FreeLibrary - #define GET_MYPROC GetProcAddress - #else - //define this to get dll import definition for win32 - #define SIFTGPU_DLL - #ifdef _DEBUG - #pragma comment(lib, "../../lib/siftgpu_d.lib") - #else - #pragma comment(lib, "../../lib/siftgpu.lib") - #endif - #endif -#else - #ifdef SIFTGPU_DLL_RUNTIME - #include <dlfcn.h> - #define FREE_MYLIB dlclose - #define GET_MYPROC dlsym - #endif -#endif - -#include "../SiftGPU/SiftGPU.h" - - -int main() -{ -#ifdef SIFTGPU_DLL_RUNTIME - #ifdef _WIN32 - #ifdef _DEBUG - HMODULE hsiftgpu = LoadLibrary("siftgpu_d.dll"); - #else - HMODULE hsiftgpu = LoadLibrary("siftgpu.dll"); - #endif - #else - void * hsiftgpu = dlopen("libsiftgpu.so", RTLD_LAZY); - #endif - - if(hsiftgpu == NULL) return 0; - - #ifdef REMOTE_SIFTGPU - ComboSiftGPU* (*pCreateRemoteSiftGPU) (int, char*) = NULL; - pCreateRemoteSiftGPU = (ComboSiftGPU* (*) (int, char*)) GET_MYPROC(hsiftgpu, "CreateRemoteSiftGPU"); - ComboSiftGPU * combo = pCreateRemoteSiftGPU(REMOTE_SERVER_PORT, REMOTE_SERVER); - SiftGPU* sift = combo; - SiftMatchGPU* matcher = combo; - #else - SiftGPU* (*pCreateNewSiftGPU)(int) = NULL; - SiftMatchGPU* (*pCreateNewSiftMatchGPU)(int) = NULL; - pCreateNewSiftGPU = (SiftGPU* (*) (int)) GET_MYPROC(hsiftgpu, "CreateNewSiftGPU"); - pCreateNewSiftMatchGPU = (SiftMatchGPU* (*)(int)) GET_MYPROC(hsiftgpu, "CreateNewSiftMatchGPU"); - SiftGPU* sift = pCreateNewSiftGPU(1); - SiftMatchGPU* matcher = pCreateNewSiftMatchGPU(4096); - #endif - -#elif defined(REMOTE_SIFTGPU) - ComboSiftGPU * combo = CreateRemoteSiftGPU(REMOTE_SERVER_PORT, REMOTE_SERVER); - SiftGPU* sift = combo; - SiftMatchGPU* matcher = combo; -#else - //this will use overloaded new operators - SiftGPU *sift = new SiftGPU; - SiftMatchGPU *matcher = new SiftMatchGPU(4096); -#endif - vector<float > descriptors1(1), descriptors2(1); - vector<SiftGPU::SiftKeypoint> keys1(1), keys2(1); - int num1 = 0, num2 = 0; - - //process parameters - //The following parameters are default in V340 - //-m, up to 2 orientations for each feature (change to single orientation by using -m 1) - //-s enable subpixel subscale (disable by using -s 0) - - - char * argv[] = {"-fo", "-1", "-v", "1"};// - //-fo -1 staring from -1 octave - //-v 1 only print out # feature and overall time - //-loweo add a (.5, .5) offset - //-tc <num> set a soft limit to number of detected features - - //NEW: parameters for GPU-selection - //1. CUDA. Use parameter "-cuda", "[device_id]" - //2. OpenGL. Use "-Display", "display_name" to select monitor/GPU (XLIB/GLUT) - // on windows the display name would be something like \\.\DISPLAY4 - - ////////////////////////////////////////////////////////////////////////////////////// - //You use CUDA for nVidia graphic cards by specifying - //-cuda : cuda implementation (fastest for smaller images) - // CUDA-implementation allows you to create multiple instances for multiple threads - // Checkout src\TestWin\MultiThreadSIFT - ///////////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////////////////// - ////////////////////////Two Important Parameters/////////////////////////// - // First, texture reallocation happens when image size increases, and too many - // reallocation may lead to allocatoin failure. You should be careful when using - // siftgpu on a set of images with VARYING imag sizes. It is recommended that you - // preset the allocation size to the largest width and largest height by using function - // AllocationPyramid or prameter '-p' (e.g. "-p", "1024x768"). - - // Second, there is a parameter you may not be aware of: the allowed maximum working - // dimension. All the SIFT octaves that needs a larger texture size will be skipped. - // The default prameter is 2560 for the unpacked implementation and 3200 for the packed. - // Those two default parameter is tuned to for 768MB of graphic memory. You should adjust - // it for your own GPU memory. You can also use this to keep/skip the small featuers. - // To change this, call function SetMaxDimension or use parameter "-maxd". - // - // NEW: by default SiftGPU will try to fit the cap of GPU memory, and reduce the working - // dimension so as to not allocate too much. This feature can be disabled by -nomc - ////////////////////////////////////////////////////////////////////////////////////// - - - int argc = sizeof(argv)/sizeof(char*); - sift->ParseParam(argc, argv); - - /////////////////////////////////////////////////////////////////////// - //Only the following parameters can be changed after initialization (by calling ParseParam). - //-dw, -ofix, -ofix-not, -fo, -unn, -maxd, -b - //to change other parameters at runtime, you need to first unload the dynamically loaded libaray - //reload the libarary, then create a new siftgpu instance - - - //Create a context for computation, and SiftGPU will be initialized automatically - //The same context can be used by SiftMatchGPU - if(sift->CreateContextGL() != SiftGPU::SIFTGPU_FULL_SUPPORTED) return 0; - - if(sift->RunSIFT("../data/800-1.jpg")) - { - //Call SaveSIFT to save result to file, the format is the same as Lowe's - //sift->SaveSIFT("../data/800-1.sift"); //Note that saving ASCII format is slow - - //get feature count - num1 = sift->GetFeatureNum(); - - //allocate memory - keys1.resize(num1); descriptors1.resize(128*num1); - - //reading back feature vectors is faster than writing files - //if you dont need keys or descriptors, just put NULLs here - sift->GetFeatureVector(&keys1[0], &descriptors1[0]); - //this can be used to write your own sift file. - } - - //You can have at most one OpenGL-based SiftGPU (per process). - //Normally, you should just create one, and reuse on all images. - if(sift->RunSIFT("../data/640-1.jpg")) - { - num2 = sift->GetFeatureNum(); - keys2.resize(num2); descriptors2.resize(128*num2); - sift->GetFeatureVector(&keys2[0], &descriptors2[0]); - } - - //Testing code to check how it works when image size varies - //sift->RunSIFT("../data/256.jpg");sift->SaveSIFT("../data/256.sift.1"); - //sift->RunSIFT("../data/1024.jpg"); //this will result in pyramid reallocation - //sift->RunSIFT("../data/256.jpg"); sift->SaveSIFT("../data/256.sift.2"); - //two sets of features for 256.jpg may have different order due to implementation - - //************************************************************************* - /////compute descriptors for user-specified keypoints (with or without orientations) - - //Method1, set new keypoints for the image you've just processed with siftgpu - //say vector<SiftGPU::SiftKeypoint> mykeys; - //sift->RunSIFT(mykeys.size(), &mykeys[0]); - //sift->RunSIFT(num2, &keys2[0], 1); sift->SaveSIFT("../data/640-1.sift.2"); - //sift->RunSIFT(num2, &keys2[0], 0); sift->SaveSIFT("../data/640-1.sift.3"); - - //Method2, set keypoints for the next coming image - //The difference of with method 1 is that method 1 skips gaussian filtering - //SiftGPU::SiftKeypoint mykeys[100]; - //for(int i = 0; i < 100; ++i){ - // mykeys[i].s = 1.0f;mykeys[i].o = 0.0f; - // mykeys[i].x = (i%10)*10.0f+50.0f; - // mykeys[i].y = (i/10)*10.0f+50.0f; - //} - //sift->SetKeypointList(100, mykeys, 0); - //sift->RunSIFT("../data/800-1.jpg"); sift->SaveSIFT("../data/800-1.sift.2"); - //### for comparing with method1: - //sift->RunSIFT("../data/800-1.jpg"); - //sift->RunSIFT(100, mykeys, 0); sift->SaveSIFT("../data/800-1.sift.3"); - //********************************************************************************* - - - //**********************GPU SIFT MATCHING********************************* - //**************************select shader language************************* - //SiftMatchGPU will use the same shader lanaguage as SiftGPU by default - //Before initialization, you can choose between glsl, and CUDA(if compiled). - //matcher->SetLanguage(SiftMatchGPU::SIFTMATCH_CUDA); // +i for the (i+1)-th device - - //Verify current OpenGL Context and initialize the Matcher; - //If you don't have an OpenGL Context, call matcher->CreateContextGL instead; - matcher->VerifyContextGL(); //must call once - - //Set descriptors to match, the first argument must be either 0 or 1 - //if you want to use more than 4096 or less than 4096 - //call matcher->SetMaxSift() to change the limit before calling setdescriptor - matcher->SetDescriptors(0, num1, &descriptors1[0]); //image 1 - matcher->SetDescriptors(1, num2, &descriptors2[0]); //image 2 - - //match and get result. - int (*match_buf)[2] = new int[num1][2]; - //use the default thresholds. Check the declaration in SiftGPU.h - int num_match = matcher->GetSiftMatch(num1, match_buf); - std::cout << num_match << " sift matches were found;\n"; - - //enumerate all the feature matches - for(int i = 0; i < num_match; ++i) - { - //How to get the feature matches: - //SiftGPU::SiftKeypoint & key1 = keys1[match_buf[i][0]]; - //SiftGPU::SiftKeypoint & key2 = keys2[match_buf[i][1]]; - //key1 in the first image matches with key2 in the second image - } - - //*****************GPU Guided SIFT MATCHING*************** - //example: define a homography, and use default threshold 32 to search in a 64x64 window - //float h[3][3] = {{0.8f, 0, 0}, {0, 0.8f, 0}, {0, 0, 1.0f}}; - //matcher->SetFeatureLocation(0, &keys1[0]); //SetFeatureLocaiton after SetDescriptors - //matcher->SetFeatureLocation(1, &keys2[0]); - //num_match = matcher->GetGuidedSiftMatch(num1, match_buf, h, NULL); - //std::cout << num_match << " guided sift matches were found;\n"; - //if you can want to use a Fundamental matrix, check the function definition - - // clean up.. - delete[] match_buf; -#ifdef REMOTE_SIFTGPU - delete combo; -#else - delete sift; - delete matcher; -#endif - -#ifdef SIFTGPU_DLL_RUNTIME - FREE_MYLIB(hsiftgpu); -#endif - return 1; -} - diff --git a/3rdparty/SiftGPU/src/TestWin/TestWinGlut.cpp b/3rdparty/SiftGPU/src/TestWin/TestWinGlut.cpp deleted file mode 100644 index 4c6c25b5..00000000 --- a/3rdparty/SiftGPU/src/TestWin/TestWinGlut.cpp +++ /dev/null @@ -1,188 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: TestWinGlut.cpp -// Author: Changchang Wu -// Description : Implementation of TestWinGlut Class -// -// -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - -#include <stdlib.h> -#ifdef __APPLE__ - #include "GLUT/glut.h" -#else - #include "GL/glut.h" -#endif -#include "BasicTestWin.h" -#include "TestWinGlut.h" - -///main etry - -int main(int argc, char**argv) -{ - // - ////uncomment this if you want to parse glut parameters - glutInit(&argc, argv); - - - //create the glut window - TestWinGlut twg(argc, argv); -// TestWinGlut twg2(0, NULL); - - - //run the glut main loop to display all the TestWinGlut Windows - TestWinGlut::Run(); - - return 0; -} - -////////////////////////////////////////////////////////////////////// -// TestWinGlut Class -////////////////////////////////////////////////////////////////////// - - - -///////////////////////////////////////////////////////////////////// -///Global Variables -////////////////////////////////////////////////////////////////////// -TestWinGlut* TestWinGlut::_win[TestWinGlut::MAX_TEST_WIN_GLUT]; - -////////////////////////////////////////////////////////////////////// -// Construction/Destruction -////////////////////////////////////////////////////////////////////// - - - -TestWinGlut::TestWinGlut(int argc, char**argv) - -{ - //enable console output - SetVerbose(); - ParseSiftParam(argc, argv); - - //create glut window - CreateGLUT(); - - //parse parameters and run sift - RunSiftGPU(); - - -} - -TestWinGlut::~TestWinGlut() -{ - -} - - - -void TestWinGlut::CreateGLUT() -{ - int id; - glutInitDisplayMode (GLUT_RGBA | GLUT_DOUBLE); - glutInitWindowSize (600,450); - if(_win_x != -1) glutInitWindowPosition(_win_x, _win_y); - id = glutCreateWindow ("SIFT_GPU"); - if(id>0) - { - if(id >=MAX_TEST_WIN_GLUT) exit(0);//should not happen... - - // - glutDisplayFunc (display); - glutKeyboardFunc(keyboard); - glutReshapeFunc (reshape); - glutIdleFunc(idle); - glutMotionFunc(motion); - glutMouseFunc(button); - //save a pointer to the stucture - _win[id] = this; - } - -} - -void TestWinGlut::idle() -{ - int id = glutGetWindow(); - _win[id]->OnIdle(); -} - -void TestWinGlut::keyboard(unsigned char key, int x, int y) -{ - int id = glutGetWindow(); - - _win[id]->KeyInput(key); - glutPostRedisplay(); -} -void TestWinGlut::reshape(int w, int h) -{ - int id = glutGetWindow(); - _win[id]->ReShape(w, h); - glutPostRedisplay(); -} -void TestWinGlut::display() -{ - static int firstcall=1; - int id = glutGetWindow(); - _win[id]->Display(); - glutSwapBuffers(); - if(firstcall ==0) - { - }else - { - //if it is the first display call, redraw - firstcall = 0; - glutPostRedisplay(); - } -} - -void TestWinGlut::Run() -{ - glutMainLoop(); -} - -void TestWinGlut::motion(int x, int y) -{ - int id = glutGetWindow(); - _win[id]->MoveMouse(x, y); -} - -void TestWinGlut::SetWindowTitle(char *title) -{ - glutSetWindowTitle(title); -} - -void TestWinGlut::button(int button, int state,int x, int y) -{ - int id = glutGetWindow(); - if (button == GLUT_LEFT_BUTTON) - { - if(state == GLUT_DOWN) - _win[id]->StartMotion(x, y); - else if (state == GLUT_UP) - _win[id]->EndMotion(); - } -} - -void TestWinGlut::UpdateDisplay() -{ - glutPostRedisplay(); -} - -void TestWinGlut::SetDisplaySize(int w, int h) -{ - glutReshapeWindow(w, h); -} diff --git a/3rdparty/SiftGPU/src/TestWin/TestWinGlut.h b/3rdparty/SiftGPU/src/TestWin/TestWinGlut.h deleted file mode 100644 index 58912d54..00000000 --- a/3rdparty/SiftGPU/src/TestWin/TestWinGlut.h +++ /dev/null @@ -1,62 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: TestWinGlut.h -// Author: Changchang Wu -// Description : interface for the TestWinGlut class. -// GLUT-based SiftGPU viewer -// -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - -#if !defined(TEST_WIN_GLUT_H) -#define TEST_WIN_GLUT_H - -#if _WIN32 && _MSC_VER > 1000 -#pragma once -#endif // _MSC_VER > 1000 - -class BasicTestWin; - -class TestWinGlut : public BasicTestWin -{ - enum - { - MAX_TEST_WIN_GLUT = 100 - }; - static void button(int button, int state,int x, int y); - static void display(); - static void keyboard(unsigned char key, int x, int y); - static void idle(); - void CreateGLUT(); - static void reshape(int w, int h); - - //may also use std::vector - static TestWinGlut * _win[MAX_TEST_WIN_GLUT]; - -public: - void SetDisplaySize(int w, int h); - void UpdateDisplay(); - void SetWindowTitle(char *title); - static void motion(int x, int y); - static void Run(); - TestWinGlut(int argc, char**argv); - virtual ~TestWinGlut(); - - -}; - -#endif // !defined(TEST_WIN_GLUT_H) - diff --git a/3rdparty/SiftGPU/src/TestWin/speed.cpp b/3rdparty/SiftGPU/src/TestWin/speed.cpp deleted file mode 100644 index 9c385bdb..00000000 --- a/3rdparty/SiftGPU/src/TestWin/speed.cpp +++ /dev/null @@ -1,200 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// File: Speed.cpp -// Author: Changchang Wu -// Description : Evaluate the speed of SiftGPU -// -// -// -// Copyright (c) 2007 University of North Carolina at Chapel Hill -// All Rights Reserved -// -// Permission to use, copy, modify and distribute this software and its -// documentation for educational, research and non-profit purposes, without -// fee, and without a written agreement is hereby granted, provided that the -// above copyright notice and the following paragraph appear in all copies. -// -// The University of North Carolina at Chapel Hill make no representations -// about the suitability of this software for any purpose. It is provided -// 'as is' without express or implied warranty. -// -// Please send BUG REPORTS to ccwu@cs.unc.edu -// -//////////////////////////////////////////////////////////////////////////// - - -#include <stdlib.h> -#include <vector> -#include <iostream> -using std::cout; - -#ifdef _WIN32 - //dll import definition for win32 - #define SIFTGPU_DLL -#endif - -#if defined(_WIN32) -//for windows, the default timing uses timeGetTime, you can define TIMING_BY_CLOCK to use clock() - #if defined(TIMING_BY_CLOCK) - #include <time.h> - #else - #define WIN32_LEAN_AND_MEAN - #include <windows.h> - #include <mmsystem.h> - #pragma comment(lib, "winmm.lib") - #endif -#else - #include <sys/time.h> -#endif - - -#include "../SiftGPU/SiftGPU.h" - -//define the marcro bellow to reload image file everytime -//#define INCLUDE_DISK_READING_TIME - -//define the macro below to test speed of multi-process mode -//#define TEST_MULTI_PROCESS_SIFTGPU - - - -#define SIFTGPU_REPEAT 30 - -// you should specify the input image and the sift parameter to speed.exe -// for example: speed.exe -i 600.pgm -fo -1 -// to test CUDA, you first need to recompile SiftGPU with CUDA capability - -int GetMilliSecond(); - -int main(int argc, char * argv[]) -{ -#ifndef TEST_MULTI_PROCESS_SIFTGPU - SiftGPU sift; -#else - ComboSiftGPU* combo = CreateRemoteSiftGPU(); - SiftGPU& sift = (*combo); -#endif - int num; - float timing[10], time_all =0, time_start; - - //Parse parameters - sift.ParseParam(argc - 1, argv + 1); - sift.SetVerbose(0); - - std::cout<<"Initialize and warm up...\n"; - //create an OpenGL context for computation - if(sift.CreateContextGL() ==0) return 0; - - if(sift.RunSIFT()==0) return 0; - - //image is loaded for only once for this experiment -#ifndef TEST_MULTI_PROCESS_SIFTGPU - std::cout<<"Loading image: " << sift._timing[0]*1000 << "ms, " - <<"Tex initialization: "<<sift._timing[1]*1000 << "ms\n\n" - <<"Start 2x"<<SIFTGPU_REPEAT<<" repetitions...\n"; -#ifdef INCLUDE_DISK_READING_TIME - char imagepath[MAX_PATH]; - strcpy(imagepath, sift.GetCurrentImagePath()); -#endif -#endif - - //run one more time to get all texture allocated - - sift.RunSIFT(); - num = sift.GetFeatureNum(); - - - //use no message output mode to get maximum speed. - time_start = (float) GetMilliSecond(); - for(int i = 0; i < SIFTGPU_REPEAT; i++) - { -#ifdef INCLUDE_DISK_READING_TIME - sift.RunSIFT(imagepath); -#else - sift.RunSIFT(); -#endif - if(sift.GetFeatureNum()==num) std::cout <<"+"; - else std::cout << "e"; - } - time_all = ((float)GetMilliSecond() - time_start)/1000/SIFTGPU_REPEAT; - std::cout<<"\n"; - - //change the timing setting, so that we can get more accurate timing for each steps - //in this mode, the overal speed will be decreased. - sift.SetVerbose(-2); //little trick to disable all output but keep the timing - std::fill(timing, timing + 10, 0.0f); - for(int k = 0; k < SIFTGPU_REPEAT; k++) - { -#ifdef INCLUDE_DISK_READING_TIME - sift.RunSIFT(imagepath); -#else - sift.RunSIFT(); -#endif - for(int j = 0; j < 10; j++) timing[j] += sift._timing[j]; - if(sift.GetFeatureNum()==num) std::cout <<"#"; - else std::cout << "e"; - } - for(int j = 0; j < 10; j++) timing[j] /= SIFTGPU_REPEAT; - - std::cout - <<"\n\n****************************************\n" - <<"[Feature Count]:\t" << num << "\n" - <<"[Average Time]:\t\t" << time_all * 1000.0f << "ms\n" - <<"[Average Speed]:\t" << 1.0 / time_all << "hz\n" -#ifndef TEST_MULTI_PROCESS_SIFTGPU -#ifdef INCLUDE_DISK_READING_TIME - <<"[Load Image File]:\t" << timing[0] * 1000.0f << "ms\n" -#endif - <<"[Build Pyramid]:\t" << timing[2] * 1000.0f << "ms\n" - <<"[Detection]:\t\t" << timing[3] * 1000.0f << "ms\n" - <<"[Feature List]:\t\t" << timing[4] * 1000.0f << "ms\n" - <<"[Orientation]:\t\t" << timing[5] * 1000.0f << "ms\n" - <<"[MO Feature List]:\t" << timing[6] * 1000.0f << "ms\n" - <<"[Download Keys]:\t" << timing[7] * 1000.0f << "ms\n" - <<"[Descriptor]:\t\t" << timing[8] * 1000.0f << "ms\n" -#endif - <<"****************************************\n"; - -#ifdef TEST_MULTI_PROCESS_SIFTGPU - delete combo; -#endif - - return 0; -} - - - -int GetMilliSecond() -{ -#if defined(_WIN32) - #if defined(TIMING_BY_CLOCK) - return clock() * 1000 / CLOCKS_PER_SEC; - #else - static int started = 0; - static int tstart; - //the resolution of teimGetTime will be changed to 1ms inside SiftGPU - if(started == 0) - { - tstart = timeGetTime(); - started = 1; - return 0; - }else - { - return timeGetTime() - tstart; - } - #endif -#else - static int started = 0; - static struct timeval tstart; - if(started == 0) - { - gettimeofday(&tstart, NULL); - started = 1; - return 0; - }else - { - struct timeval now; - gettimeofday(&now, NULL) ; - return (now.tv_usec - tstart.tv_usec + (now.tv_sec - tstart.tv_sec) * 1000000)/1000; - } -#endif -} From 282af2ad56cd2df36f362d6cfeec0e701abeb00b Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Wed, 18 May 2016 12:01:39 +0200 Subject: [PATCH 03/79] fixes initial cmake file --- CMakeLists.txt | 39 ++--- ODConfig.h.in => cmake/od_config.h.in | 0 ...y_dependency.cmake => od_dependency.cmake} | 5 +- cmake/od_macros.cmake | 133 ++++++++++++++++++ cmake/od_targets.cmake | 126 +---------------- common/CMakeLists.txt | 4 - examples/apps/CMakeLists.txt | 4 + examples/apps/version/opendetection.cpp | 13 ++ opendetection.cpp | 16 --- 9 files changed, 172 insertions(+), 168 deletions(-) rename ODConfig.h.in => cmake/od_config.h.in (100%) rename cmake/{od_mandatory_dependency.cmake => od_dependency.cmake} (71%) create mode 100644 cmake/od_macros.cmake create mode 100644 examples/apps/version/opendetection.cpp delete mode 100644 opendetection.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 83f38e4b..54a8f8ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,47 +3,34 @@ project(OpenDetection) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ") -# init variables for convinience +# Initialize variables set(OD_SOURCE_DIR ${OpenDetection_SOURCE_DIR}) set(OD_BINARY_DIR ${OpenDetection_BINARY_DIR}) +set(OD_CMAKE_DIR "${OpenDetection_SOURCE_DIR}/cmake" ) +set(CMAKE_MODULE_PATH ${OD_CMAKE_DIR}/modules) -# init directories before doing all the searches and includes -set(OD_CMAKE_DIR "${OpenDetection_SOURCE_DIR}/cmake" "${OpenDetection_SOURCE_DIR}/cmake/modules") -set(CMAKE_MODULE_PATH ${OD_CMAKE_DIR} ${CMAKE_MODULE_PATH}) - -# include bunch of stuffs and set the appropriate variables - -include_directories("/home/giacomo/libraries/opencv3.1/prefix/include") - -# 1. Version -include(od_version) +# Initialize versioning +include(${OD_CMAKE_DIR}/od_version.cmake) set(OD_VERSION "${OD_MAJOR_VERSION}.${OD_MINOR_VERSION}") set(OD_VERSION_DETAILED "${OD_MAJOR_VERSION}.${OD_MINOR_VERSION}.${OD_BUILD_VERSION}") -# generate config files -configure_file ( - "${OD_SOURCE_DIR}/ODConfig.h.in" - "${OD_BINARY_DIR}/generated/ODConfig.h" - ) +configure_file ("${OD_CMAKE_DIR}/od_config.h.in" "${OD_BINARY_DIR}/generated/od_config.h" ) -# 3. set up different macros and configurations -include(od_targets) -include(od_mandatory_dependency) +# Set up macros and configurations +include(${OD_CMAKE_DIR}/od_targets.cmake) +include(${OD_CMAKE_DIR}/od_dependency.cmake) +include(${OD_CMAKE_DIR}/od_macros.cmake) -# 2. collect config options and set appropriate variables which will be used in the next hierarchy +# Optional parameters option(WITH_DOCUMENTATION "Build the OD documentation" ON) option(WITH_GPU "Build GPU components of the project" ON) option(WITH_SVMLIGHT "Build SVM Light" OFF) -# 3. add all the modules +# Set modules set(OD_MODULES_NAMES 3rdparty common doc detectors examples ) set(OD_MODULES_DIRS ${OD_MODULES_NAMES}) +# Add modules foreach(subdir ${OD_MODULES_DIRS}) add_subdirectory("${OD_SOURCE_DIR}/${subdir}") endforeach(subdir) -#include folders -include_directories("${OD_BINARY_DIR}/generated") - -set(SOURCE_FILES opendetection.cpp) -add_executable(opendetection ${SOURCE_FILES}) \ No newline at end of file diff --git a/ODConfig.h.in b/cmake/od_config.h.in similarity index 100% rename from ODConfig.h.in rename to cmake/od_config.h.in diff --git a/cmake/od_mandatory_dependency.cmake b/cmake/od_dependency.cmake similarity index 71% rename from cmake/od_mandatory_dependency.cmake rename to cmake/od_dependency.cmake index 94f5f621..e426e461 100644 --- a/cmake/od_mandatory_dependency.cmake +++ b/cmake/od_dependency.cmake @@ -1,8 +1,7 @@ find_package(PCL REQUIRED) -find_package(OpenCV REQUIRED) +find_package(OpenCV 3 REQUIRED) find_package(VTK REQUIRED) find_package(Eigen REQUIRED) -find_package(Boost 1.40 COMPONENTS program_options REQUIRED ) - +find_package(Boost 1.40 COMPONENTS program_options REQUIRED) include_directories("${OD_SOURCE_DIR}" ${EIGEN_INCLUDE_DIRS} ${OpenCV_INCLUDE_DIRS} ${PCL_INCLUDE_DIRS} ${OD_SOURCE_DIR}/3rdparty/SiftGPU/src/SiftGPU) \ No newline at end of file diff --git a/cmake/od_macros.cmake b/cmake/od_macros.cmake new file mode 100644 index 00000000..87de5dff --- /dev/null +++ b/cmake/od_macros.cmake @@ -0,0 +1,133 @@ +# global installation type +set(OD_LIB_TYPE SHARED) + +macro(OD_ADD_LIBRARY1 _name _srcs _incs _impl_incs) + + set(lib_name "od_${_name}") + + message(input to od_add_library: ${OD_LIB_TYPE} ${_srcs} ${_incs} ${_impl_incs}) + add_library(${lib_name} ${OD_LIB_TYPE} ${_srcs} ${_incs} ${_impl_incs}) + + # allways link libs: + target_link_libraries(${lib_name} ${Boost_LIBRARIES}) + + # target properties + set_target_properties(${lib_name} PROPERTIES + VERSION ${OD_VERSION} + SOVERSION ${OD_MAJOR_VERSION}.${OD_MINOR_VERSION} + ) + + # Install library + install(TARGETS ${lib_name} + RUNTIME DESTINATION ${OD_INSTALL_RUNTIME_DIR} COMPONENT ${lib_name} + LIBRARY DESTINATION ${OD_INSTALL_LIBRARY_DIR} COMPONENT ${lib_name} + ARCHIVE DESTINATION ${OD_INSTALL_ARCHIVE_DIR} COMPONENT ${lib_name}) + + #install includes + install(FILES ${_incs} ${_impl_incs} + DESTINATION ${OD_INSTALL_INCLUDE_DIR}/${_name} + COMPONENT ${lib_name}) + +endmacro(OD_ADD_LIBRARY1) + +macro(OD_ADD_LIBRARY_ALL _name ) + + set(lib_name "od_${_name}") + + set(options) + set(oneValueArgs) + set(multiValueArgs SRCS INCS) + cmake_parse_arguments(OD_ADD_LIBRARY_ALL "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + #message(input to od_add_library: ${OD_LIB_TYPE} ${OD_ADD_LIBRARY_ALL_SRCS} ${OD_ADD_LIBRARY_ALL_INCS}) + + add_library(${lib_name} ${OD_LIB_TYPE} ${OD_ADD_LIBRARY_ALL_SRCS} ${OD_ADD_LIBRARY_ALL_INCS}) + + # allways link libs: + target_link_libraries(${lib_name} ${Boost_LIBRARIES}) + + # target properties + set_target_properties(${lib_name} PROPERTIES + VERSION ${OD_VERSION} + SOVERSION ${OD_MAJOR_VERSION}.${OD_MINOR_VERSION} + ) + + + # Install library + install(TARGETS ${lib_name} + RUNTIME DESTINATION ${OD_INSTALL_RUNTIME_DIR} COMPONENT ${lib_name} + LIBRARY DESTINATION ${OD_INSTALL_LIBRARY_DIR} COMPONENT ${lib_name} + ARCHIVE DESTINATION ${OD_INSTALL_ARCHIVE_DIR} COMPONENT ${lib_name}) + + message(${_name}) + message("includes to od_add_library: " ${OD_ADD_LIBRARY_ALL_INCS}) + message("include dir: " ${OD_INSTALL_INCLUDE_DIR}) + message("include dir: " ${OD_INSTALL_DOXYGEN_DIR}) + #install includes + install(FILES ${_OD_ADD_LIBRARY_ALL_INCS} + DESTINATION ${OD_INSTALL_INCLUDE_DIR}/${_name} + COMPONENT ${lib_name}) + +endmacro(OD_ADD_LIBRARY_ALL) + +macro(OD_ADD_LIBRARY _name) + + set(lib_name "od_${_name}") + + include_directories("${OD_SOURCE_DIR}") + + #message(input to od_add_library: ${OD_LIB_TYPE} ${ARGN}) + add_library(${lib_name} ${OD_LIB_TYPE} ${ARGN}) + + + # allways link libs: + target_link_libraries(${lib_name} ${Boost_LIBRARIES}) + + # target properties + set_target_properties(${lib_name} PROPERTIES + VERSION ${OD_VERSION} + SOVERSION ${OD_MAJOR_VERSION}.${OD_MINOR_VERSION} + ) + + # Install library + install(TARGETS ${lib_name} + RUNTIME DESTINATION ${OD_INSTALL_RUNTIME_DIR} COMPONENT ${lib_name} + LIBRARY DESTINATION ${OD_INSTALL_LIBRARY_DIR} COMPONENT ${lib_name} + ARCHIVE DESTINATION ${OD_INSTALL_ARCHIVE_DIR} COMPONENT ${lib_name}) + +endmacro(OD_ADD_LIBRARY) + + +macro(OD_ADD_EXAMPLE _name) + set(options) + set(oneValueArgs) + set(multiValueArgs FILES LINK_WITH INCLUDE) + cmake_parse_arguments(OD_ADD_EXAMPLE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) + include_directories("${OD_SOURCE_DIR}" ${OD_ADD_EXAMPLE_INCLUDE}) + add_executable(${_name} ${OD_ADD_EXAMPLE_FILES}) + target_link_libraries(${_name} ${OD_ADD_EXAMPLE_LINK_WITH}) +endmacro(OD_ADD_EXAMPLE) + + +macro(OD_ADD_INCLUDES _name) + #install includes + install(FILES ${ARGN} + DESTINATION ${OD_INSTALL_INCLUDE_DIR}/${_name} + COMPONENT ${lib_name}) + +endmacro(OD_ADD_INCLUDES) + +MACRO(HEADER_DIRECTORIES return_list) + FILE(GLOB_RECURSE new_list *.h) + SET(dir_list "") + FOREACH(file_path ${new_list}) + GET_FILENAME_COMPONENT(dir_path ${file_path} PATH) + SET(dir_list ${dir_list} ${dir_path}) + ENDFOREACH() + LIST(REMOVE_DUPLICATES dir_list) + SET(${return_list} ${dir_list}) +ENDMACRO() + +MACRO(SUBDIRS list) + FILE(GLOB list RELATIVE ${curdir} ${curdir}/*) +ENDMACRO() \ No newline at end of file diff --git a/cmake/od_targets.cmake b/cmake/od_targets.cmake index 73ed2b0a..c9d81e2a 100644 --- a/cmake/od_targets.cmake +++ b/cmake/od_targets.cmake @@ -3,145 +3,33 @@ if(NOT OD_INSTALL_RUNTIME_DIR) set(OD_INSTALL_RUNTIME_DIR bin) endif() + if(NOT OD_INSTALL_LIBRARY_DIR) set(OD_INSTALL_LIBRARY_DIR lib) endif() + if(NOT OD_INSTALL_ARCHIVE_DIR) set(OD_INSTALL_ARCHIVE_DIR lib) endif() + if(NOT OD_INSTALL_INCLUDE_DIR) set(OD_INSTALL_INCLUDE_DIR include/od-${OD_VERSION}/od) endif() + if(NOT OD_INSTALL_DATA_DIR) set(OD_INSTALL_DATA_DIR share/od-${OD_VERSION}) endif() + if(NOT OD_INSTALL_DOC_DIR) set(OD_INSTALL_DOC_DIR share/doc/od-${OD_VERSION}) endif() + if(NOT OD_INSTALL_PACKAGE_DIR) set(OD_INSTALL_PACKAGE_DIR "lib/cmake/od-${OD_VERSION}") endif() + if(NOT OD_INSTALL_DOXYGEN_DIR) set(OD_INSTALL_DOXYGEN_DIR ${OD_INSTALL_DOC_DIR}/doxygen) endif() -# global installation type -set(OD_LIB_TYPE SHARED) - -# usefull functions called -macro(OD_ADD_LIBRARY1 _name _srcs _incs _impl_incs) - - set(lib_name "od_${_name}") - - message(input to od_add_library: ${OD_LIB_TYPE} ${_srcs} ${_incs} ${_impl_incs}) - add_library(${lib_name} ${OD_LIB_TYPE} ${_srcs} ${_incs} ${_impl_incs}) - - # allways link libs: - target_link_libraries(${lib_name} ${Boost_LIBRARIES}) - - # target properties - set_target_properties(${lib_name} PROPERTIES - VERSION ${OD_VERSION} - SOVERSION ${OD_MAJOR_VERSION}.${OD_MINOR_VERSION} - ) - - # Install library - install(TARGETS ${lib_name} - RUNTIME DESTINATION ${OD_INSTALL_RUNTIME_DIR} COMPONENT ${lib_name} - LIBRARY DESTINATION ${OD_INSTALL_LIBRARY_DIR} COMPONENT ${lib_name} - ARCHIVE DESTINATION ${OD_INSTALL_ARCHIVE_DIR} COMPONENT ${lib_name}) - - #install includes - install(FILES ${_incs} ${_impl_incs} - DESTINATION ${OD_INSTALL_INCLUDE_DIR}/${_name} - COMPONENT ${lib_name}) - -endmacro(OD_ADD_LIBRARY1) - -macro(OD_ADD_LIBRARY_ALL _name ) - - set(lib_name "od_${_name}") - - set(options) - set(oneValueArgs) - set(multiValueArgs SRCS INCS) - cmake_parse_arguments(OD_ADD_LIBRARY_ALL "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) - - #message(input to od_add_library: ${OD_LIB_TYPE} ${OD_ADD_LIBRARY_ALL_SRCS} ${OD_ADD_LIBRARY_ALL_INCS}) - - add_library(${lib_name} ${OD_LIB_TYPE} ${OD_ADD_LIBRARY_ALL_SRCS} ${OD_ADD_LIBRARY_ALL_INCS}) - - # allways link libs: - target_link_libraries(${lib_name} ${Boost_LIBRARIES}) - - # target properties - set_target_properties(${lib_name} PROPERTIES - VERSION ${OD_VERSION} - SOVERSION ${OD_MAJOR_VERSION}.${OD_MINOR_VERSION} - ) - - - # Install library - install(TARGETS ${lib_name} - RUNTIME DESTINATION ${OD_INSTALL_RUNTIME_DIR} COMPONENT ${lib_name} - LIBRARY DESTINATION ${OD_INSTALL_LIBRARY_DIR} COMPONENT ${lib_name} - ARCHIVE DESTINATION ${OD_INSTALL_ARCHIVE_DIR} COMPONENT ${lib_name}) - - message(${_name}) - message("includes to od_add_library: " ${OD_ADD_LIBRARY_ALL_INCS}) - message("include dir: " ${OD_INSTALL_INCLUDE_DIR}) - message("include dir: " ${OD_INSTALL_DOXYGEN_DIR}) - #install includes - install(FILES ${_OD_ADD_LIBRARY_ALL_INCS} - DESTINATION ${OD_INSTALL_INCLUDE_DIR}/${_name} - COMPONENT ${lib_name}) - -endmacro(OD_ADD_LIBRARY_ALL) - -macro(OD_ADD_LIBRARY _name) - - set(lib_name "od_${_name}") - - include_directories("${OD_SOURCE_DIR}") - - #message(input to od_add_library: ${OD_LIB_TYPE} ${ARGN}) - add_library(${lib_name} ${OD_LIB_TYPE} ${ARGN}) - - - # allways link libs: - target_link_libraries(${lib_name} ${Boost_LIBRARIES}) - - # target properties - set_target_properties(${lib_name} PROPERTIES - VERSION ${OD_VERSION} - SOVERSION ${OD_MAJOR_VERSION}.${OD_MINOR_VERSION} - ) - - # Install library - install(TARGETS ${lib_name} - RUNTIME DESTINATION ${OD_INSTALL_RUNTIME_DIR} COMPONENT ${lib_name} - LIBRARY DESTINATION ${OD_INSTALL_LIBRARY_DIR} COMPONENT ${lib_name} - ARCHIVE DESTINATION ${OD_INSTALL_ARCHIVE_DIR} COMPONENT ${lib_name}) - -endmacro(OD_ADD_LIBRARY) - - -macro(OD_ADD_EXAMPLE _name) - set(options) - set(oneValueArgs) - set(multiValueArgs FILES LINK_WITH) - cmake_parse_arguments(OD_ADD_EXAMPLE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) - include_directories("${OD_SOURCE_DIR}") - add_executable(${_name} ${OD_ADD_EXAMPLE_FILES}) - target_link_libraries(${_name} ${OD_ADD_EXAMPLE_LINK_WITH}) -endmacro(OD_ADD_EXAMPLE) - - -macro(OD_ADD_INCLUDES _name) - #install includes - install(FILES ${ARGN} - DESTINATION ${OD_INSTALL_INCLUDE_DIR}/${_name} - COMPONENT ${lib_name}) - -endmacro(OD_ADD_INCLUDES) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index d47dd7a8..53382e0b 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -3,14 +3,10 @@ set(LIB_NAME od_${SUBSYS_NAME}) set(SUBSYS_DESC "The core module of OpenDetection having the pipeline logic") set(SUBSYS_DEPS ${OpenCV_LIBS} ${PCL_LIBRARIES}) -message("place is " ${OpenCV_INCLUDE_DIRS}) - if(WITH_GPU) set(SUBSYS_DEPS ${SUBSYS_DEPS} siftgpu) endif() -include_directories(${OpenCV_INCLUDE_DIRS}) - set(build TRUE) if(build) diff --git a/examples/apps/CMakeLists.txt b/examples/apps/CMakeLists.txt index 381347d7..c685db0d 100644 --- a/examples/apps/CMakeLists.txt +++ b/examples/apps/CMakeLists.txt @@ -1,6 +1,10 @@ OD_ADD_EXAMPLE(odcadrecog_test_single_model FILES cadrecog2D/od_test_single_db_single_model.cpp LINK_WITH od_common od_local_image_detector) +include_directories("${OD_BINARY_DIR}/generated") +set(SOURCE_FILES version/opendetection.cpp) +add_executable(opendetection ${SOURCE_FILES}) + if(WITH_SVMLIGHT) OD_ADD_EXAMPLE(od_multihog_app FILES global2D/od_multihog_app.cpp LINK_WITH od_common od_global_image_detector) diff --git a/examples/apps/version/opendetection.cpp b/examples/apps/version/opendetection.cpp new file mode 100644 index 00000000..f1b41b7e --- /dev/null +++ b/examples/apps/version/opendetection.cpp @@ -0,0 +1,13 @@ +// +// Created by sarkar on 03.06.15. +// + +#include "od_config.h" +#include <iostream> + +int main (int argc, char *argv[]) +{ + std::cout << "OpenDetection: " << std::endl; + std::cout << "You are using OpenDetection Version " << OD_MAJOR_VERSION << "." << OD_MINOR_VERSION << std::endl; + return 0; +} diff --git a/opendetection.cpp b/opendetection.cpp deleted file mode 100644 index 57b26230..00000000 --- a/opendetection.cpp +++ /dev/null @@ -1,16 +0,0 @@ -// -// Created by sarkar on 03.06.15. -// - - -#include "ODConfig.h" -#include <iostream> - -using namespace std; - -int main (int argc, char *argv[]) -{ - cout << "OpenDetection: " << endl; - cout << "You are using OpenDetection Version " << OD_MAJOR_VERSION << "." << OD_MINOR_VERSION << endl; - return 0; -} From 98a83e625aca0818a478e9e3e48ba0ac4a4f332c Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Wed, 18 May 2016 16:10:52 +0200 Subject: [PATCH 04/79] first major structure refactoring working --- CMakeLists.txt | 2 +- cmake/od_macros.cmake | 4 ++ common/CMakeLists.txt | 52 ++++++--------- .../{ => include/common}/bindings/svmlight.h | 0 .../common}/pipeline/ODAlgorithmBase.h | 0 .../common}/pipeline/ODDetection.h | 0 .../common}/pipeline/ODDetector.h | 0 .../{ => include/common}/pipeline/ODScene.h | 0 .../{ => include/common}/pipeline/ODTrainer.h | 0 .../common}/pipeline/ObjectDetector.h | 0 .../common}/utils/ODFeatureDetector2D.h | 0 .../common}/utils/ODFrameGenerator.h | 2 +- common/{ => include/common}/utils/utils.h | 0 common/{ => src}/pipeline/ODScene.cpp | 2 +- common/{ => src}/pipeline/ObjectDetector.cpp | 2 +- common/{pipeline => src/utils}/ODDetector.cpp | 0 .../{ => src}/utils/ODFeatureDetector2D.cpp | 2 +- common/{ => src}/utils/utils.cpp | 2 +- detectors/CMakeLists.txt | 13 ++-- detectors/global2D/CMakeLists.txt | 65 ------------------- detectors/global3D/CMakeLists.txt | 51 --------------- .../detection/ODCADDetector3DGlobal.hpp | 0 .../detectors}/global2D/ODFaceRecognizer.h | 0 .../global2D/detection/ODCascadeDetector.h | 0 .../global2D/detection/ODHOGDetector.h | 0 .../global2D/training/ODHOGTrainer.h | 0 .../global3D/ODPointCloudGlobalMatching.h | 0 .../detection/ODCADDetector3DGlobal.h | 0 .../training/ODCADDetectTrainer3DGlobal.h | 0 .../detectors}/local2D/ODImageLocalMatching.h | 0 .../detection/ODCADRecognizer2DLocal.h | 2 +- .../training/ODCADRecogTrainerSnapshotBased.h | 2 +- .../misc/detection/ODDetectorMultiAlgo.h | 0 .../simple_ransac_detection/CMakeLists.txt | 0 .../simple_ransac_detection/CsvReader.cpp | 0 .../simple_ransac_detection/CsvReader.h | 2 +- .../simple_ransac_detection/CsvWriter.cpp | 0 .../simple_ransac_detection/CsvWriter.h | 0 .../simple_ransac_detection/Mesh.cpp | 0 .../simple_ransac_detection/Mesh.h | 0 .../simple_ransac_detection/Model.cpp | 0 .../simple_ransac_detection/Model.h | 0 .../ModelRegistration.cpp | 0 .../ModelRegistration.h | 0 .../simple_ransac_detection/PnPProblem.cpp | 0 .../simple_ransac_detection/PnPProblem.h | 0 .../simple_ransac_detection/RobustMatcher.cpp | 0 .../simple_ransac_detection/RobustMatcher.h | 0 .../simple_ransac_detection/Utils.cpp | 0 .../simple_ransac_detection/Utils.h | 0 .../main_detection_image_wo_k.cpp | 0 .../main_detection_video.cpp | 0 .../main_registration.cpp | 0 .../scale_intrinsic.cpp | 0 detectors/src/global2D/CMakeLists.txt | 62 ++++++++++++++++++ .../{ => src}/global2D/ODFaceRecognizer.cpp | 0 .../global2D/detection/ODCascadeDetector.cpp | 2 +- .../global2D/detection/ODHOGDetector.cpp | 0 .../global2D/training/ODHOGTrainer.cpp | 0 detectors/src/global3D/CMakeLists.txt | 42 ++++++++++++ .../global3D/ODPointCloudGlobalMatching.cpp | 0 .../detection/ODCADDetector3DGlobal.cpp | 0 .../training/ODCADDetectTrainer3DGlobal.cpp | 0 detectors/{ => src}/local2D/CMakeLists.txt | 30 ++++----- .../local2D/ODImageLocalMatching.cpp | 0 .../detection/ODCADRecognizer2DLocal.cpp | 2 +- .../ODCADRecogTrainerSnapshotBased.cpp | 2 +- detectors/{ => src}/misc/CMakeLists.txt | 22 +++---- .../misc/detection/ODDetectorMultiAlgo.cpp | 2 +- doc/doxygen/doxyfile.in | 6 +- doc/doxygen/tutorials_doxygen/detection_3d.md | 2 +- .../tutorials_doxygen/getting_started.md | 8 +-- .../tutorials_doxygen/idea_list_gsoc2016.md | 2 +- .../installation_instruction.md | 2 +- examples/objectdetector/CMakeLists.txt | 5 +- 75 files changed, 186 insertions(+), 206 deletions(-) rename common/{ => include/common}/bindings/svmlight.h (100%) rename common/{ => include/common}/pipeline/ODAlgorithmBase.h (100%) rename common/{ => include/common}/pipeline/ODDetection.h (100%) rename common/{ => include/common}/pipeline/ODDetector.h (100%) rename common/{ => include/common}/pipeline/ODScene.h (100%) rename common/{ => include/common}/pipeline/ODTrainer.h (100%) rename common/{ => include/common}/pipeline/ObjectDetector.h (100%) rename common/{ => include/common}/utils/ODFeatureDetector2D.h (100%) rename common/{ => include/common}/utils/ODFrameGenerator.h (98%) rename common/{ => include/common}/utils/utils.h (100%) rename common/{ => src}/pipeline/ODScene.cpp (97%) rename common/{ => src}/pipeline/ObjectDetector.cpp (97%) rename common/{pipeline => src/utils}/ODDetector.cpp (100%) rename common/{ => src}/utils/ODFeatureDetector2D.cpp (99%) rename common/{ => src}/utils/utils.cpp (99%) delete mode 100644 detectors/global2D/CMakeLists.txt delete mode 100644 detectors/global3D/CMakeLists.txt rename detectors/{global3D => impl}/detection/ODCADDetector3DGlobal.hpp (100%) rename detectors/{ => include/detectors}/global2D/ODFaceRecognizer.h (100%) rename detectors/{ => include/detectors}/global2D/detection/ODCascadeDetector.h (100%) rename detectors/{ => include/detectors}/global2D/detection/ODHOGDetector.h (100%) rename detectors/{ => include/detectors}/global2D/training/ODHOGTrainer.h (100%) rename detectors/{ => include/detectors}/global3D/ODPointCloudGlobalMatching.h (100%) rename detectors/{ => include/detectors}/global3D/detection/ODCADDetector3DGlobal.h (100%) rename detectors/{ => include/detectors}/global3D/training/ODCADDetectTrainer3DGlobal.h (100%) rename detectors/{ => include/detectors}/local2D/ODImageLocalMatching.h (100%) rename detectors/{ => include/detectors}/local2D/detection/ODCADRecognizer2DLocal.h (99%) rename detectors/{ => include/detectors}/local2D/training/ODCADRecogTrainerSnapshotBased.h (98%) rename detectors/{ => include/detectors}/misc/detection/ODDetectorMultiAlgo.h (100%) rename detectors/{local2D/detection => }/simple_ransac_detection/CMakeLists.txt (100%) rename detectors/{local2D/detection => }/simple_ransac_detection/CsvReader.cpp (100%) rename detectors/{local2D/detection => }/simple_ransac_detection/CsvReader.h (92%) rename detectors/{local2D/detection => }/simple_ransac_detection/CsvWriter.cpp (100%) rename detectors/{local2D/detection => }/simple_ransac_detection/CsvWriter.h (100%) rename detectors/{local2D/detection => }/simple_ransac_detection/Mesh.cpp (100%) rename detectors/{local2D/detection => }/simple_ransac_detection/Mesh.h (100%) rename detectors/{local2D/detection => }/simple_ransac_detection/Model.cpp (100%) rename detectors/{local2D/detection => }/simple_ransac_detection/Model.h (100%) rename detectors/{local2D/detection => }/simple_ransac_detection/ModelRegistration.cpp (100%) rename detectors/{local2D/detection => }/simple_ransac_detection/ModelRegistration.h (100%) rename detectors/{local2D/detection => }/simple_ransac_detection/PnPProblem.cpp (100%) rename detectors/{local2D/detection => }/simple_ransac_detection/PnPProblem.h (100%) rename detectors/{local2D/detection => }/simple_ransac_detection/RobustMatcher.cpp (100%) rename detectors/{local2D/detection => }/simple_ransac_detection/RobustMatcher.h (100%) rename detectors/{local2D/detection => }/simple_ransac_detection/Utils.cpp (100%) rename detectors/{local2D/detection => }/simple_ransac_detection/Utils.h (100%) rename detectors/{local2D/detection => }/simple_ransac_detection/main_detection_image_wo_k.cpp (100%) rename detectors/{local2D/detection => }/simple_ransac_detection/main_detection_video.cpp (100%) rename detectors/{local2D/detection => }/simple_ransac_detection/main_registration.cpp (100%) rename detectors/{local2D/detection => }/simple_ransac_detection/scale_intrinsic.cpp (100%) create mode 100644 detectors/src/global2D/CMakeLists.txt rename detectors/{ => src}/global2D/ODFaceRecognizer.cpp (100%) rename detectors/{ => src}/global2D/detection/ODCascadeDetector.cpp (98%) rename detectors/{ => src}/global2D/detection/ODHOGDetector.cpp (100%) rename detectors/{ => src}/global2D/training/ODHOGTrainer.cpp (100%) create mode 100644 detectors/src/global3D/CMakeLists.txt rename detectors/{ => src}/global3D/ODPointCloudGlobalMatching.cpp (100%) rename detectors/{ => src}/global3D/detection/ODCADDetector3DGlobal.cpp (100%) rename detectors/{ => src}/global3D/training/ODCADDetectTrainer3DGlobal.cpp (100%) rename detectors/{ => src}/local2D/CMakeLists.txt (56%) rename detectors/{ => src}/local2D/ODImageLocalMatching.cpp (100%) rename detectors/{ => src}/local2D/detection/ODCADRecognizer2DLocal.cpp (99%) rename detectors/{ => src}/local2D/training/ODCADRecogTrainerSnapshotBased.cpp (99%) rename detectors/{ => src}/misc/CMakeLists.txt (52%) rename detectors/{ => src}/misc/detection/ODDetectorMultiAlgo.cpp (98%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 54a8f8ca..d8d34384 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,7 +26,7 @@ option(WITH_GPU "Build GPU components of the project" ON) option(WITH_SVMLIGHT "Build SVM Light" OFF) # Set modules -set(OD_MODULES_NAMES 3rdparty common doc detectors examples ) +set(OD_MODULES_NAMES 3rdparty common doc detectors) set(OD_MODULES_DIRS ${OD_MODULES_NAMES}) # Add modules diff --git a/cmake/od_macros.cmake b/cmake/od_macros.cmake index 87de5dff..126c2a97 100644 --- a/cmake/od_macros.cmake +++ b/cmake/od_macros.cmake @@ -130,4 +130,8 @@ ENDMACRO() MACRO(SUBDIRS list) FILE(GLOB list RELATIVE ${curdir} ${curdir}/*) +ENDMACRO() + +MACRO(SUBDIRLIST result curdir) + FILE(GLOB result RELATIVE ${curdir} ${curdir}/*) ENDMACRO() \ No newline at end of file diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 53382e0b..c174640d 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -11,45 +11,35 @@ set(build TRUE) if(build) - set(submodules - pipeline - ) - - set(incs - "pipeline/ObjectDetector.h" - "pipeline/ODAlgorithmBase.h" - - "pipeline/ODScene.h" - "pipeline/ODDetection.h" - - "pipeline/ODTrainer.h" - "pipeline/ODDetector.h" - - "utils/ODFrameGenerator.h" - "utils/utils.h" - "utils/ODFeatureDetector2D.h" - - "bindings/svmlight.h" + set(INCLUDES + "include/common/pipeline/ObjectDetector.h" + "include/common/pipeline/ODAlgorithmBase.h" + "include/common/pipeline/ODScene.h" + "include/common/pipeline/ODDetection.h" + "include/common/pipeline/ODTrainer.h" + "include/common/pipeline/ODDetector.h" + "include/common/utils/ODFrameGenerator.h" + "include/common/utils/utils.h" + "include/common/utils/ODFeatureDetector2D.h" + "include/common/bindings/svmlight.h" ) - set(impl_incs - ) + set(IMPL_INCLUDES "") - set(srcs - "pipeline/ObjectDetector.cpp" - "pipeline/ODScene.cpp" - "utils/utils.cpp" - "utils/ODFeatureDetector2D.cpp" + set(SOURCES + "src/pipeline/ObjectDetector.cpp" + "src/pipeline/ODScene.cpp" + "src/utils/utils.cpp" + "src/utils/ODFeatureDetector2D.cpp" ) - OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${srcs} INCS ${incs} ${impl_incs}) - install(FILES ${incs} DESTINATION ${OD_INSTALL_INCLUDE_DIR}/${SUBSYS_NAME} COMPONENT ${LIB_NAME}) + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) + + OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${SOURCES} INCS ${INCLUDES} ${IMPL_INCLUDES}) + install(FILES ${INCLUDES} DESTINATION ${OD_INSTALL_INCLUDE_DIR}/${SUBSYS_NAME} COMPONENT ${LIB_NAME}) if(SUBSYS_DEPS) target_link_libraries("${LIB_NAME}" ${SUBSYS_DEPS}) endif(SUBSYS_DEPS) - #PCL_MAKE_PKGCONFIG("${LIB_NAME}" "${SUBSYS_NAME}" "${SUBSYS_DESC}" "${SUBSYS_DEPS}" "" "" "" "") - - endif(build) \ No newline at end of file diff --git a/common/bindings/svmlight.h b/common/include/common/bindings/svmlight.h similarity index 100% rename from common/bindings/svmlight.h rename to common/include/common/bindings/svmlight.h diff --git a/common/pipeline/ODAlgorithmBase.h b/common/include/common/pipeline/ODAlgorithmBase.h similarity index 100% rename from common/pipeline/ODAlgorithmBase.h rename to common/include/common/pipeline/ODAlgorithmBase.h diff --git a/common/pipeline/ODDetection.h b/common/include/common/pipeline/ODDetection.h similarity index 100% rename from common/pipeline/ODDetection.h rename to common/include/common/pipeline/ODDetection.h diff --git a/common/pipeline/ODDetector.h b/common/include/common/pipeline/ODDetector.h similarity index 100% rename from common/pipeline/ODDetector.h rename to common/include/common/pipeline/ODDetector.h diff --git a/common/pipeline/ODScene.h b/common/include/common/pipeline/ODScene.h similarity index 100% rename from common/pipeline/ODScene.h rename to common/include/common/pipeline/ODScene.h diff --git a/common/pipeline/ODTrainer.h b/common/include/common/pipeline/ODTrainer.h similarity index 100% rename from common/pipeline/ODTrainer.h rename to common/include/common/pipeline/ODTrainer.h diff --git a/common/pipeline/ObjectDetector.h b/common/include/common/pipeline/ObjectDetector.h similarity index 100% rename from common/pipeline/ObjectDetector.h rename to common/include/common/pipeline/ObjectDetector.h diff --git a/common/utils/ODFeatureDetector2D.h b/common/include/common/utils/ODFeatureDetector2D.h similarity index 100% rename from common/utils/ODFeatureDetector2D.h rename to common/include/common/utils/ODFeatureDetector2D.h diff --git a/common/utils/ODFrameGenerator.h b/common/include/common/utils/ODFrameGenerator.h similarity index 98% rename from common/utils/ODFrameGenerator.h rename to common/include/common/utils/ODFrameGenerator.h index 42d0b312..2fdf5fec 100644 --- a/common/utils/ODFrameGenerator.h +++ b/common/include/common/utils/ODFrameGenerator.h @@ -25,7 +25,7 @@ namespace od * getNextFrame() to get an instance of next scene of SceneType. getNextFrame() returns valid scenes until all scenes matched are exhausted - the time when 'isValid()' is false. * * \tparam SceneT One of the Scene classes - ODSceneImage or ODScenePointCloud - * \tparam TYPE TYPE can be GENERATOR_TYPE_FILE_LIST which means you provide the list of scene files to be returned by the FrameGenerator in the constructor (for eg. \/home/username/pics/*.jpg) + * \tparam TYPE TYPE can be GENERATOR_TYPE_FILE_LIST which means you provide the list of scene files to be returned by the FrameGenerator in the constructor (for eg. \/home/username/pics/\*.jpg) * Or it can be GENERATOR_TYPE_DEVICE which picks up the webcam or the kinect based on the SceneType. * \author Kripasindhu Sarkar * diff --git a/common/utils/utils.h b/common/include/common/utils/utils.h similarity index 100% rename from common/utils/utils.h rename to common/include/common/utils/utils.h diff --git a/common/pipeline/ODScene.cpp b/common/src/pipeline/ODScene.cpp similarity index 97% rename from common/pipeline/ODScene.cpp rename to common/src/pipeline/ODScene.cpp index 9fb6709a..c8fd70dc 100644 --- a/common/pipeline/ODScene.cpp +++ b/common/src/pipeline/ODScene.cpp @@ -28,4 +28,4 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 10.06.15. // -#include "ODScene.h" +#include "common/pipeline/ODScene.h" diff --git a/common/pipeline/ObjectDetector.cpp b/common/src/pipeline/ObjectDetector.cpp similarity index 97% rename from common/pipeline/ObjectDetector.cpp rename to common/src/pipeline/ObjectDetector.cpp index b5627401..c7493be1 100644 --- a/common/pipeline/ObjectDetector.cpp +++ b/common/src/pipeline/ObjectDetector.cpp @@ -28,4 +28,4 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 03.06.15. // -#include "ObjectDetector.h" +#include "common/pipeline/ObjectDetector.h" diff --git a/common/pipeline/ODDetector.cpp b/common/src/utils/ODDetector.cpp similarity index 100% rename from common/pipeline/ODDetector.cpp rename to common/src/utils/ODDetector.cpp diff --git a/common/utils/ODFeatureDetector2D.cpp b/common/src/utils/ODFeatureDetector2D.cpp similarity index 99% rename from common/utils/ODFeatureDetector2D.cpp rename to common/src/utils/ODFeatureDetector2D.cpp index 33ed6df1..ffb0edff 100644 --- a/common/utils/ODFeatureDetector2D.cpp +++ b/common/src/utils/ODFeatureDetector2D.cpp @@ -3,7 +3,7 @@ // #include <GL/gl.h> -#include "ODFeatureDetector2D.h" +#include "common/utils/ODFeatureDetector2D.h" using namespace std; namespace od diff --git a/common/utils/utils.cpp b/common/src/utils/utils.cpp similarity index 99% rename from common/utils/utils.cpp rename to common/src/utils/utils.cpp index f00f1b68..4e45fb8b 100644 --- a/common/utils/utils.cpp +++ b/common/src/utils/utils.cpp @@ -1,7 +1,7 @@ // // Created by sarkar on 19.06.15. // -#include "utils.h" +#include "common/utils/utils.h" #include "opencv2/imgproc/imgproc.hpp" #include <boost/functional/hash.hpp> diff --git a/detectors/CMakeLists.txt b/detectors/CMakeLists.txt index e58544ac..445a9366 100644 --- a/detectors/CMakeLists.txt +++ b/detectors/CMakeLists.txt @@ -1,4 +1,9 @@ -add_subdirectory(local2D) -add_subdirectory(global2D) -add_subdirectory(global3D) -add_subdirectory(misc) +set(DETECTORS_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(DETECTORS_INCLUDE_DIR ${DETECTORS_DIR}/include) +set(DETECTORS_IMPL_DIR ${DETECTORS_DIR}/impl) + +add_subdirectory(simple_ransac_detection) +add_subdirectory("src/local2D") +add_subdirectory("src/global2D") +add_subdirectory("src/global3D") +add_subdirectory("src/misc") diff --git a/detectors/global2D/CMakeLists.txt b/detectors/global2D/CMakeLists.txt deleted file mode 100644 index 43fafa08..00000000 --- a/detectors/global2D/CMakeLists.txt +++ /dev/null @@ -1,65 +0,0 @@ -set(SUBSYS_NAME global_image_detector) -set(LIB_NAME od_${SUBSYS_NAME}) -set(SUBSYS_DESC "global feature based detection in 2D images") - -set(SUBSYS_DEPS od_common ${OpenCV_LIBS}) - -set(build TRUE) -#PCL_SUBSYS_OPTION(build "${SUBSYS_NAME}" "${SUBSYS_DESC}" ON) -#PCL_SUBSYS_DEPEND(build "${SUBSYS_NAME}" DEPS ${SUBSYS_DEPS}) - - -if(build) - - - set(impl_incs - ) -if(WITH_SVMLIGHT) - set(srcs - ODFaceRecognizer.cpp - "detection/ODHOGDetector.cpp" - "detection/ODCascadeDetector.cpp" - "training/ODHOGTrainer.cpp" - ) - set(incs - "ODFaceRecognizer.h" - "detection/ODHOGDetector.h" - "detection/ODCascadeDetector.h" - "training/ODHOGTrainer.h" - ) - - -else() - set(srcs - ODFaceRecognizer.cpp - "detection/ODCascadeDetector.cpp" - ) - - set(incs - "ODFaceRecognizer.h" - "detection/ODCascadeDetector.h" - ) - -endif() - - - - OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${srcs} INCS ${incs} ${impl_incs}) - install(FILES ${incs} DESTINATION ${OD_INSTALL_INCLUDE_DIR}/${SUBSYS_NAME} COMPONENT ${LIB_NAME} ) - -if(WITH_SVMLIGHT) - if(SUBSYS_DEPS) - target_link_libraries("${LIB_NAME}" ${SUBSYS_DEPS} svm) - endif(SUBSYS_DEPS) -else() - if(SUBSYS_DEPS) - target_link_libraries("${LIB_NAME}" ${SUBSYS_DEPS} ) - endif(SUBSYS_DEPS) -endif() - - - - #PCL_MAKE_PKGCONFIG("${LIB_NAME}" "${SUBSYS_NAME}" "${SUBSYS_DESC}" "${SUBSYS_DEPS}" "" "" "" "") - - -endif(build) \ No newline at end of file diff --git a/detectors/global3D/CMakeLists.txt b/detectors/global3D/CMakeLists.txt deleted file mode 100644 index 42a1dbdd..00000000 --- a/detectors/global3D/CMakeLists.txt +++ /dev/null @@ -1,51 +0,0 @@ -set(SUBSYS_NAME pointcloud_global_detector) -set(LIB_NAME od_${SUBSYS_NAME}) -set(SUBSYS_DESC "The global detector for point cloouds") -set(SUBSYS_DEPS od_common ${PCL_LIBRARIES} ${VTK_LIBRARIES} ${OpenCV_LIBS} siftgpu) - -set(build TRUE) -#PCL_SUBSYS_OPTION(build "${SUBSYS_NAME}" "${SUBSYS_DESC}" ON) -#PCL_SUBSYS_DEPEND(build "${SUBSYS_NAME}" DEPS ${SUBSYS_DEPS}) - - -if(build) - - #add_subdirectory(detection/simple_ransac_detection) - - set(incs - "ODPointCloudGlobalMatching.h" - - "detection/ODCADDetector3DGlobal.h" - "training/ODCADDetectTrainer3DGlobal.h" - ) - - set(impl_incs - "detection/ODCADDetector3DGlobal.hpp" - ) - - set(srcs - "ODPointCloudGlobalMatching.cpp" - - "detection/ODCADDetector3DGlobal.cpp" - "training/ODCADDetectTrainer3DGlobal.cpp" - - #${SUB_IMPL_SRCS} - ) - - #include all the individual implementation dirs - - - #set mandatory directories - include_directories("${OD_SOURCE_DIR}" ) - - OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${srcs} INCS ${incs} ${impl_incs}) - install(FILES ${incs} DESTINATION ${OD_INSTALL_INCLUDE_DIR}/${SUBSYS_NAME} COMPONENT ${LIB_NAME}) - - if(SUBSYS_DEPS) - target_link_libraries("${LIB_NAME}" ${SUBSYS_DEPS}) - endif(SUBSYS_DEPS) - - #PCL_MAKE_PKGCONFIG("${LIB_NAME}" "${SUBSYS_NAME}" "${SUBSYS_DESC}" "${SUBSYS_DEPS}" "" "" "" "") - - -endif(build) \ No newline at end of file diff --git a/detectors/global3D/detection/ODCADDetector3DGlobal.hpp b/detectors/impl/detection/ODCADDetector3DGlobal.hpp similarity index 100% rename from detectors/global3D/detection/ODCADDetector3DGlobal.hpp rename to detectors/impl/detection/ODCADDetector3DGlobal.hpp diff --git a/detectors/global2D/ODFaceRecognizer.h b/detectors/include/detectors/global2D/ODFaceRecognizer.h similarity index 100% rename from detectors/global2D/ODFaceRecognizer.h rename to detectors/include/detectors/global2D/ODFaceRecognizer.h diff --git a/detectors/global2D/detection/ODCascadeDetector.h b/detectors/include/detectors/global2D/detection/ODCascadeDetector.h similarity index 100% rename from detectors/global2D/detection/ODCascadeDetector.h rename to detectors/include/detectors/global2D/detection/ODCascadeDetector.h diff --git a/detectors/global2D/detection/ODHOGDetector.h b/detectors/include/detectors/global2D/detection/ODHOGDetector.h similarity index 100% rename from detectors/global2D/detection/ODHOGDetector.h rename to detectors/include/detectors/global2D/detection/ODHOGDetector.h diff --git a/detectors/global2D/training/ODHOGTrainer.h b/detectors/include/detectors/global2D/training/ODHOGTrainer.h similarity index 100% rename from detectors/global2D/training/ODHOGTrainer.h rename to detectors/include/detectors/global2D/training/ODHOGTrainer.h diff --git a/detectors/global3D/ODPointCloudGlobalMatching.h b/detectors/include/detectors/global3D/ODPointCloudGlobalMatching.h similarity index 100% rename from detectors/global3D/ODPointCloudGlobalMatching.h rename to detectors/include/detectors/global3D/ODPointCloudGlobalMatching.h diff --git a/detectors/global3D/detection/ODCADDetector3DGlobal.h b/detectors/include/detectors/global3D/detection/ODCADDetector3DGlobal.h similarity index 100% rename from detectors/global3D/detection/ODCADDetector3DGlobal.h rename to detectors/include/detectors/global3D/detection/ODCADDetector3DGlobal.h diff --git a/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h b/detectors/include/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h similarity index 100% rename from detectors/global3D/training/ODCADDetectTrainer3DGlobal.h rename to detectors/include/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h diff --git a/detectors/local2D/ODImageLocalMatching.h b/detectors/include/detectors/local2D/ODImageLocalMatching.h similarity index 100% rename from detectors/local2D/ODImageLocalMatching.h rename to detectors/include/detectors/local2D/ODImageLocalMatching.h diff --git a/detectors/local2D/detection/ODCADRecognizer2DLocal.h b/detectors/include/detectors/local2D/detection/ODCADRecognizer2DLocal.h similarity index 99% rename from detectors/local2D/detection/ODCADRecognizer2DLocal.h rename to detectors/include/detectors/local2D/detection/ODCADRecognizer2DLocal.h index 2b3313d2..15dd4b73 100644 --- a/detectors/local2D/detection/ODCADRecognizer2DLocal.h +++ b/detectors/include/detectors/local2D/detection/ODCADRecognizer2DLocal.h @@ -31,7 +31,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef OPENDETECTION_SIMPLERANSACDETECTOR_H #define OPENDETECTION_SIMPLERANSACDETECTOR_H -#include <detectors/local2D/ODImageLocalMatching.h> +#include "ODImageLocalMatching.h" #include "common/pipeline/ODDetector.h" #include "common/pipeline/ODScene.h" diff --git a/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h b/detectors/include/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h similarity index 98% rename from detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h rename to detectors/include/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h index 42682489..adf204da 100644 --- a/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h +++ b/detectors/include/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h @@ -34,7 +34,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "common/pipeline/ODTrainer.h" #include "common/utils/utils.h" -#include "detectors/local2D/ODImageLocalMatching.h" +#include "ODImageLocalMatching.h" #define VIEW_ANGLE 30 diff --git a/detectors/misc/detection/ODDetectorMultiAlgo.h b/detectors/include/detectors/misc/detection/ODDetectorMultiAlgo.h similarity index 100% rename from detectors/misc/detection/ODDetectorMultiAlgo.h rename to detectors/include/detectors/misc/detection/ODDetectorMultiAlgo.h diff --git a/detectors/local2D/detection/simple_ransac_detection/CMakeLists.txt b/detectors/simple_ransac_detection/CMakeLists.txt similarity index 100% rename from detectors/local2D/detection/simple_ransac_detection/CMakeLists.txt rename to detectors/simple_ransac_detection/CMakeLists.txt diff --git a/detectors/local2D/detection/simple_ransac_detection/CsvReader.cpp b/detectors/simple_ransac_detection/CsvReader.cpp similarity index 100% rename from detectors/local2D/detection/simple_ransac_detection/CsvReader.cpp rename to detectors/simple_ransac_detection/CsvReader.cpp diff --git a/detectors/local2D/detection/simple_ransac_detection/CsvReader.h b/detectors/simple_ransac_detection/CsvReader.h similarity index 92% rename from detectors/local2D/detection/simple_ransac_detection/CsvReader.h rename to detectors/simple_ransac_detection/CsvReader.h index 3e8c4989..6f7774b6 100644 --- a/detectors/local2D/detection/simple_ransac_detection/CsvReader.h +++ b/detectors/simple_ransac_detection/CsvReader.h @@ -25,7 +25,7 @@ class CsvReader { * Read a plane text file with .ply format * * @param list_vertex - The container of the vertices list of the mesh - * @param list_triangle - The container of the triangles list of the mesh + * @param list_triangles - The container of the triangles list of the mesh * @return */ void readPLY(vector<Point3f> &list_vertex, vector<vector<int> > &list_triangles); diff --git a/detectors/local2D/detection/simple_ransac_detection/CsvWriter.cpp b/detectors/simple_ransac_detection/CsvWriter.cpp similarity index 100% rename from detectors/local2D/detection/simple_ransac_detection/CsvWriter.cpp rename to detectors/simple_ransac_detection/CsvWriter.cpp diff --git a/detectors/local2D/detection/simple_ransac_detection/CsvWriter.h b/detectors/simple_ransac_detection/CsvWriter.h similarity index 100% rename from detectors/local2D/detection/simple_ransac_detection/CsvWriter.h rename to detectors/simple_ransac_detection/CsvWriter.h diff --git a/detectors/local2D/detection/simple_ransac_detection/Mesh.cpp b/detectors/simple_ransac_detection/Mesh.cpp similarity index 100% rename from detectors/local2D/detection/simple_ransac_detection/Mesh.cpp rename to detectors/simple_ransac_detection/Mesh.cpp diff --git a/detectors/local2D/detection/simple_ransac_detection/Mesh.h b/detectors/simple_ransac_detection/Mesh.h similarity index 100% rename from detectors/local2D/detection/simple_ransac_detection/Mesh.h rename to detectors/simple_ransac_detection/Mesh.h diff --git a/detectors/local2D/detection/simple_ransac_detection/Model.cpp b/detectors/simple_ransac_detection/Model.cpp similarity index 100% rename from detectors/local2D/detection/simple_ransac_detection/Model.cpp rename to detectors/simple_ransac_detection/Model.cpp diff --git a/detectors/local2D/detection/simple_ransac_detection/Model.h b/detectors/simple_ransac_detection/Model.h similarity index 100% rename from detectors/local2D/detection/simple_ransac_detection/Model.h rename to detectors/simple_ransac_detection/Model.h diff --git a/detectors/local2D/detection/simple_ransac_detection/ModelRegistration.cpp b/detectors/simple_ransac_detection/ModelRegistration.cpp similarity index 100% rename from detectors/local2D/detection/simple_ransac_detection/ModelRegistration.cpp rename to detectors/simple_ransac_detection/ModelRegistration.cpp diff --git a/detectors/local2D/detection/simple_ransac_detection/ModelRegistration.h b/detectors/simple_ransac_detection/ModelRegistration.h similarity index 100% rename from detectors/local2D/detection/simple_ransac_detection/ModelRegistration.h rename to detectors/simple_ransac_detection/ModelRegistration.h diff --git a/detectors/local2D/detection/simple_ransac_detection/PnPProblem.cpp b/detectors/simple_ransac_detection/PnPProblem.cpp similarity index 100% rename from detectors/local2D/detection/simple_ransac_detection/PnPProblem.cpp rename to detectors/simple_ransac_detection/PnPProblem.cpp diff --git a/detectors/local2D/detection/simple_ransac_detection/PnPProblem.h b/detectors/simple_ransac_detection/PnPProblem.h similarity index 100% rename from detectors/local2D/detection/simple_ransac_detection/PnPProblem.h rename to detectors/simple_ransac_detection/PnPProblem.h diff --git a/detectors/local2D/detection/simple_ransac_detection/RobustMatcher.cpp b/detectors/simple_ransac_detection/RobustMatcher.cpp similarity index 100% rename from detectors/local2D/detection/simple_ransac_detection/RobustMatcher.cpp rename to detectors/simple_ransac_detection/RobustMatcher.cpp diff --git a/detectors/local2D/detection/simple_ransac_detection/RobustMatcher.h b/detectors/simple_ransac_detection/RobustMatcher.h similarity index 100% rename from detectors/local2D/detection/simple_ransac_detection/RobustMatcher.h rename to detectors/simple_ransac_detection/RobustMatcher.h diff --git a/detectors/local2D/detection/simple_ransac_detection/Utils.cpp b/detectors/simple_ransac_detection/Utils.cpp similarity index 100% rename from detectors/local2D/detection/simple_ransac_detection/Utils.cpp rename to detectors/simple_ransac_detection/Utils.cpp diff --git a/detectors/local2D/detection/simple_ransac_detection/Utils.h b/detectors/simple_ransac_detection/Utils.h similarity index 100% rename from detectors/local2D/detection/simple_ransac_detection/Utils.h rename to detectors/simple_ransac_detection/Utils.h diff --git a/detectors/local2D/detection/simple_ransac_detection/main_detection_image_wo_k.cpp b/detectors/simple_ransac_detection/main_detection_image_wo_k.cpp similarity index 100% rename from detectors/local2D/detection/simple_ransac_detection/main_detection_image_wo_k.cpp rename to detectors/simple_ransac_detection/main_detection_image_wo_k.cpp diff --git a/detectors/local2D/detection/simple_ransac_detection/main_detection_video.cpp b/detectors/simple_ransac_detection/main_detection_video.cpp similarity index 100% rename from detectors/local2D/detection/simple_ransac_detection/main_detection_video.cpp rename to detectors/simple_ransac_detection/main_detection_video.cpp diff --git a/detectors/local2D/detection/simple_ransac_detection/main_registration.cpp b/detectors/simple_ransac_detection/main_registration.cpp similarity index 100% rename from detectors/local2D/detection/simple_ransac_detection/main_registration.cpp rename to detectors/simple_ransac_detection/main_registration.cpp diff --git a/detectors/local2D/detection/simple_ransac_detection/scale_intrinsic.cpp b/detectors/simple_ransac_detection/scale_intrinsic.cpp similarity index 100% rename from detectors/local2D/detection/simple_ransac_detection/scale_intrinsic.cpp rename to detectors/simple_ransac_detection/scale_intrinsic.cpp diff --git a/detectors/src/global2D/CMakeLists.txt b/detectors/src/global2D/CMakeLists.txt new file mode 100644 index 00000000..203f9a6c --- /dev/null +++ b/detectors/src/global2D/CMakeLists.txt @@ -0,0 +1,62 @@ +set(SUBSYS_NAME global_image_detector) +set(LIB_NAME od_${SUBSYS_NAME}) +set(SUBSYS_DESC "global feature based detection in 2D images") + +set(SUBSYS_DEPS od_common ${OpenCV_LIBS}) + +set(build TRUE) +#PCL_SUBSYS_OPTION(build "${SUBSYS_NAME}" "${SUBSYS_DESC}" ON) +#PCL_SUBSYS_DEPEND(build "${SUBSYS_NAME}" DEPS ${SUBSYS_DEPS}) + + +if(build) + set(IMPL_INCLUDES "") + + if(WITH_SVMLIGHT) + set(SOURCES + "ODFaceRecognizer.cpp" + "detection/ODHOGDetector.cpp" + "detection/ODCascadeDetector.cpp" + "training/ODHOGTrainer.cpp" + ) + set(INCLUDES + "${DETECTORS_INCLUDE_DIR}/detectors/global2D/DFaceRecognizer.h" + "${DETECTORS_INCLUDE_DIR}/detectors/global2D/detection/ODHOGDetector.h" + "${DETECTORS_INCLUDE_DIR}/detectors/global2D/detection/ODCascadeDetector.h" + "${DETECTORS_INCLUDE_DIR}/detectors/global2D/training/ODHOGTrainer.h" + ) + else() + set(SOURCES + "ODFaceRecognizer.cpp" + "detection/ODCascadeDetector.cpp" + ) + + set(INCLUDES + "${DETECTORS_INCLUDE_DIR}/detectors/global2D/ODFaceRecognizer.h" + "${DETECTORS_INCLUDE_DIR}/detectors/global2D/detection/ODCascadeDetector.h" + ) + + endif() + + include_directories(${DETECTORS_INCLUDE_DIR}/detectors/global2D) + include_directories(${OD_SOURCE_DIR}/common/include) + + OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${SOURCES} INCS ${INCLUDES} ${impl_incs}) + install(FILES ${INCLUDES} DESTINATION ${OD_INSTALL_INCLUDE_DIR}/${SUBSYS_NAME} COMPONENT ${LIB_NAME} ) + + if(WITH_SVMLIGHT) + if(SUBSYS_DEPS) + target_link_libraries("${LIB_NAME}" ${SUBSYS_DEPS} svm) + endif(SUBSYS_DEPS) + else() + if(SUBSYS_DEPS) + target_link_libraries("${LIB_NAME}" ${SUBSYS_DEPS} ) + endif(SUBSYS_DEPS) + endif() + + + + #PCL_MAKE_PKGCONFIG("${LIB_NAME}" "${SUBSYS_NAME}" "${SUBSYS_DESC}" "${SUBSYS_DEPS}" "" "" "" "") + + +endif(build) \ No newline at end of file diff --git a/detectors/global2D/ODFaceRecognizer.cpp b/detectors/src/global2D/ODFaceRecognizer.cpp similarity index 100% rename from detectors/global2D/ODFaceRecognizer.cpp rename to detectors/src/global2D/ODFaceRecognizer.cpp diff --git a/detectors/global2D/detection/ODCascadeDetector.cpp b/detectors/src/global2D/detection/ODCascadeDetector.cpp similarity index 98% rename from detectors/global2D/detection/ODCascadeDetector.cpp rename to detectors/src/global2D/detection/ODCascadeDetector.cpp index 5bbd23c0..defefad0 100644 --- a/detectors/global2D/detection/ODCascadeDetector.cpp +++ b/detectors/src/global2D/detection/ODCascadeDetector.cpp @@ -27,7 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 17.07.15. // -#include "ODCascadeDetector.h" +#include "detection/ODCascadeDetector.h" using namespace cv; using namespace std; diff --git a/detectors/global2D/detection/ODHOGDetector.cpp b/detectors/src/global2D/detection/ODHOGDetector.cpp similarity index 100% rename from detectors/global2D/detection/ODHOGDetector.cpp rename to detectors/src/global2D/detection/ODHOGDetector.cpp diff --git a/detectors/global2D/training/ODHOGTrainer.cpp b/detectors/src/global2D/training/ODHOGTrainer.cpp similarity index 100% rename from detectors/global2D/training/ODHOGTrainer.cpp rename to detectors/src/global2D/training/ODHOGTrainer.cpp diff --git a/detectors/src/global3D/CMakeLists.txt b/detectors/src/global3D/CMakeLists.txt new file mode 100644 index 00000000..28f5397e --- /dev/null +++ b/detectors/src/global3D/CMakeLists.txt @@ -0,0 +1,42 @@ +set(SUBSYS_NAME pointcloud_global_detector) +set(LIB_NAME od_${SUBSYS_NAME}) +set(SUBSYS_DESC "The global detector for point cloouds") +set(SUBSYS_DEPS od_common ${PCL_LIBRARIES} ${VTK_LIBRARIES} ${OpenCV_LIBS} siftgpu) + +set(build TRUE) +#PCL_SUBSYS_OPTION(build "${SUBSYS_NAME}" "${SUBSYS_DESC}" ON) +#PCL_SUBSYS_DEPEND(build "${SUBSYS_NAME}" DEPS ${SUBSYS_DEPS}) + + +if(build) + + set(INCLUDES + "${DETECTORS_INCLUDE_DIR}/detectors/global3D/ODPointCloudGlobalMatching.h" + "${DETECTORS_INCLUDE_DIR}/detectors/global3D/detection/ODCADDetector3DGlobal.h" + "${DETECTORS_INCLUDE_DIR}/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h" + ) + + set(IMPL_INCLUDES + "${DETECTORS_IMPL_DIR}/detection/ODCADDetector3DGlobal.hpp" + ) + + set(srcs + "src/ODPointCloudGlobalMatching.cpp" + "src/detection/ODCADDetector3DGlobal.cpp" + "src/training/ODCADDetectTrainer3DGlobal.cpp" + ) + + include_directories(${DETECTORS_INCLUDE_DIR}/detectors/global3D) + include_directories(${OD_SOURCE_DIR}/common/include) + + OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${SOURCES} INCS ${INCLUDES} ${IMPL_INCLUDES}) + install(FILES ${INCLUDES} DESTINATION ${OD_INSTALL_INCLUDE_DIR}/${SUBSYS_NAME} COMPONENT ${LIB_NAME}) + + if(SUBSYS_DEPS) + target_link_libraries("${LIB_NAME}" ${SUBSYS_DEPS}) + endif(SUBSYS_DEPS) + + #PCL_MAKE_PKGCONFIG("${LIB_NAME}" "${SUBSYS_NAME}" "${SUBSYS_DESC}" "${SUBSYS_DEPS}" "" "" "" "") + + +endif(build) \ No newline at end of file diff --git a/detectors/global3D/ODPointCloudGlobalMatching.cpp b/detectors/src/global3D/ODPointCloudGlobalMatching.cpp similarity index 100% rename from detectors/global3D/ODPointCloudGlobalMatching.cpp rename to detectors/src/global3D/ODPointCloudGlobalMatching.cpp diff --git a/detectors/global3D/detection/ODCADDetector3DGlobal.cpp b/detectors/src/global3D/detection/ODCADDetector3DGlobal.cpp similarity index 100% rename from detectors/global3D/detection/ODCADDetector3DGlobal.cpp rename to detectors/src/global3D/detection/ODCADDetector3DGlobal.cpp diff --git a/detectors/global3D/training/ODCADDetectTrainer3DGlobal.cpp b/detectors/src/global3D/training/ODCADDetectTrainer3DGlobal.cpp similarity index 100% rename from detectors/global3D/training/ODCADDetectTrainer3DGlobal.cpp rename to detectors/src/global3D/training/ODCADDetectTrainer3DGlobal.cpp diff --git a/detectors/local2D/CMakeLists.txt b/detectors/src/local2D/CMakeLists.txt similarity index 56% rename from detectors/local2D/CMakeLists.txt rename to detectors/src/local2D/CMakeLists.txt index df897e4a..595448ae 100644 --- a/detectors/local2D/CMakeLists.txt +++ b/detectors/src/local2D/CMakeLists.txt @@ -13,32 +13,27 @@ set(build TRUE) if(build) - - #include all the individual implementation dirs - add_subdirectory(detection/simple_ransac_detection) - - set(incs - "ODImageLocalMatching.h" - - "training/ODCADRecogTrainerSnapshotBased.h" - "detection/ODCADRecognizer2DLocal.h" - + + set(INCLUDES + "${DETECTORS_INCLUDE_DIR}/detectors/local2D/ODImageLocalMatching.h" + "${DETECTORS_INCLUDE_DIR}/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h" + "${DETECTORS_INCLUDE_DIR}/detectors/local2D/detection/ODCADRecognizer2DLocal.h" ) - set(impl_incs - ) + set(IMPL_INCLUDES "") - set(srcs + set(SOURCES "ODImageLocalMatching.cpp" "training/ODCADRecogTrainerSnapshotBased.cpp" "detection/ODCADRecognizer2DLocal.cpp" - - ${SUB_IMPL_SRCS} ) + include_directories(${DETECTORS_INCLUDE_DIR}/detectors/local2D) + include_directories(${OD_SOURCE_DIR}/common/include) + include_directories(${DETECTORS_DIR}) - OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${srcs} INCS ${incs} ${impl_incs}) - install(FILES ${incs} DESTINATION ${OD_INSTALL_INCLUDE_DIR}/${SUBSYS_NAME} COMPONENT ${LIB_NAME}) + OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${SOURCES} INCS ${INCLUDES} ${IMPL_INCLUDES}) + install(FILES ${INCLUDES} DESTINATION ${OD_INSTALL_INCLUDE_DIR}/${SUBSYS_NAME} COMPONENT ${LIB_NAME}) if(SUBSYS_DEPS) target_link_libraries("${LIB_NAME}" ${SUBSYS_DEPS} pugixml) @@ -46,5 +41,4 @@ if(build) #PCL_MAKE_PKGCONFIG("${LIB_NAME}" "${SUBSYS_NAME}" "${SUBSYS_DESC}" "${SUBSYS_DEPS}" "" "" "" "") - endif(build) \ No newline at end of file diff --git a/detectors/local2D/ODImageLocalMatching.cpp b/detectors/src/local2D/ODImageLocalMatching.cpp similarity index 100% rename from detectors/local2D/ODImageLocalMatching.cpp rename to detectors/src/local2D/ODImageLocalMatching.cpp diff --git a/detectors/local2D/detection/ODCADRecognizer2DLocal.cpp b/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp similarity index 99% rename from detectors/local2D/detection/ODCADRecognizer2DLocal.cpp rename to detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp index 078f82a2..65357fb7 100644 --- a/detectors/local2D/detection/ODCADRecognizer2DLocal.cpp +++ b/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp @@ -28,7 +28,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 08.06.15. // -#include "ODCADRecognizer2DLocal.h" +#include "detection/ODCADRecognizer2DLocal.h" #include <opencv2/highgui/highgui.hpp> diff --git a/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.cpp b/detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp similarity index 99% rename from detectors/local2D/training/ODCADRecogTrainerSnapshotBased.cpp rename to detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp index 3570df57..402eb0c1 100644 --- a/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.cpp +++ b/detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp @@ -24,7 +24,7 @@ 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. */ -#include "ODCADRecogTrainerSnapshotBased.h" +#include "training/ODCADRecogTrainerSnapshotBased.h" //simplecube/newcube.obj simplecube/flower.jpeg //Lion/Final.obj Lion/Texture.png //BigDaddy/Param.obj BigDaddy/Parameterization.png diff --git a/detectors/misc/CMakeLists.txt b/detectors/src/misc/CMakeLists.txt similarity index 52% rename from detectors/misc/CMakeLists.txt rename to detectors/src/misc/CMakeLists.txt index 65c8a4d0..93c43d07 100644 --- a/detectors/misc/CMakeLists.txt +++ b/detectors/src/misc/CMakeLists.txt @@ -1,31 +1,32 @@ set(SUBSYS_NAME miscellaneous_detector) set(LIB_NAME od_${SUBSYS_NAME}) set(SUBSYS_DESC "global feature based detection in 2D images") - set(SUBSYS_DEPS od_common od_global_image_detector od_local_image_detector ${OpenCV_LIBS}) - set(build TRUE) #PCL_SUBSYS_OPTION(build "${SUBSYS_NAME}" "${SUBSYS_DESC}" ON) #PCL_SUBSYS_DEPEND(build "${SUBSYS_NAME}" DEPS ${SUBSYS_DEPS}) - if(build) - set(incs - "detection/ODDetectorMultiAlgo.h" + set(INCLUDES + "${DETECTORS_INCLUDE_DIR}/detectors/misc/detection/ODDetectorMultiAlgo.h" ) - set(impl_incs - ) + set(IMPL_INCLUDES "") - set(srcs - "detection/ODDetectorMultiAlgo.cpp" + set(SOURCES + "detection/ODDetectorMultiAlgo.cpp" ) + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${DETECTORS_INCLUDE_DIR}/detectors/local2D) + include_directories(${OD_SOURCE_DIR}/common/include) + include_directories(${DETECTORS_DIR}) + include_directories(${DETECTORS_IMPL_DIR}/detection/) - OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${srcs} INCS ${incs} ${impl_incs}) + OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${SOURCES} INCS ${INCLUDES} ${IMPL_INCLUDES}) if(SUBSYS_DEPS) target_link_libraries("${LIB_NAME}" ${SUBSYS_DEPS}) @@ -33,5 +34,4 @@ if(build) #PCL_MAKE_PKGCONFIG("${LIB_NAME}" "${SUBSYS_NAME}" "${SUBSYS_DESC}" "${SUBSYS_DEPS}" "" "" "" "") - endif(build) \ No newline at end of file diff --git a/detectors/misc/detection/ODDetectorMultiAlgo.cpp b/detectors/src/misc/detection/ODDetectorMultiAlgo.cpp similarity index 98% rename from detectors/misc/detection/ODDetectorMultiAlgo.cpp rename to detectors/src/misc/detection/ODDetectorMultiAlgo.cpp index d5223cf0..bbf95016 100644 --- a/detectors/misc/detection/ODDetectorMultiAlgo.cpp +++ b/detectors/src/misc/detection/ODDetectorMultiAlgo.cpp @@ -27,7 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 06.08.15. // -#include "ODDetectorMultiAlgo.h" +#include "detectors/misc/detection/ODDetectorMultiAlgo.h" #include "detectors/global2D/detection/ODCascadeDetector.h" #include "detectors/global2D/detection/ODHOGDetector.h" #include "detectors/global2D/ODFaceRecognizer.h" diff --git a/doc/doxygen/doxyfile.in b/doc/doxygen/doxyfile.in index 43feda79..c48be864 100644 --- a/doc/doxygen/doxyfile.in +++ b/doc/doxygen/doxyfile.in @@ -168,9 +168,9 @@ HTML_EXTRA_FILES = "@OD_SOURCE_DIR@/doc/doxygen/css/tabs.css" \ "@OD_SOURCE_DIR@/doc/doxygen/images/face_business.jpg" \ "@OD_SOURCE_DIR@/doc/doxygen/images/snap_hog3.png" HTML_EXTRA_STYLESHEET = "@OD_SOURCE_DIR@/doc/doxygen/css/customdoxygen.css" -HTML_COLORSTYLE_HUE =218 -HTML_COLORSTYLE_SAT =82 -HTML_COLORSTYLE_GAMMA =15 +HTML_COLORSTYLE_HUE = 218 +HTML_COLORSTYLE_SAT = 82 +HTML_COLORSTYLE_GAMMA = 80 HTML_TIMESTAMP = YES HTML_DYNAMIC_SECTIONS = NO GENERATE_DOCSET = YES diff --git a/doc/doxygen/tutorials_doxygen/detection_3d.md b/doc/doxygen/tutorials_doxygen/detection_3d.md index 44ceffb9..2f230c4b 100644 --- a/doc/doxygen/tutorials_doxygen/detection_3d.md +++ b/doc/doxygen/tutorials_doxygen/detection_3d.md @@ -19,7 +19,7 @@ By default CADDetector3D (there is a CADDetector2D as well which detects CAD mod ###Data {#detection_3d3} -You need to get 3D CAD models of the objects you want to detect in the query scene. You can download CAD models from the CAD model databases available in the internet (eg: 3D warehouse: https://3dwarehouse.sketchup.com/?hl=en) or acquire your own CAD models of the real objects from a 3D scanners (which are popular in Robotics). We have provided some example CAD models in our \ref getting_started2 "Data Repository" at location <source_to_data>/training_input/CADModels. If you don't have objects which look like them (which is obvious), you won't get successful detection. If you don't have a 3D scanner, I would suggest you to look at the CAD models in 3D warehouse which looks 'similar' to the real object. Thanks to the the 3D global features which do not require CAD models to be of exactly the same geometry as the real object. Somewhat 'similar looking' CAD model will work as well. +You need to get 3D CAD models of the objects you want to detect in the query scene. You can download CAD models from the CAD model databases available in the internet (eg: 3D warehouse: https://3dwarehouse.sketchup.com/?hl=en) or acquire your own CAD models of the real objects from a 3D scanners (which are popular in Robotics). We have provided some example CAD models in our \ref getting_started2 "Data Repository" at location \<source_to_data\>/training_input/CADModels. If you don't have objects which look like them (which is obvious), you won't get successful detection. If you don't have a 3D scanner, I would suggest you to look at the CAD models in 3D warehouse which looks 'similar' to the real object. Thanks to the the 3D global features which do not require CAD models to be of exactly the same geometry as the real object. Somewhat 'similar looking' CAD model will work as well. The CAD models is to be placed in a strict directory structure before training which is documented in od::g3d::ODCADDetectTrainer3DGlobal. This particular demo uses kinect to acquire Point Cloud in the real time, so you need a kinect device as well. diff --git a/doc/doxygen/tutorials_doxygen/getting_started.md b/doc/doxygen/tutorials_doxygen/getting_started.md index 73759f1c..383d7e00 100644 --- a/doc/doxygen/tutorials_doxygen/getting_started.md +++ b/doc/doxygen/tutorials_doxygen/getting_started.md @@ -32,15 +32,15 @@ Make `<path_to_data>/trained_data` as your trained_data location in all the exam ##Run your first examples {#getting_started3} -Go to the build directory of OD (<path_to_source>/build). Run a face detector from your webcam using: +Go to the build directory of OD (\<path_to_source\>/build). Run a face detector from your webcam using: @code{.bash} -./examples/objectdetector/od_cascade_cam <path_to_data>/trained_data/ +./examples/objectdetector/od_cascade_cam \<path_to_data\>/trained_data/ @endcode -Or get some images containing people (suppose in <path_to_images>/*.JPG) and run the people detector using: +Or get some images containing people (suppose in \<path_to_images\>/\*.JPG) and run the people detector using: @code{.bash} -./examples/objectdetector/od_image_hog_files <path_to_data>/trained_data/ "<path_to_images>/*.JPG" +./examples/objectdetector/od_image_hog_files \<path_to_data\>/trained_data/ "\<path_to_images\>/\*.JPG" @endcode Depending on the image, you will see something like the following. diff --git a/doc/doxygen/tutorials_doxygen/idea_list_gsoc2016.md b/doc/doxygen/tutorials_doxygen/idea_list_gsoc2016.md index 78122f33..f10c005b 100644 --- a/doc/doxygen/tutorials_doxygen/idea_list_gsoc2016.md +++ b/doc/doxygen/tutorials_doxygen/idea_list_gsoc2016.md @@ -70,7 +70,7 @@ This is one of the important project and have a very strong influence to the fut **Key tasks**: * This is an open idea. We always would like to integrate **state-of-the-art** algorithms in our framework. It would be easy to port an available C++ code in this framework instead of implementing an idea/paper from scratch. So, we expect you to *do some literature review and report different algorithms* with their availability and the ones you would like to add. Please note that, just because the implementation of some algorithm is available online does not mean that it is the state-of-the-art or one of the popular algorithms. -* Implement/port the identified detection algorithms under od::ODDetector2D/od::ODDetector3D and the respective *Trainer* s. +* Implement/port the identified detection algorithms under od::ODDetector2D / od::ODDetector3D and the respective *Trainer* s. **Prerequisites**: Expertise in Computer Vision and C++. diff --git a/doc/doxygen/tutorials_doxygen/installation_instruction.md b/doc/doxygen/tutorials_doxygen/installation_instruction.md index 9044fa4a..bd8a6392 100644 --- a/doc/doxygen/tutorials_doxygen/installation_instruction.md +++ b/doc/doxygen/tutorials_doxygen/installation_instruction.md @@ -23,7 +23,7 @@ OpenCV 3.0 is to be compiled with the modules xfeatures2d (for features like SIF Detailed instructions with source are provided here: https://github.com/itseez/opencv_contrib . You need to download this seperate repository before compiling OpenCV. - *CMAKE options*: OPENCV_EXTRA_MODULES_PATH=<path_to_opencv_contrib>/modules + *CMAKE options*: OPENCV_EXTRA_MODULES_PATH=\<path_to_opencv_contrib\>/modules - *OpenCV CUDA module* - for GPU enabled feature detectors and matcher. diff --git a/examples/objectdetector/CMakeLists.txt b/examples/objectdetector/CMakeLists.txt index c6b63a88..9c09a83e 100644 --- a/examples/objectdetector/CMakeLists.txt +++ b/examples/objectdetector/CMakeLists.txt @@ -1,3 +1,5 @@ +include_directories(${OD_SOURCE_DIR}/common/include) + OD_ADD_EXAMPLE(od_image_camera FILES od_image_cadrecog_camera.cpp LINK_WITH od_common od_local_image_detector) @@ -33,9 +35,6 @@ OD_ADD_EXAMPLE(od_cascade_files FILES od_cascade_files.cpp OD_ADD_EXAMPLE(od_image_facerecog FILES od_image_facerecog.cpp LINK_WITH od_common od_global_image_detector) - - - OD_ADD_EXAMPLE(od_example_pc_global FILES od_pc_global.cpp LINK_WITH od_common od_pointcloud_global_detector) From 3240b69226797b76fa5858022cfa44380da94c16 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Thu, 19 May 2016 15:59:54 +0200 Subject: [PATCH 05/79] added separate options for all examples --- CMakeLists.txt | 3 +- .../gsoc2016_blog_giacomo.md | 33 +++++- examples/CMakeLists.txt | 6 +- examples/objectdetector/CMakeLists.txt | 109 ++++++++++++------ 4 files changed, 111 insertions(+), 40 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d8d34384..a5c538f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,9 +24,10 @@ include(${OD_CMAKE_DIR}/od_macros.cmake) option(WITH_DOCUMENTATION "Build the OD documentation" ON) option(WITH_GPU "Build GPU components of the project" ON) option(WITH_SVMLIGHT "Build SVM Light" OFF) +option(WITH_EXAMPLES "Build examples" OFF) # Set modules -set(OD_MODULES_NAMES 3rdparty common doc detectors) +set(OD_MODULES_NAMES 3rdparty common doc detectors examples) set(OD_MODULES_DIRS ${OD_MODULES_NAMES}) # Add modules diff --git a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md index 18b54841..d54a926f 100644 --- a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md +++ b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md @@ -3,12 +3,12 @@ GSoC 2016 Blog - Giacomo {#gsoc2016_blog_giacomo} ==== [TOC] -Framework design and library maintenance {#gsoc2016_blog_giacomo1} +#Framework design and library maintenance# {#gsoc2016_blog_giacomo1} ==== - [Link to Proposal](https://docs.google.com/document/d/16Wyd0h5b9-7DaG7ZYJT30a2i096krviFUCcDYwg-jZc/edit?usp=sharing) - [Link to GSoC2016 Project Page](https://summerofcode.withgoogle.com/organizations/6007728078061568/#5675882488266752) -**About Me** +##About Me## I finished my **joint master degree** in computer science and networking at Sant'Annas school of advanced studies and the university of Pisa in 2014 with a thesis on the static allocation of real-time OpenMP jobs on multicore machines. The master program was focused on parallel and high performance computing including OpenMP, MPI, Cuda and Tbb. @@ -24,7 +24,7 @@ I am contributing to the different libraries like **PCL**, **OpenCV** and **libf -[Link to Github](https://github.com/giacomodabisias) -**General Project Idea** +##General Project Idea## The concept of this gsoc project is to restructure the OpenDetection Library in order to make it more usable, scalable and documented. In a second phase there could also be the possibility to implement some new features in the library. @@ -39,3 +39,30 @@ Depending on OpenCV/PCL version activate/deactivate modules of the library. - **Task 8** - small - Documentation and examples : Continue the documentation process and add new examples for the new implemented structures/algorithms. - **Task 9** - small task - merge other gsoc16 contributions : Merge changes from other contribution of GSOC. Other projects will be using existing APIs. There might me need to make small changes to fit to the changes made in this project - **Task 10** - moderate task - deb packaging : Create an automated way to generate a deb file for the library so it can be installed through debian packaging system. + +##Fix 3rd party dependencies## + +The library has some dependencies inserted as source code into the 3rdparty folder; these are pugixml and SiftGPU (and maybe others which will come later). There is also the dependency of svmlight, but this is not mandatory so it will be added as external dependency; if the library is present on the system all the depending libraries will be built. + +In general its a bad idea to integrate external source code into a library since you can't have update versions which maybe fix bugs etc.. and you have to take care of licenses. To fix this issue I removed the two folders (**pugixml** and **SiftGPU**) and added the git repositories of these two libraries as submodules. This way, when a user is downloading the opendetection library, he will also download the lates version of the two libraries. One could argue that the API of the libraries could change, but it is possible to checkout a fixed version in order to avoid these issues. + +- **SiftGPU** can just be added with the **add_subdirectory** cmake command which will execute the *CMakeLists.txt* in the library folder and export the produced targets (libsiftGPU.so in this case. +- **Pugixml** is a bit more tricky since it has no cmake file; it contains a Make file which produces a test executable but no library. The library can be easily built since we have just two include files and one .cpp file. To fix the issue I created a separate pugixml_build folder with a simple custom cmake which builds the library. This way we can just add that folder as add_subdirectory and directly export the newly compile **libpugixml.so** library. +- **Svmlight** will be inserted in the system with a find_package or something similar and a custom **WITH_SVMLIGHT** cmake flag. These steps will come shortly; we will just leave the svmlight binding. + +##Refactoring file structure## + +I started to move files in appropriate folders and to fix the "Main" *CmakeLists.txt* file. I renamed the ODconfig.h.in file to od_config.h and moved it into the cmake folder for now. Then I moved the opendetection.cpp source file away from the root (not nice to have source files in the root of a library) and added it as separate app in a new version folder which builds the od_version executable. + +I fixed the **OD_ADD_EXAMPLE** macro adding the **INCLUDE** argument to include specific headers and used it in the *od_version* app. A separate cmake file has benn created to include all MACROS, but I have still to check if it is possible to add ordering between include cmake statements since there are some dependencies between the files. I separated the **OD_CMAKE_DIR** from **CMAKE_MODULE_PATH** in order to have a clear struture and to not mix up folders; the files in the cmake folder have also been renamed to have a common naming scheme.The next step will involve separating source and header files in appropriate folders and adding them in the cmake without explicitly naming them to avoid problems when renaming files in the library. + +The next huge step consisted in refactoring common, detectors and example folders to have a common standard file structure. This is usually built having in each folder the following subflders/files: +- CMakeLists.txt : the usual cmake build file +- src : all the source files which can be subdivided into folders +- include : a folder hierarchy which resambles the src structure. This is very useful to install include files and to maintain clear include names +- impl : a floder containing the template files (.hpp usually). + +This resembles also the structure used in the PCL library. + +After refactoring the folders, all the involved CMakeFiles had to be restructured to adapt to the new structure. The first version still uses all file names explicitly, but it should be possible to automate the file detection process by using the cmake command *file(GLOB VAR PATTERN)* which finds all file in a gve folder which match a given pattern. This can be done also in a recursive manner. I will probably use this for some folders to be independent (partially) of file names, locations and number. + diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 5ad55cff..5595aaec 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,2 +1,4 @@ -add_subdirectory(objectdetector) -add_subdirectory(apps) \ No newline at end of file +if(WITH_EXAMPLES) + add_subdirectory(objectdetector) + add_subdirectory(apps) +endif() \ No newline at end of file diff --git a/examples/objectdetector/CMakeLists.txt b/examples/objectdetector/CMakeLists.txt index 9c09a83e..5c34a21d 100644 --- a/examples/objectdetector/CMakeLists.txt +++ b/examples/objectdetector/CMakeLists.txt @@ -1,51 +1,92 @@ + include_directories(${OD_SOURCE_DIR}/common/include) -OD_ADD_EXAMPLE(od_image_camera FILES od_image_cadrecog_camera.cpp - LINK_WITH od_common od_local_image_detector) +option(camera_recognition_example "Build the camera recognition example" ON) +if(od_image_camera_example) + OD_ADD_EXAMPLE(od_image_camera FILES od_image_cadrecog_camera.cpp + LINK_WITH od_common od_local_image_detector) +endif() -OD_ADD_EXAMPLE(od_example_files FILES od_image_cadrecog_files.cpp - LINK_WITH od_common od_local_image_detector) +option(image_recognition_example "Build the image recognition example" ON) +if(image_recognition_example) + OD_ADD_EXAMPLE(od_example_files FILES od_image_cadrecog_files.cpp + LINK_WITH od_common od_local_image_detector) +endif() if(WITH_SVMLIGHT) - -OD_ADD_EXAMPLE(od_hog_train FILES od_hog_train.cpp - LINK_WITH od_common od_global_image_detector) - -OD_ADD_EXAMPLE(od_image_hog_files FILES od_image_hog_files.cpp - LINK_WITH od_common od_global_image_detector) - -OD_ADD_EXAMPLE(od_image_customhog FILES od_image_customhog.cpp - LINK_WITH od_common od_global_image_detector) - - -OD_ADD_EXAMPLE(od_multialgo_files FILES od_multialgo_files.cpp - LINK_WITH od_miscellaneous_detector) - -OD_ADD_EXAMPLE(od_multialgo_pc FILES od_multialgo_pc.cpp - LINK_WITH od_miscellaneous_detector) + option(hog_train_example "Build the hog train example" ON) + if(hog_train_example) + OD_ADD_EXAMPLE(od_hog_train FILES od_hog_train.cpp + LINK_WITH od_common od_global_image_detector) + endif() + + option(image_hog_files_example "Build the hog image example" ON) + if(image_hog_files_example) + OD_ADD_EXAMPLE(od_image_hog_files FILES od_image_hog_files.cpp + LINK_WITH od_common od_global_image_detector) + endif() + + option(image_customhog_example "Build the custom hog example" ON) + if(image_customhog_example) + OD_ADD_EXAMPLE(od_image_customhog FILES od_image_customhog.cpp + LINK_WITH od_common od_global_image_detector) + endif() + + option(multialgo_files_example "Build the multialgo example" ON) + if(multialgo_files_example) + OD_ADD_EXAMPLE(od_multialgo_files FILES od_multialgo_files.cpp + LINK_WITH od_miscellaneous_detector) + endif() + + option(multialgo_pc_example "Build the multialgo_pc example" ON) + if(multialgo_pc_example) + OD_ADD_EXAMPLE(od_multialgo_pc FILES od_multialgo_pc.cpp + LINK_WITH od_miscellaneous_detector) + endif() endif() -OD_ADD_EXAMPLE(od_cascade_cam FILES od_cascade_cam.cpp - LINK_WITH od_common od_global_image_detector) +option(cascade_cam_example "Build the cascade camera example" ON) +if(cascade_cam_example) + OD_ADD_EXAMPLE(od_cascade_cam FILES od_cascade_cam.cpp + LINK_WITH od_common od_global_image_detector) +endif() -OD_ADD_EXAMPLE(od_cascade_files FILES od_cascade_files.cpp - LINK_WITH od_common od_global_image_detector) +option(cascade_files_example "Build the cascade file example" ON) +if(cascade_files_example) + OD_ADD_EXAMPLE(od_cascade_files FILES od_cascade_files.cpp + LINK_WITH od_common od_global_image_detector) +endif() -OD_ADD_EXAMPLE(od_image_facerecog FILES od_image_facerecog.cpp - LINK_WITH od_common od_global_image_detector) +option(image_facerecog_example "Build the face recognition example" ON) +if(image_facerecog_example) + OD_ADD_EXAMPLE(od_image_facerecog FILES od_image_facerecog.cpp + LINK_WITH od_common od_global_image_detector) +endif() -OD_ADD_EXAMPLE(od_example_pc_global FILES od_pc_global.cpp - LINK_WITH od_common od_pointcloud_global_detector) +option(pc_global_example "Build the global detector example" ON) +if(pc_global_example) + OD_ADD_EXAMPLE(od_example_pc_global FILES od_pc_global.cpp + LINK_WITH od_common od_pointcloud_global_detector) +endif() -OD_ADD_EXAMPLE(od_example_pc_global_files FILES od_pc_global_files.cpp - LINK_WITH od_common od_pointcloud_global_detector) +option(pc_global_files_example "Build the global detector file example" ON) +if(pc_global_files_example) + OD_ADD_EXAMPLE(od_example_pc_global_files FILES od_pc_global_files.cpp + LINK_WITH od_common od_pointcloud_global_detector) +endif() -OD_ADD_EXAMPLE(od_example_pc_global_real_time FILES od_pc_global_real_time.cpp - LINK_WITH od_common od_pointcloud_global_detector) +option(pc_global_real_time_example "Build the global detector real time example" ON) +if(pc_global_real_time_example) + OD_ADD_EXAMPLE(od_example_pc_global_real_time FILES od_pc_global_real_time.cpp + LINK_WITH od_common od_pointcloud_global_detector) +endif() -OD_ADD_EXAMPLE(od_example_framegenerator FILES od_example_framegenerator.cpp - LINK_WITH od_common) +option(framegenerator_example "Build the frame generator example" ON) +if(framegenerator_example) + OD_ADD_EXAMPLE(od_example_framegenerator FILES od_example_framegenerator.cpp + LINK_WITH od_common) +endif() From d8ce9ea6ac6b18b53ddeecac3eeaa36268d23dd3 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Mon, 23 May 2016 11:43:46 +0200 Subject: [PATCH 06/79] clean build except for three examples (pc) which have a linking problem --- CMakeLists.txt | 6 +++--- cmake/od_macros.cmake | 21 ++++++++++++++----- common/CMakeLists.txt | 4 +++- detectors/CMakeLists.txt | 6 ++++++ .../detection/ODCADDetector3DGlobal.h | 5 +---- .../detection/ODCADRecognizer2DLocal.h | 2 +- .../training/ODCADRecogTrainerSnapshotBased.h | 2 +- .../simple_ransac_detection/CMakeLists.txt | 21 ++++++++++--------- detectors/src/global2D/CMakeLists.txt | 6 +++--- detectors/src/global2D/ODFaceRecognizer.cpp | 2 +- .../global2D/detection/ODCascadeDetector.cpp | 2 +- detectors/src/global3D/CMakeLists.txt | 5 +++-- .../global3D/ODPointCloudGlobalMatching.cpp | 2 +- .../detection/ODCADDetector3DGlobal.cpp | 2 +- .../training/ODCADDetectTrainer3DGlobal.cpp | 2 +- detectors/src/local2D/CMakeLists.txt | 7 ++++--- .../src/local2D/ODImageLocalMatching.cpp | 2 +- .../detection/ODCADRecognizer2DLocal.cpp | 2 +- .../ODCADRecogTrainerSnapshotBased.cpp | 2 +- detectors/src/misc/CMakeLists.txt | 8 +++---- .../gsoc2016_blog_giacomo.md | 11 ++++++++++ examples/CMakeLists.txt | 6 ++++-- examples/apps/CMakeLists.txt | 4 ++++ .../od_test_single_db_single_model.cpp | 3 ++- examples/objectdetector/CMakeLists.txt | 5 ++++- 25 files changed, 88 insertions(+), 50 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a5c538f3..a152213e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,19 +1,19 @@ cmake_minimum_required(VERSION 2.8) project(OpenDetection) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") # Initialize variables set(OD_SOURCE_DIR ${OpenDetection_SOURCE_DIR}) set(OD_BINARY_DIR ${OpenDetection_BINARY_DIR}) -set(OD_CMAKE_DIR "${OpenDetection_SOURCE_DIR}/cmake" ) +set(OD_CMAKE_DIR "${OpenDetection_SOURCE_DIR}/cmake") set(CMAKE_MODULE_PATH ${OD_CMAKE_DIR}/modules) # Initialize versioning include(${OD_CMAKE_DIR}/od_version.cmake) set(OD_VERSION "${OD_MAJOR_VERSION}.${OD_MINOR_VERSION}") set(OD_VERSION_DETAILED "${OD_MAJOR_VERSION}.${OD_MINOR_VERSION}.${OD_BUILD_VERSION}") -configure_file ("${OD_CMAKE_DIR}/od_config.h.in" "${OD_BINARY_DIR}/generated/od_config.h" ) +configure_file ("${OD_CMAKE_DIR}/od_config.h.in" "${OD_BINARY_DIR}/generated/od_config.h") # Set up macros and configurations include(${OD_CMAKE_DIR}/od_targets.cmake) diff --git a/cmake/od_macros.cmake b/cmake/od_macros.cmake index 126c2a97..19f59354 100644 --- a/cmake/od_macros.cmake +++ b/cmake/od_macros.cmake @@ -128,10 +128,21 @@ MACRO(HEADER_DIRECTORIES return_list) SET(${return_list} ${dir_list}) ENDMACRO() -MACRO(SUBDIRS list) - FILE(GLOB list RELATIVE ${curdir} ${curdir}/*) +MACRO(CMAKE_SUBDIR_LIST result) + file (GLOB result */CMakeLists.txt) ENDMACRO() -MACRO(SUBDIRLIST result curdir) - FILE(GLOB result RELATIVE ${curdir} ${curdir}/*) -ENDMACRO() \ No newline at end of file +MACRO(SUBDIR_LIST retval curdir return_relative) + file(GLOB sub-dir RELATIVE ${curdir} *) + set(list_of_dirs "") + foreach(dir ${sub-dir}) + if(IS_DIRECTORY ${curdir}/${dir}) + if (${return_relative}) + set(list_of_dirs ${list_of_dirs} ${dir}) + else() + set(list_of_dirs ${list_of_dirs} ${curdir}/${dir}) + endif() + endif() + endforeach() + set(${retval} ${list_of_dirs}) +endmacro() diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index c174640d..7d5f33d9 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -2,6 +2,8 @@ set(SUBSYS_NAME common) set(LIB_NAME od_${SUBSYS_NAME}) set(SUBSYS_DESC "The core module of OpenDetection having the pipeline logic") set(SUBSYS_DEPS ${OpenCV_LIBS} ${PCL_LIBRARIES}) +set(COMMON_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) +set(COMMON_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include PARENT_SCOPE) if(WITH_GPU) set(SUBSYS_DEPS ${SUBSYS_DEPS} siftgpu) @@ -33,7 +35,7 @@ if(build) "src/utils/ODFeatureDetector2D.cpp" ) - include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) + include_directories(${COMMON_INCLUDE_DIR}) OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${SOURCES} INCS ${INCLUDES} ${IMPL_INCLUDES}) install(FILES ${INCLUDES} DESTINATION ${OD_INSTALL_INCLUDE_DIR}/${SUBSYS_NAME} COMPONENT ${LIB_NAME}) diff --git a/detectors/CMakeLists.txt b/detectors/CMakeLists.txt index 445a9366..b3e84695 100644 --- a/detectors/CMakeLists.txt +++ b/detectors/CMakeLists.txt @@ -1,7 +1,13 @@ set(DETECTORS_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(SIMPLE_RANSAC_INCLUDE_DIR ${DETECTORS_DIR}) set(DETECTORS_INCLUDE_DIR ${DETECTORS_DIR}/include) set(DETECTORS_IMPL_DIR ${DETECTORS_DIR}/impl) +#Add in parent scope to build examples +set(DETECTORS_INCLUDE_DIR ${DETECTORS_DIR}/include PARENT_SCOPE) +set(DETECTORS_IMPL_DIR ${DETECTORS_DIR}/impl PARENT_SCOPE) +set(SIMPLE_RANSAC_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR} PARENT_SCOPE) + add_subdirectory(simple_ransac_detection) add_subdirectory("src/local2D") add_subdirectory("src/global2D") diff --git a/detectors/include/detectors/global3D/detection/ODCADDetector3DGlobal.h b/detectors/include/detectors/global3D/detection/ODCADDetector3DGlobal.h index 92329e94..517b0662 100644 --- a/detectors/include/detectors/global3D/detection/ODCADDetector3DGlobal.h +++ b/detectors/include/detectors/global3D/detection/ODCADDetector3DGlobal.h @@ -32,9 +32,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <common/pipeline/ODDetector.h> - - - #include <pcl/pcl_macros.h> #include <pcl/apps/3d_rec_framework/pipeline/global_nn_classifier.h> #include <pcl/apps/3d_rec_framework/pc_source/mesh_source.h> @@ -113,5 +110,5 @@ namespace od } } -#include "ODCADDetector3DGlobal.hpp" +#include "detection/ODCADDetector3DGlobal.hpp" #endif //OPENDETECTION_ODPOINTCLOUDGLOBALMATCHINGDETECTOR_H diff --git a/detectors/include/detectors/local2D/detection/ODCADRecognizer2DLocal.h b/detectors/include/detectors/local2D/detection/ODCADRecognizer2DLocal.h index 15dd4b73..0ed95dc6 100644 --- a/detectors/include/detectors/local2D/detection/ODCADRecognizer2DLocal.h +++ b/detectors/include/detectors/local2D/detection/ODCADRecognizer2DLocal.h @@ -31,7 +31,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef OPENDETECTION_SIMPLERANSACDETECTOR_H #define OPENDETECTION_SIMPLERANSACDETECTOR_H -#include "ODImageLocalMatching.h" +#include "detectors/local2D/ODImageLocalMatching.h" #include "common/pipeline/ODDetector.h" #include "common/pipeline/ODScene.h" diff --git a/detectors/include/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h b/detectors/include/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h index adf204da..42682489 100644 --- a/detectors/include/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h +++ b/detectors/include/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h @@ -34,7 +34,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "common/pipeline/ODTrainer.h" #include "common/utils/utils.h" -#include "ODImageLocalMatching.h" +#include "detectors/local2D/ODImageLocalMatching.h" #define VIEW_ANGLE 30 diff --git a/detectors/simple_ransac_detection/CMakeLists.txt b/detectors/simple_ransac_detection/CMakeLists.txt index beb84896..7ce0afc7 100644 --- a/detectors/simple_ransac_detection/CMakeLists.txt +++ b/detectors/simple_ransac_detection/CMakeLists.txt @@ -1,12 +1,13 @@ -set(simple_ransac_src_dir ${CMAKE_CURRENT_SOURCE_DIR}) +set(SIMPLE_RANSAC_SRC ${CMAKE_CURRENT_SOURCE_DIR}) +set(SIMPLE_RANSAC_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR} PARENT_SCOPE) -set(SUB_IMPL_SRCS - ${simple_ransac_src_dir}/CsvReader.cpp - ${simple_ransac_src_dir}/CsvWriter.cpp - ${simple_ransac_src_dir}/ModelRegistration.cpp - ${simple_ransac_src_dir}/Mesh.cpp - ${simple_ransac_src_dir}/Model.cpp - ${simple_ransac_src_dir}/PnPProblem.cpp - ${simple_ransac_src_dir}/Utils.cpp - ${simple_ransac_src_dir}/RobustMatcher.cpp +set(SIMPLE_RANSAC_SRC_FILES + ${SIMPLE_RANSAC_SRC}/CsvReader.cpp + ${SIMPLE_RANSAC_SRC}/CsvWriter.cpp + ${SIMPLE_RANSAC_SRC}/ModelRegistration.cpp + ${SIMPLE_RANSAC_SRC}/Mesh.cpp + ${SIMPLE_RANSAC_SRC}/Model.cpp + ${SIMPLE_RANSAC_SRC}/PnPProblem.cpp + ${SIMPLE_RANSAC_SRC}/Utils.cpp + ${SIMPLE_RANSAC_SRC}/RobustMatcher.cpp PARENT_SCOPE) \ No newline at end of file diff --git a/detectors/src/global2D/CMakeLists.txt b/detectors/src/global2D/CMakeLists.txt index 203f9a6c..c8fe5825 100644 --- a/detectors/src/global2D/CMakeLists.txt +++ b/detectors/src/global2D/CMakeLists.txt @@ -20,7 +20,7 @@ if(build) "training/ODHOGTrainer.cpp" ) set(INCLUDES - "${DETECTORS_INCLUDE_DIR}/detectors/global2D/DFaceRecognizer.h" + "${DETECTORS_INCLUDE_DIR}/detectors/global2D/ODFaceRecognizer.h" "${DETECTORS_INCLUDE_DIR}/detectors/global2D/detection/ODHOGDetector.h" "${DETECTORS_INCLUDE_DIR}/detectors/global2D/detection/ODCascadeDetector.h" "${DETECTORS_INCLUDE_DIR}/detectors/global2D/training/ODHOGTrainer.h" @@ -38,8 +38,8 @@ if(build) endif() - include_directories(${DETECTORS_INCLUDE_DIR}/detectors/global2D) - include_directories(${OD_SOURCE_DIR}/common/include) + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${SOURCES} INCS ${INCLUDES} ${impl_incs}) install(FILES ${INCLUDES} DESTINATION ${OD_INSTALL_INCLUDE_DIR}/${SUBSYS_NAME} COMPONENT ${LIB_NAME} ) diff --git a/detectors/src/global2D/ODFaceRecognizer.cpp b/detectors/src/global2D/ODFaceRecognizer.cpp index cc88097b..186ae386 100644 --- a/detectors/src/global2D/ODFaceRecognizer.cpp +++ b/detectors/src/global2D/ODFaceRecognizer.cpp @@ -27,7 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 16.07.15. // -#include "ODFaceRecognizer.h" +#include "detectors/global2D/ODFaceRecognizer.h" using namespace cv; using namespace std; diff --git a/detectors/src/global2D/detection/ODCascadeDetector.cpp b/detectors/src/global2D/detection/ODCascadeDetector.cpp index defefad0..e01e2cdc 100644 --- a/detectors/src/global2D/detection/ODCascadeDetector.cpp +++ b/detectors/src/global2D/detection/ODCascadeDetector.cpp @@ -27,7 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 17.07.15. // -#include "detection/ODCascadeDetector.h" +#include "detectors/global2D/detection/ODCascadeDetector.h" using namespace cv; using namespace std; diff --git a/detectors/src/global3D/CMakeLists.txt b/detectors/src/global3D/CMakeLists.txt index 28f5397e..064e50b6 100644 --- a/detectors/src/global3D/CMakeLists.txt +++ b/detectors/src/global3D/CMakeLists.txt @@ -26,8 +26,9 @@ if(build) "src/training/ODCADDetectTrainer3DGlobal.cpp" ) - include_directories(${DETECTORS_INCLUDE_DIR}/detectors/global3D) - include_directories(${OD_SOURCE_DIR}/common/include) + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${DETECTORS_IMPL_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${SOURCES} INCS ${INCLUDES} ${IMPL_INCLUDES}) install(FILES ${INCLUDES} DESTINATION ${OD_INSTALL_INCLUDE_DIR}/${SUBSYS_NAME} COMPONENT ${LIB_NAME}) diff --git a/detectors/src/global3D/ODPointCloudGlobalMatching.cpp b/detectors/src/global3D/ODPointCloudGlobalMatching.cpp index 91b9454b..8c8b305a 100644 --- a/detectors/src/global3D/ODPointCloudGlobalMatching.cpp +++ b/detectors/src/global3D/ODPointCloudGlobalMatching.cpp @@ -27,4 +27,4 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 16.06.15. // -#include "ODPointCloudGlobalMatching.h" +#include "detectors/global3D/ODPointCloudGlobalMatching.h" diff --git a/detectors/src/global3D/detection/ODCADDetector3DGlobal.cpp b/detectors/src/global3D/detection/ODCADDetector3DGlobal.cpp index f048ec70..c3e296fe 100644 --- a/detectors/src/global3D/detection/ODCADDetector3DGlobal.cpp +++ b/detectors/src/global3D/detection/ODCADDetector3DGlobal.cpp @@ -27,4 +27,4 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 16.06.15. // -#include "ODCADDetector3DGlobal.h" +#include "detection/ODCADDetector3DGlobal.h" diff --git a/detectors/src/global3D/training/ODCADDetectTrainer3DGlobal.cpp b/detectors/src/global3D/training/ODCADDetectTrainer3DGlobal.cpp index acbf755f..2e9edacc 100644 --- a/detectors/src/global3D/training/ODCADDetectTrainer3DGlobal.cpp +++ b/detectors/src/global3D/training/ODCADDetectTrainer3DGlobal.cpp @@ -27,7 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 16.06.15. // -#include "ODCADDetectTrainer3DGlobal.h" +#include "detection/ODCADDetectTrainer3DGlobal.h" #include <pcl/pcl_macros.h> #include <pcl/apps/3d_rec_framework/pipeline/global_nn_classifier.h> diff --git a/detectors/src/local2D/CMakeLists.txt b/detectors/src/local2D/CMakeLists.txt index 595448ae..d16129dc 100644 --- a/detectors/src/local2D/CMakeLists.txt +++ b/detectors/src/local2D/CMakeLists.txt @@ -26,11 +26,12 @@ if(build) "ODImageLocalMatching.cpp" "training/ODCADRecogTrainerSnapshotBased.cpp" "detection/ODCADRecognizer2DLocal.cpp" + ${SIMPLE_RANSAC_SRC_FILES} ) - include_directories(${DETECTORS_INCLUDE_DIR}/detectors/local2D) - include_directories(${OD_SOURCE_DIR}/common/include) - include_directories(${DETECTORS_DIR}) + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${SIMPLE_RANSAC_INCLUDE_DIR}) OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${SOURCES} INCS ${INCLUDES} ${IMPL_INCLUDES}) install(FILES ${INCLUDES} DESTINATION ${OD_INSTALL_INCLUDE_DIR}/${SUBSYS_NAME} COMPONENT ${LIB_NAME}) diff --git a/detectors/src/local2D/ODImageLocalMatching.cpp b/detectors/src/local2D/ODImageLocalMatching.cpp index 4d68fd7c..7df54ed7 100644 --- a/detectors/src/local2D/ODImageLocalMatching.cpp +++ b/detectors/src/local2D/ODImageLocalMatching.cpp @@ -28,4 +28,4 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 05.06.15. // -#include "ODImageLocalMatching.h" +#include "detectors/local2D/ODImageLocalMatching.h" diff --git a/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp b/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp index 65357fb7..1db20315 100644 --- a/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp +++ b/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp @@ -28,7 +28,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 08.06.15. // -#include "detection/ODCADRecognizer2DLocal.h" +#include "detectors/local2D/detection/ODCADRecognizer2DLocal.h" #include <opencv2/highgui/highgui.hpp> diff --git a/detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp b/detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp index 402eb0c1..20cd488a 100644 --- a/detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp +++ b/detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp @@ -24,7 +24,7 @@ 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. */ -#include "training/ODCADRecogTrainerSnapshotBased.h" +#include "detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h" //simplecube/newcube.obj simplecube/flower.jpeg //Lion/Final.obj Lion/Texture.png //BigDaddy/Param.obj BigDaddy/Parameterization.png diff --git a/detectors/src/misc/CMakeLists.txt b/detectors/src/misc/CMakeLists.txt index 93c43d07..7cdcf363 100644 --- a/detectors/src/misc/CMakeLists.txt +++ b/detectors/src/misc/CMakeLists.txt @@ -20,11 +20,9 @@ if(build) ) include_directories(${DETECTORS_INCLUDE_DIR}) - include_directories(${DETECTORS_INCLUDE_DIR}/detectors/local2D) - include_directories(${OD_SOURCE_DIR}/common/include) - include_directories(${DETECTORS_DIR}) - include_directories(${DETECTORS_IMPL_DIR}/detection/) - + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${DETECTORS_IMPL_DIR}) + include_directories(${SIMPLE_RANSAC_INCLUDE_DIR}) OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${SOURCES} INCS ${INCLUDES} ${IMPL_INCLUDES}) diff --git a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md index d54a926f..6cda1f2b 100644 --- a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md +++ b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md @@ -66,3 +66,14 @@ This resembles also the structure used in the PCL library. After refactoring the folders, all the involved CMakeFiles had to be restructured to adapt to the new structure. The first version still uses all file names explicitly, but it should be possible to automate the file detection process by using the cmake command *file(GLOB VAR PATTERN)* which finds all file in a gve folder which match a given pattern. This can be done also in a recursive manner. I will probably use this for some folders to be independent (partially) of file names, locations and number. +##Making examples optional## + +Examples should not be built always, or at least it should be possible to not build them at all or partially. To do so I added the **WITH_EXAMPLES** option whichi enables the building of the examples. Then for each example I added a variable to trigger the building process of that examples: + +option(image_hog_files_example "Build the hog image example" ON) +if(image_hog_files_example) + OD_ADD_EXAMPLE(od_image_hog_files FILES od_image_hog_files.cpp + LINK_WITH od_common od_global_image_detector) +endif() + +The option command adds an option in the cmake; the first parameter is the cmake option name (which for now is written in that way but it will probably change), the second is a description of the option, and the third is the default option value. I left it on on so that even unexperienced user can build some examples to test the library. The option is then followed by an if which checks the option vale and in case adds the example to the build process. I would like to change also the structure of the example folder by creating subfolders which then contain the different examples subdividing them by **type** . Each folder will then have its own *CMakeLists.txt* file which can be added by the parent cmake with the usual *add_subdirectory* command. This helps also since we can add automatically all the subfolders of the example folder to the build process without stating the names explicitly. \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 5595aaec..35695336 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,4 +1,6 @@ if(WITH_EXAMPLES) - add_subdirectory(objectdetector) - add_subdirectory(apps) + SUBDIR_LIST(EXAMPLE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} "") + foreach(DIR ${EXAMPLE_DIRS}) + add_subdirectory(${DIR}) + endforeach() endif() \ No newline at end of file diff --git a/examples/apps/CMakeLists.txt b/examples/apps/CMakeLists.txt index c685db0d..48e64869 100644 --- a/examples/apps/CMakeLists.txt +++ b/examples/apps/CMakeLists.txt @@ -1,3 +1,7 @@ +include_directories(${DETECTORS_INCLUDE_DIR}) +include_directories(${COMMON_INCLUDE_DIR}) +include_directories(${SIMPLE_RANSAC_INCLUDE_DIR}) + OD_ADD_EXAMPLE(odcadrecog_test_single_model FILES cadrecog2D/od_test_single_db_single_model.cpp LINK_WITH od_common od_local_image_detector) diff --git a/examples/apps/cadrecog2D/od_test_single_db_single_model.cpp b/examples/apps/cadrecog2D/od_test_single_db_single_model.cpp index 169fb107..882b4882 100644 --- a/examples/apps/cadrecog2D/od_test_single_db_single_model.cpp +++ b/examples/apps/cadrecog2D/od_test_single_db_single_model.cpp @@ -23,7 +23,8 @@ 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. -*/#include "detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h" +*/ +#include "detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h" #include "detectors/local2D/detection/ODCADRecognizer2DLocal.h" #include "common/utils/ODFrameGenerator.h" diff --git a/examples/objectdetector/CMakeLists.txt b/examples/objectdetector/CMakeLists.txt index 5c34a21d..8bd7a0fe 100644 --- a/examples/objectdetector/CMakeLists.txt +++ b/examples/objectdetector/CMakeLists.txt @@ -1,5 +1,8 @@ -include_directories(${OD_SOURCE_DIR}/common/include) +include_directories(${DETECTORS_INCLUDE_DIR}) +include_directories(${COMMON_INCLUDE_DIR}) +include_directories(${SIMPLE_RANSAC_INCLUDE_DIR}) +include_directories(${DETECTORS_IMPL_DIR}) option(camera_recognition_example "Build the camera recognition example" ON) if(od_image_camera_example) From a23a893ffeed63fd88e066c7c07324a8b090c49b Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Tue, 24 May 2016 11:05:40 +0200 Subject: [PATCH 07/79] fixes last linking bug and fixes example code removing dynamic object creation --- common/include/common/bindings/svmlight.h | 1 + .../training/ODCADDetectTrainer3DGlobal.h | 5 -- .../training/ODCADRecogTrainerSnapshotBased.h | 4 -- detectors/src/global3D/CMakeLists.txt | 9 ++-- .../detection/ODCADDetector3DGlobal.cpp | 2 +- .../training/ODCADDetectTrainer3DGlobal.cpp | 2 +- .../gsoc2016_blog_giacomo.md | 40 +++++++++++++++- examples/apps/CMakeLists.txt | 2 +- examples/objectdetector/od_cascade_cam.cpp | 24 +++++----- examples/objectdetector/od_cascade_files.cpp | 27 ++++++----- .../od_example_framegenerator.cpp | 4 +- examples/objectdetector/od_hog_train.cpp | 43 ++++++++--------- .../od_image_cadrecog_camera.cpp | 39 ++++++++-------- .../od_image_cadrecog_files.cpp | 38 +++++++-------- .../objectdetector/od_image_customhog.cpp | 29 ++++++------ .../objectdetector/od_image_facerecog.cpp | 38 ++++++++------- .../objectdetector/od_image_hog_files.cpp | 22 +++++---- .../objectdetector/od_multialgo_files.cpp | 25 +++++----- examples/objectdetector/od_multialgo_pc.cpp | 46 +++++++++---------- examples/objectdetector/od_pc_global.cpp | 36 ++++++++------- .../objectdetector/od_pc_global_files.cpp | 37 ++++++++------- .../objectdetector/od_pc_global_real_time.cpp | 46 +++++++++++-------- 22 files changed, 280 insertions(+), 239 deletions(-) diff --git a/common/include/common/bindings/svmlight.h b/common/include/common/bindings/svmlight.h index b57277c0..83e8ee7b 100644 --- a/common/include/common/bindings/svmlight.h +++ b/common/include/common/bindings/svmlight.h @@ -56,6 +56,7 @@ Unless required by applicable law or agreed to in writing, software distributed #include <vector> // svmlight related // namespace required for avoiding collisions of declarations (e.g. LINEAR being declared in flann, svmlight and libsvm) + namespace svmlight { extern "C" { #include "3rdparty/svmlight/svm_common.h" diff --git a/detectors/include/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h b/detectors/include/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h index 400de8a5..6039a019 100644 --- a/detectors/include/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h +++ b/detectors/include/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h @@ -95,11 +95,6 @@ namespace od protected: std::string desc_name; }; - - /** \example objectdetector/od_pc_global_real_time.cpp - * \example objectdetector/od_pc_global_files.cpp - */ - } } diff --git a/detectors/include/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h b/detectors/include/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h index 42682489..c6e98cea 100644 --- a/detectors/include/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h +++ b/detectors/include/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h @@ -75,10 +75,6 @@ namespace od }; - /** \example objectdetector/od_image_cadrecog_camera.cpp - * \example objectdetector/od_image_cadrecog_files.cpp - */ - } } diff --git a/detectors/src/global3D/CMakeLists.txt b/detectors/src/global3D/CMakeLists.txt index 064e50b6..1632a4d6 100644 --- a/detectors/src/global3D/CMakeLists.txt +++ b/detectors/src/global3D/CMakeLists.txt @@ -7,7 +7,6 @@ set(build TRUE) #PCL_SUBSYS_OPTION(build "${SUBSYS_NAME}" "${SUBSYS_DESC}" ON) #PCL_SUBSYS_DEPEND(build "${SUBSYS_NAME}" DEPS ${SUBSYS_DEPS}) - if(build) set(INCLUDES @@ -20,10 +19,10 @@ if(build) "${DETECTORS_IMPL_DIR}/detection/ODCADDetector3DGlobal.hpp" ) - set(srcs - "src/ODPointCloudGlobalMatching.cpp" - "src/detection/ODCADDetector3DGlobal.cpp" - "src/training/ODCADDetectTrainer3DGlobal.cpp" + set(SOURCES + "ODPointCloudGlobalMatching.cpp" + "detection/ODCADDetector3DGlobal.cpp" + "training/ODCADDetectTrainer3DGlobal.cpp" ) include_directories(${DETECTORS_INCLUDE_DIR}) diff --git a/detectors/src/global3D/detection/ODCADDetector3DGlobal.cpp b/detectors/src/global3D/detection/ODCADDetector3DGlobal.cpp index c3e296fe..0d4159e6 100644 --- a/detectors/src/global3D/detection/ODCADDetector3DGlobal.cpp +++ b/detectors/src/global3D/detection/ODCADDetector3DGlobal.cpp @@ -27,4 +27,4 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 16.06.15. // -#include "detection/ODCADDetector3DGlobal.h" +#include "detectors/global3D/detection/ODCADDetector3DGlobal.h" diff --git a/detectors/src/global3D/training/ODCADDetectTrainer3DGlobal.cpp b/detectors/src/global3D/training/ODCADDetectTrainer3DGlobal.cpp index 2e9edacc..5cc821c6 100644 --- a/detectors/src/global3D/training/ODCADDetectTrainer3DGlobal.cpp +++ b/detectors/src/global3D/training/ODCADDetectTrainer3DGlobal.cpp @@ -27,7 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 16.06.15. // -#include "detection/ODCADDetectTrainer3DGlobal.h" +#include "detectors/global3D/training/ODCADDetectTrainer3DGlobal.h" #include <pcl/pcl_macros.h> #include <pcl/apps/3d_rec_framework/pipeline/global_nn_classifier.h> diff --git a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md index 6cda1f2b..99f944bc 100644 --- a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md +++ b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md @@ -70,10 +70,48 @@ After refactoring the folders, all the involved CMakeFiles had to be restructure Examples should not be built always, or at least it should be possible to not build them at all or partially. To do so I added the **WITH_EXAMPLES** option whichi enables the building of the examples. Then for each example I added a variable to trigger the building process of that examples: +@code option(image_hog_files_example "Build the hog image example" ON) if(image_hog_files_example) OD_ADD_EXAMPLE(od_image_hog_files FILES od_image_hog_files.cpp LINK_WITH od_common od_global_image_detector) endif() +@endcode -The option command adds an option in the cmake; the first parameter is the cmake option name (which for now is written in that way but it will probably change), the second is a description of the option, and the third is the default option value. I left it on on so that even unexperienced user can build some examples to test the library. The option is then followed by an if which checks the option vale and in case adds the example to the build process. I would like to change also the structure of the example folder by creating subfolders which then contain the different examples subdividing them by **type** . Each folder will then have its own *CMakeLists.txt* file which can be added by the parent cmake with the usual *add_subdirectory* command. This helps also since we can add automatically all the subfolders of the example folder to the build process without stating the names explicitly. \ No newline at end of file +The option command adds an option in the cmake; the first parameter is the cmake option name (which for now is written in that way but it will probably change), the second is a description of the option, and the third is the default option value. I left it on on so that even unexperienced user can build some examples to test the library. The option is then followed by an if which checks the option vale and in case adds the example to the build process. I would like to change also the structure of the example folder by creating subfolders which then contain the different examples subdividing them by **type** . Each folder will then have its own *CMakeLists.txt* file which can be added by the parent cmake with the usual *add_subdirectory* command. This helps also since we can add automatically all the subfolders of the example folder to the build process without stating the names explicitly. + +##Include structure## + +While continuing to restructure the library I came across a decision which can be solved in different ways but for which I still don't have the best solution. Lets assume we want to include a global 3D detector for example, we would use *ODCADDetector3DGlobal.h* . We could have in our file + +@code +#include <ODCADDetector3DGlobal.h> +@endcode + +and the compiler would need the include folder where the file is located to compile. This means that the file should be localted directly in the upper include folder, but this is not our case since the structure is like *detectors/global3D/detection/* so it would be better to have + +@code +#include <detectors/global3D/detection/ODCADDetector3DGlobal.h> +@endcode + +and include the upper level folder. To use the first solution we would need to include *detectors/global3D/detection/* but this is not really appealing since it would mess with the whole include structure. For now I opted for the second solution, but the include structure could be changed at any time. This way the include file structure resables exactly the source file structure: + +- common + * bindings + * pipeline + * utils +- detectors + * global2D + - detection + - training + * global3D + - detection + - training + * local2D + - detection + - training + * misc + - detection + +The new structure compiles fines except for three examples which have a linker bug (undefined reference to `vtable for od::g3d::ODCADDetectTrainer3DGlobal' +) which I am trying to resolve. I fixed a bit the source code of all the examples removing unnecessary includes, fixing namespaces, maintaining a common interface and avoiding dynamic memory allocation where possible. The next step after fixing the linker bug will be to fix the install paths for includes and libs. \ No newline at end of file diff --git a/examples/apps/CMakeLists.txt b/examples/apps/CMakeLists.txt index 48e64869..5d1b45fa 100644 --- a/examples/apps/CMakeLists.txt +++ b/examples/apps/CMakeLists.txt @@ -3,7 +3,7 @@ include_directories(${COMMON_INCLUDE_DIR}) include_directories(${SIMPLE_RANSAC_INCLUDE_DIR}) OD_ADD_EXAMPLE(odcadrecog_test_single_model FILES cadrecog2D/od_test_single_db_single_model.cpp - LINK_WITH od_common od_local_image_detector) + LINK_WITH od_common od_local_image_detector ) include_directories("${OD_BINARY_DIR}/generated") set(SOURCE_FILES version/opendetection.cpp) diff --git a/examples/objectdetector/od_cascade_cam.cpp b/examples/objectdetector/od_cascade_cam.cpp index c05be99d..d4da5728 100644 --- a/examples/objectdetector/od_cascade_cam.cpp +++ b/examples/objectdetector/od_cascade_cam.cpp @@ -30,33 +30,33 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <detectors/global2D/detection/ODCascadeDetector.h> -#include "common/utils/ODFrameGenerator.h" - -#include "common/pipeline/ObjectDetector.h" -#include "common/pipeline/ODDetection.h" - - -using namespace od; +#include <common/utils/ODFrameGenerator.h> int main(int argc, char *argv[]) { + + if(argc < 2){ + std::cout << "Wrong number of parameters: trained_data_dir" << std::endl; + return -1; + } + std::string trained_cascade(argv[1]); //detector - od::g2d::ODCascadeDetector *detector = new od::g2d::ODCascadeDetector; - detector->setTrainedDataLocation(trained_cascade); - detector->init(); + od::g2d::ODCascadeDetector detector; + detector.setTrainedDataLocation(trained_cascade); + detector.init(); //get scenes od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_DEVICE> frameGenerator(0); //GUI cv::namedWindow("Overlay", cv::WINDOW_NORMAL); - while(frameGenerator.isValid() && cv::waitKey(20) != 27) + while(frameGenerator.isValid() && cv::waitKey(10) != 27) { od::ODSceneImage * scene = frameGenerator.getNextFrame(); //Detect - ODDetections2D *detections = detector->detectOmni(scene); + od::ODDetections2D *detections = detector.detectOmni(scene); if(detections->size() > 0) cv::imshow("Overlay", detections->getMetainfoImage()); diff --git a/examples/objectdetector/od_cascade_files.cpp b/examples/objectdetector/od_cascade_files.cpp index 726a0893..e168bc1f 100644 --- a/examples/objectdetector/od_cascade_files.cpp +++ b/examples/objectdetector/od_cascade_files.cpp @@ -30,24 +30,23 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <detectors/global2D/detection/ODCascadeDetector.h> -//#include "detectors/global2D/detection/ODHOGDetector.h" -#include "common/utils/ODFrameGenerator.h" - -#include "common/pipeline/ObjectDetector.h" -#include "common/pipeline/ODDetection.h" - - -using namespace od; -using namespace std; +#include <common/utils/ODFrameGenerator.h> int main(int argc, char *argv[]) { - string trained_cascade(argv[1]), images(argv[2]); + + if(argc < 3){ + std::cout << "Wrong number of parameters: training_dir, query_images_dir" << std::endl; + return -1; + } + + std::string trained_cascade(argv[1]); + std::string images(argv[2]); //detector - od::g2d::ODCascadeDetector *detector = new od::g2d::ODCascadeDetector; - detector->setTrainedDataLocation(trained_cascade); - detector->init(); + od::g2d::ODCascadeDetector detector; + detector.setTrainedDataLocation(trained_cascade); + detector.init(); //get scenes od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(images); @@ -58,7 +57,7 @@ int main(int argc, char *argv[]) od::ODSceneImage * scene = frameGenerator.getNextFrame(); //Detect - ODDetections2D *detections = detector->detectOmni(scene); + od::ODDetections2D *detections = detector.detectOmni(scene); if(detections->size() > 0) { diff --git a/examples/objectdetector/od_example_framegenerator.cpp b/examples/objectdetector/od_example_framegenerator.cpp index 30a00d09..4b809c1b 100644 --- a/examples/objectdetector/od_example_framegenerator.cpp +++ b/examples/objectdetector/od_example_framegenerator.cpp @@ -33,9 +33,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -#include <boost/smart_ptr/make_shared_array.hpp> -#include "common/utils/ODFrameGenerator.h" - +#include <common/utils/ODFrameGenerator.h> int main(int argc, char *argv[]) { diff --git a/examples/objectdetector/od_hog_train.cpp b/examples/objectdetector/od_hog_train.cpp index 6903493a..6f3d2b09 100644 --- a/examples/objectdetector/od_hog_train.cpp +++ b/examples/objectdetector/od_hog_train.cpp @@ -32,43 +32,44 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <detectors/global2D/training/ODHOGTrainer.h> -#include "detectors/global2D/detection/ODHOGDetector.h" -#include "common/utils/ODFrameGenerator.h" - -#include "common/pipeline/ObjectDetector.h" -#include "common/pipeline/ODDetection.h" - - -using namespace od; +#include <detectors/global2D/detection/ODHOGDetector.h> +#include <common/utils/ODFrameGenerator.h> int main(int argc, char *argv[]) { - std::string pos_samples(argv[1]), neg_samples(argv[2]), trained_data_dir(argv[3]), test_images(argv[4]); + if(argc < 5){ + std::cout << "Wrong number of parameters: positive_samples_dir, negative_Samples_dir, trained_data_dir, test_iamges_dir" << std::endl; + return -1; + } - //trainer - od::g2d::ODHOGTrainer *trainer = new od::g2d::ODHOGTrainer("", trained_data_dir); - trainer->setPosSamplesDir(pos_samples); - trainer->setNegSamplesDir(neg_samples); - trainer->setNOFeaturesNeg(10); - trainer->setTrainHardNegetive(true); - trainer->train(); + std::string pos_samples(argv[1]) + std::string neg_samples(argv[2]); + std::string trained_data_dir(argv[3]); + std::string test_images(argv[4]); + //trainer + od::g2d::ODHOGTrainer trainer("", trained_data_dir); + trainer.setPosSamplesDir(pos_samples); + trainer.setNegSamplesDir(neg_samples); + trainer.setNOFeaturesNeg(10); + trainer.setTrainHardNegetive(true); + trainer.train(); - od::g2d::ODHOGDetector *detector = new od::g2d::ODHOGDetector; - detector->setTrainedDataLocation(trained_data_dir); - detector->init(); + od::g2d::ODHOGDetector detector; + detector.setTrainedDataLocation(trained_data_dir); + detector.init(); //get scenes od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(test_images); //GUI cv::namedWindow("Overlay", cv::WINDOW_NORMAL); - while(frameGenerator.isValid() && cv::waitKey(2000) != 27) + while(frameGenerator.isValid() && cv::waitKey(10) != 27) { od::ODSceneImage * scene = frameGenerator.getNextFrame(); //Detect - ODDetections2D *detections = detector->detectOmni(scene); + ODDetections2D *detections = detector.detectOmni(scene); if(detections->size() > 0) cv::imshow("Overlay", detections->renderMetainfo(*scene).getCVImage()); diff --git a/examples/objectdetector/od_image_cadrecog_camera.cpp b/examples/objectdetector/od_image_cadrecog_camera.cpp index bad77a6a..d30b07dd 100644 --- a/examples/objectdetector/od_image_cadrecog_camera.cpp +++ b/examples/objectdetector/od_image_cadrecog_camera.cpp @@ -30,47 +30,50 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -#include "detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h" -#include "detectors/local2D/detection/ODCADRecognizer2DLocal.h" -#include "common/utils/ODFrameGenerator.h" - -#include "common/pipeline/ObjectDetector.h" -#include "common/pipeline/ODDetection.h" -#include "detectors/local2D/ODImageLocalMatching.h" - - -using namespace od; +#include <detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h> +#include <detectors/local2D/detection/ODCADRecognizer2DLocal.h> +#include <common/utils/ODFrameGenerator.h> int main(int argc, char *argv[]) { - std::string training_input_dir(argv[1]), trained_data_dir(argv[2]); + + if(argc < 4){ + std::cout << "Wrong number of parameters: training_dir, trained_data_dir" << std::endl; + return -1; + } + + std::string training_input_dir(argv[1]); + std::string trained_data_dir(argv[2]); //trainer - od::l2d::ODCADRecogTrainerSnapshotBased *trainer = new od::l2d::ODCADRecogTrainerSnapshotBased(training_input_dir, trained_data_dir); + //TODO CHECK WHY COMMENTED + od::l2d::ODCADRecogTrainerSnapshotBased trainer(training_input_dir, trained_data_dir); //trainer->train(); //detector - od::l2d::ODCADRecognizer2DLocal *detector = new od::l2d::ODCADRecognizer2DLocal(trained_data_dir); + od::l2d::ODCADRecognizer2DLocal detector(trained_data_dir); //set commandline options type inputs - detector->parseParameterString("--use_gpu --fast --method=1 --error=2 --confidence=0.9 --iterations=500 --inliers=20 --metainfo"); - detector->setCameraIntrinsicFile("image_local_scenes/camera_webcam_fixed.xml"); //set some other inputs - detector->init(); + detector.parseParameterString("--use_gpu --fast --method=1 --error=2 --confidence=0.9 --iterations=500 --inliers=20 --metainfo"); + detector.setCameraIntrinsicFile("image_local_scenes/camera_webcam_fixed.xml"); //set some other inputs + detector.init(); //get scenes od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_DEVICE> frameGenerator(0); //GUI cv::namedWindow("Overlay", cv::WINDOW_NORMAL); - while(frameGenerator.isValid() && cv::waitKey(30) != 27) + while(frameGenerator.isValid() && cv::waitKey(10) != 27) { od::ODSceneImage * scene = frameGenerator.getNextFrame(); //Detect - ODDetections3D *detections = detector->detectOmni(scene); + ODDetections3D *detections = detector.detectOmni(scene); if(detections->size() > 0) cv::imshow("Overlay", detections->getMetainfoImage()); else cv::imshow("Overlay", scene->getCVImage()); + + delete scene; } return 0; diff --git a/examples/objectdetector/od_image_cadrecog_files.cpp b/examples/objectdetector/od_image_cadrecog_files.cpp index 90457370..1a6452aa 100644 --- a/examples/objectdetector/od_image_cadrecog_files.cpp +++ b/examples/objectdetector/od_image_cadrecog_files.cpp @@ -23,50 +23,52 @@ 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. -*/#include "detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h" -#include "detectors/local2D/detection/ODCADRecognizer2DLocal.h" -#include "common/utils/ODFrameGenerator.h" +*/ -#include "common/pipeline/ObjectDetector.h" -#include "common/pipeline/ODDetection.h" -#include "detectors/local2D/ODImageLocalMatching.h" - - -using namespace od; +#include <detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h> +#include <detectors/local2D/detection/ODCADRecognizer2DLocal.h> +#include <common/utils/ODFrameGenerator.h> int main(int argc, char *argv[]) { - std::string training_input_dir(argv[1]), trained_data_dir(argv[2]), query_images(argv[3]); + + if(argc < 4){ + std::cout << "Wrong number of parameters: training_dir, trained_data_dir, query_images_dir" << std::endl; + return -1; + } + + std::string training_input_dir(argv[1]); + std::string trained_data_dir(argv[2]); + std::string query_images(argv[3]); //trainer - od::l2d::ODCADRecogTrainerSnapshotBased *trainer = new od::l2d::ODCADRecogTrainerSnapshotBased(training_input_dir, trained_data_dir); + od::l2d::ODCADRecogTrainerSnapshotBased trainer(training_input_dir, trained_data_dir); //trainer->train(); //detector - od::l2d::ODCADRecognizer2DLocal *detector = new od::l2d::ODCADRecognizer2DLocal(trained_data_dir); + od::l2d::ODCADRecognizer2DLocal detector(trained_data_dir); //set commandline options type inputs - detector->parseParameterString("--use_gpu --method=1 --error=2 --confidence=0.8 --iterations=500 --inliers=20 --metainfo"); - detector->setCameraIntrinsicFile("/home/sarkar/models/opendetection_local/image_local_scenes/camera_householdnew.xml"); //set some other inputs - detector->init(); + detector.parseParameterString("--use_gpu --method=1 --error=2 --confidence=0.8 --iterations=500 --inliers=20 --metainfo"); + detector.setCameraIntrinsicFile("/home/sarkar/models/opendetection_local/image_local_scenes/camera_householdnew.xml"); //set some other inputs + detector.init(); //get scenes od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(query_images); //GUI cv::namedWindow("Overlay", cv::WINDOW_NORMAL); - while(frameGenerator.isValid() && cv::waitKey(30) != 27) + while(frameGenerator.isValid() && cv::waitKey(10) != 27) { od::ODSceneImage * scene = frameGenerator.getNextFrame(); cv::imshow("Overlay", scene->getCVImage()); //Detect - ODDetections3D *detections = detector->detectOmni(scene); + od::ODDetections3D *detections = detector.detectOmni(scene); if(detections->size() > 0) cv::imshow("Overlay", detections->getMetainfoImage()); else cv::imshow("Overlay", scene->getCVImage()); - delete scene; } diff --git a/examples/objectdetector/od_image_customhog.cpp b/examples/objectdetector/od_image_customhog.cpp index 48cdd12a..374ebee1 100644 --- a/examples/objectdetector/od_image_customhog.cpp +++ b/examples/objectdetector/od_image_customhog.cpp @@ -31,34 +31,33 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "detectors/global2D/detection/ODHOGDetector.h" -#include "common/utils/ODFrameGenerator.h" - -#include "common/pipeline/ObjectDetector.h" -#include "common/pipeline/ODDetection.h" - - -using namespace od; -using namespace std; +#include <detectors/global2D/detection/ODHOGDetector.h> +#include <common/utils/ODFrameGenerator.h> int main(int argc, char *argv[]) { - string trained_data_dir(argv[1]); + + if(argc < 4){ + std::cout << "Wrong number of parameters: trained_data_dir" << std::endl; + return -1; + } + + std::string trained_data_dir(argv[1]); //detector - g2d::ODHOGDetector *detector = new g2d::ODHOGDetector(trained_data_dir); - detector->setSvmtype(g2d::ODHOGDetector::OD_FILE); - detector->init(); + g2d::ODHOGDetector detector(trained_data_dir); + detector.setSvmtype(g2d::ODHOGDetector::OD_FILE); + detector.init(); //get scenes od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_DEVICE> frameGenerator(0); //GUI cv::namedWindow("Overlay", cv::WINDOW_NORMAL); - while(frameGenerator.isValid() && cv::waitKey(20) != 27) + while(frameGenerator.isValid() && cv::waitKey(10) != 27) { od::ODSceneImage * scene = frameGenerator.getNextFrame(); //Detect - ODDetections2D *detections = detector->detectOmni(scene); + ODDetections2D *detections = detector.detectOmni(scene); if(detections->size() > 0) cv::imshow("Overlay", detections->renderMetainfo(*scene).getCVImage()); diff --git a/examples/objectdetector/od_image_facerecog.cpp b/examples/objectdetector/od_image_facerecog.cpp index 65387b3b..cc21098b 100644 --- a/examples/objectdetector/od_image_facerecog.cpp +++ b/examples/objectdetector/od_image_facerecog.cpp @@ -31,43 +31,41 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <detectors/global2D/ODFaceRecognizer.h> - -#include "common/utils/ODFrameGenerator.h" -#include "common/pipeline/ObjectDetector.h" -#include "common/pipeline/ODDetection.h" - - -using namespace od; -using namespace std; +#include <common/utils/ODFrameGenerator.h> int main(int argc, char *argv[]) { - string training_input_csv(argv[1]), trained_xml(argv[2]), query_images(argv[3]); - //detector - g2d::ODFaceRecognizer * objdetector = new g2d::ODFaceRecognizer; - objdetector->setTrainingInputLocation(training_input_csv); - objdetector->setTrainingDataLocation(trained_xml); - objdetector->setRecogtype(g2d::ODFaceRecognizer::OD_FACE_FISCHER); - objdetector->initTrainer(); - objdetector->train(); + if(argc < 4){ + std::cout << "Wrong number of parameters: training_input_cvs, trained_xml, query_images_dir" << std::endl; + return -1; + } - objdetector->initDetector(); + std::string training_input_csv(argv[1]); + std::string trained_xml(argv[2]); + std::string query_images(argv[3]); + //detector + od::g2d::ODFaceRecognizer objdetector ; + objdetector.setTrainingInputLocation(training_input_csv); + objdetector.setTrainingDataLocation(trained_xml); + objdetector.setRecogtype(od::g2d::ODFaceRecognizer::OD_FACE_FISCHER); + objdetector.initTrainer(); + objdetector.train(); + objdetector.initDetector(); //get scenes od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(query_images); //GUI cv::namedWindow("Overlay", cv::WINDOW_NORMAL); - while(frameGenerator.isValid() && cv::waitKey(2000) != 27) + while(frameGenerator.isValid() && cv::waitKey(10) != 27) { od::ODSceneImage * scene = frameGenerator.getNextFrame(); //Detect - ODDetections *detections = objdetector->detect(scene); + od::ODDetections *detections = objdetector.detect(scene); (*detections)[0]->printSelf(); cv::imshow("Overlay", scene->getCVImage()); - delete scene; } diff --git a/examples/objectdetector/od_image_hog_files.cpp b/examples/objectdetector/od_image_hog_files.cpp index f42cc162..3eb64199 100644 --- a/examples/objectdetector/od_image_hog_files.cpp +++ b/examples/objectdetector/od_image_hog_files.cpp @@ -37,29 +37,33 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "common/pipeline/ObjectDetector.h" #include "common/pipeline/ODDetection.h" - -using namespace od; - int main(int argc, char *argv[]) { - std::string trained_hog(argv[1]), images(argv[2]); + + if(argc < 4){ + std::cout << "Wrong number of parameters: training_hog_dir, images_dir" << std::endl; + return -1; + } + + std::string trained_hog(argv[1]); + std::string images(argv[2]); //detector - od::g2d::ODHOGDetector *detector = new od::g2d::ODHOGDetector; - detector->setTrainedDataLocation(trained_hog); + od::g2d::ODHOGDetector detector ; + detector.setTrainedDataLocation(trained_hog); //detector->setSvmtype(g2d::ODHOGDetector::OD_DAIMLER_PEOPLE); - detector->init(); + detector.init(); //get scenes od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(images); //GUI cv::namedWindow("Overlay", cv::WINDOW_NORMAL); - while(frameGenerator.isValid() && cv::waitKey(1000) != 27) + while(frameGenerator.isValid() && cv::waitKey(10) != 27) { od::ODSceneImage * scene = frameGenerator.getNextFrame(); //Detect - ODDetections2D *detections = detector->detectOmni(scene); + ODDetections2D *detections = detector.detectOmni(scene); if(detections->size() > 0) cv::imshow("Overlay", detections->renderMetainfo(*scene).getCVImage()); diff --git a/examples/objectdetector/od_multialgo_files.cpp b/examples/objectdetector/od_multialgo_files.cpp index c978490a..d714927e 100644 --- a/examples/objectdetector/od_multialgo_files.cpp +++ b/examples/objectdetector/od_multialgo_files.cpp @@ -26,32 +26,33 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <detectors/misc/detection/ODDetectorMultiAlgo.h> #include <opencv2/highgui.hpp> -#include "common/utils/ODFrameGenerator.h" - -#include "common/pipeline/ObjectDetector.h" -#include "common/pipeline/ODDetection.h" - - -using namespace od; +#include <common/utils/ODFrameGenerator.h> int main(int argc, char *argv[]) { - std::string trained_data_dir(argv[1]), query_images(argv[2]);; + + if(argc < 3){ + std::cout << "Wrong number of parameters: training_dir, query_images" << std::endl; + return -1; + } + + std::string trained_data_dir(argv[1]); + std::string query_images(argv[2]); //detector - od::ODDetectorMultiAlgo2D *detector = new od::ODDetectorMultiAlgo2D(trained_data_dir); - detector->init(); + od::ODDetectorMultiAlgo2D detector(trained_data_dir); + detector.init(); //get scenes od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(query_images); //GUI cv::namedWindow("Overlay", cv::WINDOW_NORMAL); - while(frameGenerator.isValid() && cv::waitKey(3000) != 27) + while(frameGenerator.isValid() && cv::waitKey(10) != 27) { od::ODSceneImage * scene = frameGenerator.getNextFrame(); //Detect - ODDetections2D *detections = detector->detectOmni(scene); + ODDetections2D *detections = detector.detectOmni(scene); if(detections->size() > 0) cv::imshow("Overlay", detections->renderMetainfo(*scene).getCVImage()); diff --git a/examples/objectdetector/od_multialgo_pc.cpp b/examples/objectdetector/od_multialgo_pc.cpp index 818a2138..a16ce58b 100644 --- a/examples/objectdetector/od_multialgo_pc.cpp +++ b/examples/objectdetector/od_multialgo_pc.cpp @@ -24,13 +24,6 @@ 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. */ -#include <detectors/misc/detection/ODDetectorMultiAlgo.h> -#include <opencv2/highgui.hpp> -#include "common/utils/ODFrameGenerator.h" - -#include "common/pipeline/ObjectDetector.h" -#include "common/pipeline/ODDetection.h" - /** \brief Example of the usage of Multi algorithm detector for point cloud * @@ -38,24 +31,26 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ - -#include "common/pipeline/ObjectDetector.h" -#include "common/pipeline/ODDetection.h" -#include "detectors/global3D/ODPointCloudGlobalMatching.h" -#include "common/utils/ODFrameGenerator.h" - +#include <detectors/misc/detection/ODDetectorMultiAlgo.h> +#include <detectors/global3D/ODPointCloudGlobalMatching.h> +#include <common/utils/ODFrameGenerator.h> +#include <string> int main(int argc, char *argv[]) { - std::string training_input_dir(argv[1]), trained_data_dir(argv[2]); + if(argc < 3){ + std::cout << "Wrong number of parameters: training_dir, trained_data_dir" << std::endl; + return -1; + } + std::string training_input_dir(argv[1]); + std::string trained_data_dir(argv[2]); //detector - od::ODDetectorMultiAlgo *detector = new od::ODDetectorMultiAlgo(trained_data_dir); - detector->setTrainingInputLocation(training_input_dir); - detector->init(); - + od::ODDetectorMultiAlgo detector(trained_data_dir); + detector.setTrainingInputLocation(training_input_dir); + detector.init(); //GUI and feedback od::ODScenePointCloud<pcl::PointXYZRGBA> *frame; @@ -63,8 +58,9 @@ int main(int argc, char *argv[]) pcl::visualization::PCLVisualizer vis ("kinect"); size_t previous_cluster_size = 0; - od::ODFrameGenerator<od::ODScenePointCloud<pcl::PointXYZRGBA>, od::GENERATOR_TYPE_DEVICE> frameGenerator; + std::stringstream cluster_name; + pcl::PointXYZ pos; while(frameGenerator.isValid()) { @@ -75,34 +71,36 @@ int main(int argc, char *argv[]) vis.addPointCloud<pcl::PointXYZRGBA> (frame->getPointCloud(), "frame"); for (size_t i = 0; i < previous_cluster_size; i++) { - std::stringstream cluster_name; cluster_name << "cluster_" << i; vis.removePointCloud (cluster_name.str ()); vis.removeText3D (cluster_name.str() + "_txt"); + cluster_name.str(std::string()); cluster_name << "_ply_model_"; vis.removeShape (cluster_name.str ()); + cluster_name.str(std::string()); } //Detect od::ODDetections3D * detections = detector->detectOmni(frame); - //add all the detections in the visualizer with its id as text for (size_t i = 0; i < detections->size (); i++) { - std::stringstream cluster_name; cluster_name << "cluster_" << i; pcl::visualization::PointCloudColorHandlerRandom<pcl::PointXYZ> random_handler (detections->at(i)->getMetainfoCluster()); vis.addPointCloud<pcl::PointXYZ> (detections->at(i)->getMetainfoCluster(), random_handler, cluster_name.str ()); - pcl::PointXYZ pos; pos.x = detections->at(i)->getLocation()[0]; pos.y = detections->at(i)->getLocation()[1]; pos.z = detections->at(i)->getLocation()[2]; + pos.x = detections->at(i)->getLocation()[0]; + pos.y = detections->at(i)->getLocation()[1]; + pos.z = detections->at(i)->getLocation()[2]; + vis.addText3D (detections->at(i)->getId(), pos, 0.015f, 1, 0, 1, cluster_name.str() + "_txt", 0); + cluster_name.str(std::string()); } previous_cluster_size = detections->size (); - vis.spinOnce (); } diff --git a/examples/objectdetector/od_pc_global.cpp b/examples/objectdetector/od_pc_global.cpp index a4086e42..5e2e8513 100644 --- a/examples/objectdetector/od_pc_global.cpp +++ b/examples/objectdetector/od_pc_global.cpp @@ -33,36 +33,38 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ - -#include "common/pipeline/ObjectDetector.h" -#include "common/pipeline/ODDetection.h" -#include "detectors/global3D/ODPointCloudGlobalMatching.h" - +#include <iostream> +#include <detectors/global3D/ODPointCloudGlobalMatching.h> int main(int argc, char *argv[]) { - std::string training_input_dir(argv[1]), trained_data_dir(argv[2]), pointcloud_file(argv[3]); + if(argc < 4){ + std::cout << "Wrong number of parameters: training_dir, trained_data_dir, pointcloud_file" << std::endl; + return -1; + } + + std::string training_input_dir(argv[1]); + std::string trained_data_dir(argv[2]); + std::string pointcloud_file(argv[3]); //trainer - od::ODTrainer *trainer = new od::g3d::ODCADDetectTrainer3DGlobal(training_input_dir, trained_data_dir); - trainer->train(); - + od::g3d::ODCADDetectTrainer3DGlobal trainer(training_input_dir, trained_data_dir); + trainer.train(); //detector - od::g3d::ODCADDetector3DGlobal<> *detector = new od::g3d::ODCADDetector3DGlobal<>(); - detector->setTrainingInputLocation(training_input_dir); - detector->setTrainedDataLocation(trained_data_dir); - detector->init(); - + od::g3d::ODCADDetector3DGlobal<> detector ; + detector.setTrainingInputLocation(training_input_dir); + detector.setTrainedDataLocation(trained_data_dir); + detector.init(); //Get a scene - od::ODScenePointCloud<> *scene = new od::ODScenePointCloud<>(pointcloud_file); + od::ODScenePointCloud<> scene(pointcloud_file); - od::ODDetections3D * detections = detector->detectOmni(scene); + od::ODDetections3D * detections = detector.detectOmni(&scene); //feedback - for(int i = 0; i < detections->size(); i++) + for(size_t i = 0; i < detections->size(); i++) { detections->at(i)->printSelf(); } diff --git a/examples/objectdetector/od_pc_global_files.cpp b/examples/objectdetector/od_pc_global_files.cpp index f8e2bc6e..72f3c600 100644 --- a/examples/objectdetector/od_pc_global_files.cpp +++ b/examples/objectdetector/od_pc_global_files.cpp @@ -33,34 +33,34 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ - +#include <vector> #include <common/utils/ODFrameGenerator.h> -#include "common/pipeline/ObjectDetector.h" -//#include "common/pipeline/ODDetection.h" -#include "detectors/global3D/ODPointCloudGlobalMatching.h" - +#include <detectors/global3D/ODPointCloudGlobalMatching.h> -using namespace std; int main(int argc, char *argv[]) { - std::string training_input_dir(argv[1]), trained_data_dir(argv[2]), pointcloud_file(argv[3]); + if(argc < 4){ + std::cout << "Wrong number of parameters: training_dir, trained_data_dir, pointcloud_file" << std::endl; + return -1; + } - //trainer - od::ODTrainer *trainer = new od::g3d::ODCADDetectTrainer3DGlobal(training_input_dir, trained_data_dir); - trainer->train(); + std::string training_input_dir(argv[1]); + std::string trained_data_dir(argv[2]); + std::string pointcloud_file(argv[3]); + //trainer + od::g3d::ODCADDetectTrainer3DGlobal trainer(training_input_dir, trained_data_dir); + trainer.train(); //detector - od::g3d::ODCADDetector3DGlobal<> *detector = new od::g3d::ODCADDetector3DGlobal<>(); - detector->setTrainingInputLocation(training_input_dir); - detector->setTrainedDataLocation(trained_data_dir); - detector->init(); - + od::g3d::ODCADDetector3DGlobal<> detector; + detector.setTrainingInputLocation(training_input_dir); + detector.setTrainedDataLocation(trained_data_dir); + detector.init(); //Get a scene od::ODScenePointCloud<pcl::PointXYZRGBA> *frame; - vector<od::ODDetection3D *> detections; od::ODFrameGenerator<od::ODScenePointCloud<pcl::PointXYZRGBA>, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(pointcloud_file); while(frameGenerator.isValid()) @@ -68,10 +68,9 @@ int main(int argc, char *argv[]) //get frame frame = frameGenerator.getNextFrame(); + od::ODDetections3D * detections = detector.detectOmni(frame); - od::ODDetections3D * detections = detector->detectOmni(frame); - - for(int i = 0; i < detections->size(); i++) + for(size_t i = 0; i < detections->size(); i++) { detections->at(i)->printSelf(); } diff --git a/examples/objectdetector/od_pc_global_real_time.cpp b/examples/objectdetector/od_pc_global_real_time.cpp index 24f255a0..73327125 100644 --- a/examples/objectdetector/od_pc_global_real_time.cpp +++ b/examples/objectdetector/od_pc_global_real_time.cpp @@ -34,32 +34,38 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "common/pipeline/ObjectDetector.h" -#include "common/pipeline/ODDetection.h" -#include "detectors/global3D/ODPointCloudGlobalMatching.h" -#include "common/utils/ODFrameGenerator.h" - +#include <detectors/global3D/ODPointCloudGlobalMatching.h> +#include <common/utils/ODFrameGenerator.h> +#include <string> int main(int argc, char *argv[]) { - std::string training_input_dir(argv[1]), trained_data_dir(argv[2]); + if(argc < 3){ + std::cout << "Wrong number of parameters: training_dir, trained_data_dir" << std::endl; + return -1; + } + + std::string training_input_dir(argv[1]); + std::string trained_data_dir(argv[2]); //trainer - od::ODTrainer *trainer = new od::g3d::ODCADDetectTrainer3DGlobal(training_input_dir, trained_data_dir); - trainer->train(); + od::g3d::ODCADDetectTrainer3DGlobal trainer(training_input_dir, trained_data_dir); + trainer.train(); //detector - od::g3d::ODCADDetector3DGlobal<pcl::PointXYZRGBA> *detector = new od::g3d::ODCADDetector3DGlobal<pcl::PointXYZRGBA>(); - detector->setTrainingInputLocation(training_input_dir); - detector->setTrainedDataLocation(trained_data_dir); - detector->init(); + od::g3d::ODCADDetector3DGlobal<pcl::PointXYZRGBA> detector;; + detector.setTrainingInputLocation(training_input_dir); + detector.setTrainedDataLocation(trained_data_dir); + detector.init(); //GUI and feedback od::ODScenePointCloud<pcl::PointXYZRGBA> *frame; pcl::visualization::PCLVisualizer vis ("kinect"); od::ODFrameGenerator<od::ODScenePointCloud<pcl::PointXYZRGBA>, od::GENERATOR_TYPE_DEVICE> frameGenerator; + pcl::PointXYZ pos; + std::stringstream ss; while(frameGenerator.isValid()) { @@ -72,18 +78,20 @@ int main(int argc, char *argv[]) vis.addPointCloud<pcl::PointXYZRGBA> (frame->getPointCloud(), "frame"); //Detect - od::ODDetections3D * detections = detector->detectOmni(frame); + od::ODDetections3D * detections = detector.detectOmni(frame); //add all the detections in the visualizer with its id as text for (size_t i = 0; i < detections->size (); i++) { - std::stringstream cluster_name; - cluster_name << "cluster_" << i; pcl::visualization::PointCloudColorHandlerRandom<pcl::PointXYZ> random_handler (detections->at(i)->getMetainfoCluster()); - vis.addPointCloud<pcl::PointXYZ> (detections->at(i)->getMetainfoCluster(), random_handler, cluster_name.str ()); - - pcl::PointXYZ pos; pos.x = detections->at(i)->getLocation()[0]; pos.y = detections->at(i)->getLocation()[1]; pos.z = detections->at(i)->getLocation()[2]; - vis.addText3D (detections->at(i)->getId(), pos, 0.015f, 1, 0, 1, cluster_name.str() + "_txt", 0); + vis.addPointCloud<pcl::PointXYZ> (detections->at(i)->getMetainfoCluster(), random_handler, "cluster_" + i); + + pos.x = detections->at(i)->getLocation()[0]; + pos.y = detections->at(i)->getLocation()[1]; + pos.z = detections->at(i)->getLocation()[2]; + ss << "cluster_" << i << "_txt"; + vis.addText3D (detections->at(i)->getId(), pos, 0.015f, 1, 0, 1, ss.str(), 0); + ss.str(std::string()); } vis.spinOnce (); From 40fd6cfcf653ef5091fe1a7202866a9f266c46d2 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Tue, 24 May 2016 12:24:47 +0200 Subject: [PATCH 08/79] fixes new include structure with parent od folder and also fixes apps --- common/CMakeLists.txt | 22 +++++----- .../{ => od}/common/bindings/svmlight.h | 0 .../common/pipeline/ODAlgorithmBase.h | 2 +- .../{ => od}/common/pipeline/ODDetection.h | 6 +-- .../{ => od}/common/pipeline/ODDetector.h | 10 ++--- .../{ => od}/common/pipeline/ODScene.h | 0 .../{ => od}/common/pipeline/ODTrainer.h | 6 +-- .../{ => od}/common/pipeline/ObjectDetector.h | 2 +- .../common/utils/ODFeatureDetector2D.h | 4 +- .../{ => od}/common/utils/ODFrameGenerator.h | 4 +- common/include/{ => od}/common/utils/utils.h | 3 +- common/src/pipeline/ODScene.cpp | 2 +- common/src/pipeline/ObjectDetector.cpp | 2 +- common/src/utils/ODFeatureDetector2D.cpp | 2 +- common/src/utils/utils.cpp | 4 +- .../detection/ODCADDetector3DGlobal.hpp | 0 .../detectors/global2D/ODFaceRecognizer.h | 18 ++++---- .../global2D/detection/ODCascadeDetector.h | 8 ++-- .../global2D/detection/ODHOGDetector.h | 8 ++-- .../global2D/training/ODHOGTrainer.h | 4 +- .../global3D/ODPointCloudGlobalMatching.h | 8 ++-- .../detection/ODCADDetector3DGlobal.h | 4 +- .../training/ODCADDetectTrainer3DGlobal.h | 2 +- .../detectors/local2D/ODImageLocalMatching.h | 8 ++-- .../detection/ODCADRecognizer2DLocal.h | 12 +++--- .../training/ODCADRecogTrainerSnapshotBased.h | 6 +-- .../misc/detection/ODDetectorMultiAlgo.h | 4 +- .../simple_ransac_detection/RobustMatcher.cpp | 1 - .../simple_ransac_detection/RobustMatcher.h | 3 +- detectors/src/global2D/CMakeLists.txt | 12 +++--- detectors/src/global2D/ODFaceRecognizer.cpp | 2 +- .../global2D/detection/ODCascadeDetector.cpp | 2 +- .../src/global2D/detection/ODHOGDetector.cpp | 2 +- .../src/global2D/training/ODHOGTrainer.cpp | 4 +- detectors/src/global3D/CMakeLists.txt | 8 ++-- .../global3D/ODPointCloudGlobalMatching.cpp | 2 +- .../detection/ODCADDetector3DGlobal.cpp | 2 +- .../training/ODCADDetectTrainer3DGlobal.cpp | 2 +- detectors/src/local2D/CMakeLists.txt | 6 +-- .../src/local2D/ODImageLocalMatching.cpp | 2 +- .../detection/ODCADRecognizer2DLocal.cpp | 5 +-- .../ODCADRecogTrainerSnapshotBased.cpp | 2 +- detectors/src/misc/CMakeLists.txt | 2 +- .../misc/detection/ODDetectorMultiAlgo.cpp | 13 +++--- .../od_test_single_db_single_model.cpp | 40 +++++++++--------- examples/apps/global2D/od_multihog_app.cpp | 42 +++++++++---------- examples/objectdetector/od_cascade_cam.cpp | 4 +- examples/objectdetector/od_cascade_files.cpp | 4 +- .../od_example_framegenerator.cpp | 2 +- examples/objectdetector/od_hog_train.cpp | 6 +-- .../od_image_cadrecog_camera.cpp | 6 +-- .../od_image_cadrecog_files.cpp | 6 +-- .../objectdetector/od_image_customhog.cpp | 4 +- .../objectdetector/od_image_facerecog.cpp | 4 +- .../objectdetector/od_image_hog_files.cpp | 10 ++--- .../objectdetector/od_multialgo_files.cpp | 5 ++- examples/objectdetector/od_multialgo_pc.cpp | 6 +-- examples/objectdetector/od_pc_global.cpp | 2 +- .../objectdetector/od_pc_global_files.cpp | 4 +- .../objectdetector/od_pc_global_real_time.cpp | 4 +- 60 files changed, 182 insertions(+), 188 deletions(-) rename common/include/{ => od}/common/bindings/svmlight.h (100%) rename common/include/{ => od}/common/pipeline/ODAlgorithmBase.h (99%) rename common/include/{ => od}/common/pipeline/ODDetection.h (99%) rename common/include/{ => od}/common/pipeline/ODDetector.h (96%) rename common/include/{ => od}/common/pipeline/ODScene.h (100%) rename common/include/{ => od}/common/pipeline/ODTrainer.h (95%) rename common/include/{ => od}/common/pipeline/ObjectDetector.h (99%) rename common/include/{ => od}/common/utils/ODFeatureDetector2D.h (100%) rename common/include/{ => od}/common/utils/ODFrameGenerator.h (98%) rename common/include/{ => od}/common/utils/utils.h (99%) rename detectors/impl/{ => od/global3D}/detection/ODCADDetector3DGlobal.hpp (100%) rename detectors/include/{ => od}/detectors/global2D/ODFaceRecognizer.h (93%) rename detectors/include/{ => od}/detectors/global2D/detection/ODCascadeDetector.h (95%) rename detectors/include/{ => od}/detectors/global2D/detection/ODHOGDetector.h (97%) rename detectors/include/{ => od}/detectors/global2D/training/ODHOGTrainer.h (99%) rename detectors/include/{ => od}/detectors/global3D/ODPointCloudGlobalMatching.h (91%) rename detectors/include/{ => od}/detectors/global3D/detection/ODCADDetector3DGlobal.h (97%) rename detectors/include/{ => od}/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h (98%) rename detectors/include/{ => od}/detectors/local2D/ODImageLocalMatching.h (95%) rename detectors/include/{ => od}/detectors/local2D/detection/ODCADRecognizer2DLocal.h (97%) rename detectors/include/{ => od}/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h (94%) rename detectors/include/{ => od}/detectors/misc/detection/ODDetectorMultiAlgo.h (97%) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 7d5f33d9..b483f979 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -3,6 +3,8 @@ set(LIB_NAME od_${SUBSYS_NAME}) set(SUBSYS_DESC "The core module of OpenDetection having the pipeline logic") set(SUBSYS_DEPS ${OpenCV_LIBS} ${PCL_LIBRARIES}) set(COMMON_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) + + set(COMMON_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include PARENT_SCOPE) if(WITH_GPU) @@ -14,16 +16,16 @@ set(build TRUE) if(build) set(INCLUDES - "include/common/pipeline/ObjectDetector.h" - "include/common/pipeline/ODAlgorithmBase.h" - "include/common/pipeline/ODScene.h" - "include/common/pipeline/ODDetection.h" - "include/common/pipeline/ODTrainer.h" - "include/common/pipeline/ODDetector.h" - "include/common/utils/ODFrameGenerator.h" - "include/common/utils/utils.h" - "include/common/utils/ODFeatureDetector2D.h" - "include/common/bindings/svmlight.h" + "include/od/common/pipeline/ObjectDetector.h" + "include/od/common/pipeline/ODAlgorithmBase.h" + "include/od/common/pipeline/ODScene.h" + "include/od/common/pipeline/ODDetection.h" + "include/od/common/pipeline/ODTrainer.h" + "include/od/common/pipeline/ODDetector.h" + "include/od/common/utils/ODFrameGenerator.h" + "include/od/common/utils/utils.h" + "include/od/common/utils/ODFeatureDetector2D.h" + "include/od/common/bindings/svmlight.h" ) set(IMPL_INCLUDES "") diff --git a/common/include/common/bindings/svmlight.h b/common/include/od/common/bindings/svmlight.h similarity index 100% rename from common/include/common/bindings/svmlight.h rename to common/include/od/common/bindings/svmlight.h diff --git a/common/include/common/pipeline/ODAlgorithmBase.h b/common/include/od/common/pipeline/ODAlgorithmBase.h similarity index 99% rename from common/include/common/pipeline/ODAlgorithmBase.h rename to common/include/od/common/pipeline/ODAlgorithmBase.h index ba38a7cb..9363b43f 100644 --- a/common/include/common/pipeline/ODAlgorithmBase.h +++ b/common/include/od/common/pipeline/ODAlgorithmBase.h @@ -30,7 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef OPENDETECTION_ODALGORITHMBASE_H #define OPENDETECTION_ODALGORITHMBASE_H -#include<iostream> +#include <iostream> #include <boost/filesystem.hpp> #include <boost/algorithm/string.hpp> diff --git a/common/include/common/pipeline/ODDetection.h b/common/include/od/common/pipeline/ODDetection.h similarity index 99% rename from common/include/common/pipeline/ODDetection.h rename to common/include/od/common/pipeline/ODDetection.h index ccf1e7a4..3f7c19e7 100644 --- a/common/include/common/pipeline/ODDetection.h +++ b/common/include/od/common/pipeline/ODDetection.h @@ -31,9 +31,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef OPENDETECTION_ODDETECTION_H #define OPENDETECTION_ODDETECTION_H -#include "iostream" -#include "common/utils/utils.h" -#include "ODScene.h" +#include <iostream> +#include "od/common/utils/utils.h" +#include "od/common/pipeline/ODScene.h" #include <Eigen/Core> #include <opencv2/core/eigen.hpp> diff --git a/common/include/common/pipeline/ODDetector.h b/common/include/od/common/pipeline/ODDetector.h similarity index 96% rename from common/include/common/pipeline/ODDetector.h rename to common/include/od/common/pipeline/ODDetector.h index 5ff482cc..b9ea3266 100644 --- a/common/include/common/pipeline/ODDetector.h +++ b/common/include/od/common/pipeline/ODDetector.h @@ -31,11 +31,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef OPENDETECTION_ODDETECTOR_H #define OPENDETECTION_ODDETECTOR_H -#include "ObjectDetector.h" -#include "ODScene.h" -#include "ODDetection.h" -#include "ObjectDetector.h" -#include "ODAlgorithmBase.h" +#include "od/common/pipeline/ObjectDetector.h" +#include "od/common/pipeline/ODScene.h" +#include "od/common/pipeline/ODDetection.h" +#include "od/common/pipeline/ObjectDetector.h" +#include "od/common/pipeline/ODAlgorithmBase.h" #include <iostream> namespace od diff --git a/common/include/common/pipeline/ODScene.h b/common/include/od/common/pipeline/ODScene.h similarity index 100% rename from common/include/common/pipeline/ODScene.h rename to common/include/od/common/pipeline/ODScene.h diff --git a/common/include/common/pipeline/ODTrainer.h b/common/include/od/common/pipeline/ODTrainer.h similarity index 95% rename from common/include/common/pipeline/ODTrainer.h rename to common/include/od/common/pipeline/ODTrainer.h index e3fa2c32..c1bbcb2c 100644 --- a/common/include/common/pipeline/ODTrainer.h +++ b/common/include/od/common/pipeline/ODTrainer.h @@ -31,12 +31,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef OPENDETECTION_TRAINER_H #define OPENDETECTION_TRAINER_H -#include<iostream> +#include <iostream> #include <boost/filesystem.hpp> #include <boost/algorithm/string.hpp> -#include "ODAlgorithmBase.h" -#include "ObjectDetector.h" +#include "od/common/pipeline/ODAlgorithmBase.h" +#include "od/common/pipeline/ObjectDetector.h" namespace bf = boost::filesystem; diff --git a/common/include/common/pipeline/ObjectDetector.h b/common/include/od/common/pipeline/ObjectDetector.h similarity index 99% rename from common/include/common/pipeline/ObjectDetector.h rename to common/include/od/common/pipeline/ObjectDetector.h index 29bd9174..b802d2ce 100644 --- a/common/include/common/pipeline/ObjectDetector.h +++ b/common/include/od/common/pipeline/ObjectDetector.h @@ -33,7 +33,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <string> #include <vector> -#include "ODDetection.h" +#include "od/common/pipeline/ODDetection.h" namespace od diff --git a/common/include/common/utils/ODFeatureDetector2D.h b/common/include/od/common/utils/ODFeatureDetector2D.h similarity index 100% rename from common/include/common/utils/ODFeatureDetector2D.h rename to common/include/od/common/utils/ODFeatureDetector2D.h index 24535a47..cfeda570 100644 --- a/common/include/common/utils/ODFeatureDetector2D.h +++ b/common/include/od/common/utils/ODFeatureDetector2D.h @@ -6,14 +6,14 @@ #define OBJECT_DETECTION_FEATUREDETECTOR_H #include <iostream> - #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/features2d/features2d.hpp> #include <opencv2/xfeatures2d.hpp> +#include <opencv2/cudafeatures2d.hpp> + #include "SiftGPU.h" -#include <opencv2/cudafeatures2d.hpp> namespace od { diff --git a/common/include/common/utils/ODFrameGenerator.h b/common/include/od/common/utils/ODFrameGenerator.h similarity index 98% rename from common/include/common/utils/ODFrameGenerator.h rename to common/include/od/common/utils/ODFrameGenerator.h index 2fdf5fec..66804d1c 100644 --- a/common/include/common/utils/ODFrameGenerator.h +++ b/common/include/od/common/utils/ODFrameGenerator.h @@ -5,8 +5,8 @@ #ifndef OPENDETECTION_ODFRAMEGENERATOR_H #define OPENDETECTION_ODFRAMEGENERATOR_H -#include "common/pipeline/ODDetection.h" -#include "common/pipeline/ODScene.h" +#include "od/common/pipeline/ODDetection.h" +#include "od/common/pipeline/ODScene.h" #include <iostream> #include <opencv2/videoio.hpp> #include <pcl/apps/3d_rec_framework/tools/openni_frame_source.h> diff --git a/common/include/common/utils/utils.h b/common/include/od/common/utils/utils.h similarity index 99% rename from common/include/common/utils/utils.h rename to common/include/od/common/utils/utils.h index 0bee4314..de0f7765 100644 --- a/common/include/common/utils/utils.h +++ b/common/include/od/common/utils/utils.h @@ -5,6 +5,7 @@ #ifndef OPENDETECTION_UTILS_H #define OPENDETECTION_UTILS_H +//TODO remove time from lib and use chrono #include <sys/time.h> #include <boost/preprocessor.hpp> #include <boost/filesystem.hpp> @@ -16,8 +17,6 @@ #include <iostream> #include <glob.h> - - namespace bf = boost::filesystem; /** \brief misclenious helping functions; will be refactored later diff --git a/common/src/pipeline/ODScene.cpp b/common/src/pipeline/ODScene.cpp index c8fd70dc..00fa53de 100644 --- a/common/src/pipeline/ODScene.cpp +++ b/common/src/pipeline/ODScene.cpp @@ -28,4 +28,4 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 10.06.15. // -#include "common/pipeline/ODScene.h" +#include "od/common/pipeline/ODScene.h" diff --git a/common/src/pipeline/ObjectDetector.cpp b/common/src/pipeline/ObjectDetector.cpp index c7493be1..08e25ad3 100644 --- a/common/src/pipeline/ObjectDetector.cpp +++ b/common/src/pipeline/ObjectDetector.cpp @@ -28,4 +28,4 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 03.06.15. // -#include "common/pipeline/ObjectDetector.h" +#include "od/common/pipeline/ObjectDetector.h" diff --git a/common/src/utils/ODFeatureDetector2D.cpp b/common/src/utils/ODFeatureDetector2D.cpp index ffb0edff..851c32dd 100644 --- a/common/src/utils/ODFeatureDetector2D.cpp +++ b/common/src/utils/ODFeatureDetector2D.cpp @@ -3,7 +3,7 @@ // #include <GL/gl.h> -#include "common/utils/ODFeatureDetector2D.h" +#include "od/common/utils/ODFeatureDetector2D.h" using namespace std; namespace od diff --git a/common/src/utils/utils.cpp b/common/src/utils/utils.cpp index 4e45fb8b..09521b1d 100644 --- a/common/src/utils/utils.cpp +++ b/common/src/utils/utils.cpp @@ -1,8 +1,8 @@ // // Created by sarkar on 19.06.15. // -#include "common/utils/utils.h" -#include "opencv2/imgproc/imgproc.hpp" +#include "od/common/utils/utils.h" +#include <opencv2/imgproc/imgproc.hpp> #include <boost/functional/hash.hpp> using namespace std; diff --git a/detectors/impl/detection/ODCADDetector3DGlobal.hpp b/detectors/impl/od/global3D/detection/ODCADDetector3DGlobal.hpp similarity index 100% rename from detectors/impl/detection/ODCADDetector3DGlobal.hpp rename to detectors/impl/od/global3D/detection/ODCADDetector3DGlobal.hpp diff --git a/detectors/include/detectors/global2D/ODFaceRecognizer.h b/detectors/include/od/detectors/global2D/ODFaceRecognizer.h similarity index 93% rename from detectors/include/detectors/global2D/ODFaceRecognizer.h rename to detectors/include/od/detectors/global2D/ODFaceRecognizer.h index 2237f794..45c1783e 100644 --- a/detectors/include/detectors/global2D/ODFaceRecognizer.h +++ b/detectors/include/od/detectors/global2D/ODFaceRecognizer.h @@ -30,15 +30,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef OPENDETECTION_ODPOINTCLOUDGLOBALMATCHING_H #define OPENDETECTION_ODPOINTCLOUDGLOBALMATCHING_H -#include <common/pipeline/ODDetector.h> -#include <common/pipeline/ODTrainer.h> -#include <common/utils/utils.h> - -#include "opencv2/core.hpp" -#include "opencv2/face.hpp" -#include "opencv2/highgui.hpp" -#include "opencv2/imgproc.hpp" -#include "opencv2/objdetect.hpp" +#include "od/common/pipeline/ODDetector.h" +#include "od/common/pipeline/ODTrainer.h" +#include "od/common/utils/utils.h" + +#include <opencv2/core.hpp> +#include <opencv2/face.hpp> +#include <opencv2/highgui.hpp> +#include <opencv2/imgproc.hpp> +#include <opencv2/objdetect.hpp> diff --git a/detectors/include/detectors/global2D/detection/ODCascadeDetector.h b/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h similarity index 95% rename from detectors/include/detectors/global2D/detection/ODCascadeDetector.h rename to detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h index 27b92c4c..34cf4786 100644 --- a/detectors/include/detectors/global2D/detection/ODCascadeDetector.h +++ b/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h @@ -30,10 +30,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef OPENDETECTION_ODCASCADEDETECTOR_H #define OPENDETECTION_ODCASCADEDETECTOR_H -#include "common/pipeline/ODDetector.h" -#include "common/pipeline/ODScene.h" -#include "common/utils/utils.h" -#include "common/utils/ODFeatureDetector2D.h" +#include "od/common/pipeline/ODDetector.h" +#include "od/common/pipeline/ODScene.h" +#include "od/common/utils/utils.h" +#include "od/common/utils/ODFeatureDetector2D.h" #include <iostream> #include <opencv2/opencv.hpp> diff --git a/detectors/include/detectors/global2D/detection/ODHOGDetector.h b/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h similarity index 97% rename from detectors/include/detectors/global2D/detection/ODHOGDetector.h rename to detectors/include/od/detectors/global2D/detection/ODHOGDetector.h index dad5b064..e3df86f0 100644 --- a/detectors/include/detectors/global2D/detection/ODHOGDetector.h +++ b/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h @@ -30,10 +30,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef OPENDETECTION_ODHOGMULTISCALEDETECTOR_H #define OPENDETECTION_ODHOGMULTISCALEDETECTOR_H -#include "common/pipeline/ODDetector.h" -#include "common/pipeline/ODScene.h" -#include "common/utils/utils.h" -#include "common/utils/ODFeatureDetector2D.h" +#include "od/common/pipeline/ODDetector.h" +#include "od/common/pipeline/ODScene.h" +#include "od/common/utils/utils.h" +#include "od/common/utils/ODFeatureDetector2D.h" #include <iostream> #include <opencv2/opencv.hpp> diff --git a/detectors/include/detectors/global2D/training/ODHOGTrainer.h b/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h similarity index 99% rename from detectors/include/detectors/global2D/training/ODHOGTrainer.h rename to detectors/include/od/detectors/global2D/training/ODHOGTrainer.h index 377d47e3..32772631 100644 --- a/detectors/include/detectors/global2D/training/ODHOGTrainer.h +++ b/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h @@ -31,8 +31,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define OPENDETECTION_ODHOGTRAINER_H #include <opencv2/objdetect.hpp> -#include "common/pipeline/ODTrainer.h" -#include "common/utils/utils.h" +#include "od/common/pipeline/ODTrainer.h" +#include "od/common/utils/utils.h" diff --git a/detectors/include/detectors/global3D/ODPointCloudGlobalMatching.h b/detectors/include/od/detectors/global3D/ODPointCloudGlobalMatching.h similarity index 91% rename from detectors/include/detectors/global3D/ODPointCloudGlobalMatching.h rename to detectors/include/od/detectors/global3D/ODPointCloudGlobalMatching.h index a0ec5f46..b41c990b 100644 --- a/detectors/include/detectors/global3D/ODPointCloudGlobalMatching.h +++ b/detectors/include/od/detectors/global3D/ODPointCloudGlobalMatching.h @@ -30,10 +30,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef OPENDETECTION_ODPOINTCLOUDGLOBALMATCHING_H #define OPENDETECTION_ODPOINTCLOUDGLOBALMATCHING_H -#include <common/pipeline/ODDetector.h> -#include <common/pipeline/ODTrainer.h> -#include "detectors/global3D/training/ODCADDetectTrainer3DGlobal.h" -#include "detectors/global3D/detection/ODCADDetector3DGlobal.h" +#include "od/common/pipeline/ODDetector.h" +#include "od/common/pipeline/ODTrainer.h" +#include "od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h" +#include "od/detectors/global3D/detection/ODCADDetector3DGlobal.h" namespace od { diff --git a/detectors/include/detectors/global3D/detection/ODCADDetector3DGlobal.h b/detectors/include/od/detectors/global3D/detection/ODCADDetector3DGlobal.h similarity index 97% rename from detectors/include/detectors/global3D/detection/ODCADDetector3DGlobal.h rename to detectors/include/od/detectors/global3D/detection/ODCADDetector3DGlobal.h index 517b0662..4bc1984e 100644 --- a/detectors/include/detectors/global3D/detection/ODCADDetector3DGlobal.h +++ b/detectors/include/od/detectors/global3D/detection/ODCADDetector3DGlobal.h @@ -30,7 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef OPENDETECTION_ODPOINTCLOUDGLOBALMATCHINGDETECTOR_H #define OPENDETECTION_ODPOINTCLOUDGLOBALMATCHINGDETECTOR_H -#include <common/pipeline/ODDetector.h> +#include "od/common/pipeline/ODDetector.h" #include <pcl/pcl_macros.h> #include <pcl/apps/3d_rec_framework/pipeline/global_nn_classifier.h> @@ -110,5 +110,5 @@ namespace od } } -#include "detection/ODCADDetector3DGlobal.hpp" +#include "od/global3D/detection/ODCADDetector3DGlobal.hpp" #endif //OPENDETECTION_ODPOINTCLOUDGLOBALMATCHINGDETECTOR_H diff --git a/detectors/include/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h b/detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h similarity index 98% rename from detectors/include/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h rename to detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h index 6039a019..adb3e910 100644 --- a/detectors/include/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h +++ b/detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h @@ -30,7 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef OPENDETECTION_ODPOINTCLOUDGLOBALMATCHINGTRAINER_H #define OPENDETECTION_ODPOINTCLOUDGLOBALMATCHINGTRAINER_H -#include <common/pipeline/ODTrainer.h> +#include "od/common/pipeline/ODTrainer.h" #include <iostream> namespace od diff --git a/detectors/include/detectors/local2D/ODImageLocalMatching.h b/detectors/include/od/detectors/local2D/ODImageLocalMatching.h similarity index 95% rename from detectors/include/detectors/local2D/ODImageLocalMatching.h rename to detectors/include/od/detectors/local2D/ODImageLocalMatching.h index bfef0c49..d079cfd7 100644 --- a/detectors/include/detectors/local2D/ODImageLocalMatching.h +++ b/detectors/include/od/detectors/local2D/ODImageLocalMatching.h @@ -31,10 +31,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef OPENDETECTION_ODIMAGELOCALMATCHINGSIMPLE_H #define OPENDETECTION_ODIMAGELOCALMATCHINGSIMPLE_H -#include "common/pipeline/ODScene.h" -#include "common/pipeline/ODTrainer.h" -#include "common/pipeline/ODDetector.h" -#include "common/pipeline/ObjectDetector.h" +#include "od/common/pipeline/ODScene.h" +#include "od/common/pipeline/ODTrainer.h" +#include "od/common/pipeline/ODDetector.h" +#include "od/common/pipeline/ObjectDetector.h" namespace od diff --git a/detectors/include/detectors/local2D/detection/ODCADRecognizer2DLocal.h b/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h similarity index 97% rename from detectors/include/detectors/local2D/detection/ODCADRecognizer2DLocal.h rename to detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h index 0ed95dc6..0cde456f 100644 --- a/detectors/include/detectors/local2D/detection/ODCADRecognizer2DLocal.h +++ b/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h @@ -31,12 +31,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef OPENDETECTION_SIMPLERANSACDETECTOR_H #define OPENDETECTION_SIMPLERANSACDETECTOR_H -#include "detectors/local2D/ODImageLocalMatching.h" +#include "od/detectors/local2D/ODImageLocalMatching.h" -#include "common/pipeline/ODDetector.h" -#include "common/pipeline/ODScene.h" -#include "common/utils/utils.h" -#include "common/utils/ODFeatureDetector2D.h" +#include "od/common/pipeline/ODDetector.h" +#include "od/common/pipeline/ODScene.h" +#include "od/common/utils/utils.h" +#include "od/common/utils/ODFeatureDetector2D.h" #include <iostream> #include <time.h> @@ -45,8 +45,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <opencv2/core/utility.hpp> #include <opencv2/calib3d.hpp> - - #include "simple_ransac_detection/Utils.h" diff --git a/detectors/include/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h b/detectors/include/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h similarity index 94% rename from detectors/include/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h rename to detectors/include/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h index c6e98cea..46f86e43 100644 --- a/detectors/include/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h +++ b/detectors/include/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h @@ -32,9 +32,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define _SNAPSHOT_SNAP_VTK_H_ -#include "common/pipeline/ODTrainer.h" -#include "common/utils/utils.h" -#include "detectors/local2D/ODImageLocalMatching.h" +#include "od/common/pipeline/ODTrainer.h" +#include "od/common/utils/utils.h" +#include "od/detectors/local2D/ODImageLocalMatching.h" #define VIEW_ANGLE 30 diff --git a/detectors/include/detectors/misc/detection/ODDetectorMultiAlgo.h b/detectors/include/od/detectors/misc/detection/ODDetectorMultiAlgo.h similarity index 97% rename from detectors/include/detectors/misc/detection/ODDetectorMultiAlgo.h rename to detectors/include/od/detectors/misc/detection/ODDetectorMultiAlgo.h index 29da28d0..361fb60e 100644 --- a/detectors/include/detectors/misc/detection/ODDetectorMultiAlgo.h +++ b/detectors/include/od/detectors/misc/detection/ODDetectorMultiAlgo.h @@ -30,8 +30,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef OPENDETECTION_ODDETECTORMULTIALGO_H #define OPENDETECTION_ODDETECTORMULTIALGO_H -#include <common/pipeline/ODDetector.h> -#include <common/pipeline/ODDetection.h> +#include "od/common/pipeline/ODDetector.h" +#include "od/common/pipeline/ODDetection.h" namespace od { diff --git a/detectors/simple_ransac_detection/RobustMatcher.cpp b/detectors/simple_ransac_detection/RobustMatcher.cpp index cd5fffd2..49844cea 100644 --- a/detectors/simple_ransac_detection/RobustMatcher.cpp +++ b/detectors/simple_ransac_detection/RobustMatcher.cpp @@ -14,7 +14,6 @@ #include <opencv2/features2d.hpp> #include <opencv2/xfeatures2d.hpp> #include <opencv2/ml.hpp> -#include <common/utils/utils.h> void convertToUnsignedSiftGPU(cv::Mat const &cv_des, vector<unsigned char> &siftgpu_des) diff --git a/detectors/simple_ransac_detection/RobustMatcher.h b/detectors/simple_ransac_detection/RobustMatcher.h index 4674b33c..3801e7d5 100644 --- a/detectors/simple_ransac_detection/RobustMatcher.h +++ b/detectors/simple_ransac_detection/RobustMatcher.h @@ -13,7 +13,8 @@ #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/features2d/features2d.hpp> -#include "common/utils/ODFeatureDetector2D.h" +#include "od/common/utils/ODFeatureDetector2D.h" +#include "od/common/utils/utils.h" #include "Model.h" using namespace std; diff --git a/detectors/src/global2D/CMakeLists.txt b/detectors/src/global2D/CMakeLists.txt index c8fe5825..d6b85eac 100644 --- a/detectors/src/global2D/CMakeLists.txt +++ b/detectors/src/global2D/CMakeLists.txt @@ -20,10 +20,10 @@ if(build) "training/ODHOGTrainer.cpp" ) set(INCLUDES - "${DETECTORS_INCLUDE_DIR}/detectors/global2D/ODFaceRecognizer.h" - "${DETECTORS_INCLUDE_DIR}/detectors/global2D/detection/ODHOGDetector.h" - "${DETECTORS_INCLUDE_DIR}/detectors/global2D/detection/ODCascadeDetector.h" - "${DETECTORS_INCLUDE_DIR}/detectors/global2D/training/ODHOGTrainer.h" + "${DETECTORS_INCLUDE_DIR}/od/detectors/global2D/ODFaceRecognizer.h" + "${DETECTORS_INCLUDE_DIR}/od/detectors/global2D/detection/ODHOGDetector.h" + "${DETECTORS_INCLUDE_DIR}/od/detectors/global2D/detection/ODCascadeDetector.h" + "${DETECTORS_INCLUDE_DIR}/od/detectors/global2D/training/ODHOGTrainer.h" ) else() set(SOURCES @@ -32,8 +32,8 @@ if(build) ) set(INCLUDES - "${DETECTORS_INCLUDE_DIR}/detectors/global2D/ODFaceRecognizer.h" - "${DETECTORS_INCLUDE_DIR}/detectors/global2D/detection/ODCascadeDetector.h" + "${DETECTORS_INCLUDE_DIR}/od/detectors/global2D/ODFaceRecognizer.h" + "${DETECTORS_INCLUDE_DIR}/od/detectors/global2D/detection/ODCascadeDetector.h" ) endif() diff --git a/detectors/src/global2D/ODFaceRecognizer.cpp b/detectors/src/global2D/ODFaceRecognizer.cpp index 186ae386..7461530f 100644 --- a/detectors/src/global2D/ODFaceRecognizer.cpp +++ b/detectors/src/global2D/ODFaceRecognizer.cpp @@ -27,7 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 16.07.15. // -#include "detectors/global2D/ODFaceRecognizer.h" +#include "od/detectors/global2D/ODFaceRecognizer.h" using namespace cv; using namespace std; diff --git a/detectors/src/global2D/detection/ODCascadeDetector.cpp b/detectors/src/global2D/detection/ODCascadeDetector.cpp index e01e2cdc..d544b024 100644 --- a/detectors/src/global2D/detection/ODCascadeDetector.cpp +++ b/detectors/src/global2D/detection/ODCascadeDetector.cpp @@ -27,7 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 17.07.15. // -#include "detectors/global2D/detection/ODCascadeDetector.h" +#include "od/detectors/global2D/detection/ODCascadeDetector.h" using namespace cv; using namespace std; diff --git a/detectors/src/global2D/detection/ODHOGDetector.cpp b/detectors/src/global2D/detection/ODHOGDetector.cpp index 75e8902a..c455e44a 100644 --- a/detectors/src/global2D/detection/ODHOGDetector.cpp +++ b/detectors/src/global2D/detection/ODHOGDetector.cpp @@ -27,7 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 15.07.15. // -#include "ODHOGDetector.h" +#include "od/detectors/global2D/detection/ODHOGDetector.h" using namespace std; diff --git a/detectors/src/global2D/training/ODHOGTrainer.cpp b/detectors/src/global2D/training/ODHOGTrainer.cpp index 38095050..49a44e58 100644 --- a/detectors/src/global2D/training/ODHOGTrainer.cpp +++ b/detectors/src/global2D/training/ODHOGTrainer.cpp @@ -28,7 +28,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -#include "ODHOGTrainer.h" +#include "od/detectors/global2D/training/ODHOGTrainer.h" #include <fstream> @@ -40,7 +40,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <opencv2/imgproc/imgproc.hpp> //binding class for svmlight -#include "common/bindings/svmlight.h" +#include "od/common/bindings/svmlight.h" #define TRAINHOG_SVM_TO_TRAIN SVMlight diff --git a/detectors/src/global3D/CMakeLists.txt b/detectors/src/global3D/CMakeLists.txt index 1632a4d6..5367c979 100644 --- a/detectors/src/global3D/CMakeLists.txt +++ b/detectors/src/global3D/CMakeLists.txt @@ -10,13 +10,13 @@ set(build TRUE) if(build) set(INCLUDES - "${DETECTORS_INCLUDE_DIR}/detectors/global3D/ODPointCloudGlobalMatching.h" - "${DETECTORS_INCLUDE_DIR}/detectors/global3D/detection/ODCADDetector3DGlobal.h" - "${DETECTORS_INCLUDE_DIR}/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h" + "${DETECTORS_INCLUDE_DIR}/od/detectors/global3D/ODPointCloudGlobalMatching.h" + "${DETECTORS_INCLUDE_DIR}/od/detectors/global3D/detection/ODCADDetector3DGlobal.h" + "${DETECTORS_INCLUDE_DIR}/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h" ) set(IMPL_INCLUDES - "${DETECTORS_IMPL_DIR}/detection/ODCADDetector3DGlobal.hpp" + "${DETECTORS_IMPL_DIR}/od/global3D/detection/ODCADDetector3DGlobal.hpp" ) set(SOURCES diff --git a/detectors/src/global3D/ODPointCloudGlobalMatching.cpp b/detectors/src/global3D/ODPointCloudGlobalMatching.cpp index 8c8b305a..0dc02c0f 100644 --- a/detectors/src/global3D/ODPointCloudGlobalMatching.cpp +++ b/detectors/src/global3D/ODPointCloudGlobalMatching.cpp @@ -27,4 +27,4 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 16.06.15. // -#include "detectors/global3D/ODPointCloudGlobalMatching.h" +#include "od/detectors/global3D/ODPointCloudGlobalMatching.h" diff --git a/detectors/src/global3D/detection/ODCADDetector3DGlobal.cpp b/detectors/src/global3D/detection/ODCADDetector3DGlobal.cpp index 0d4159e6..815c678d 100644 --- a/detectors/src/global3D/detection/ODCADDetector3DGlobal.cpp +++ b/detectors/src/global3D/detection/ODCADDetector3DGlobal.cpp @@ -27,4 +27,4 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 16.06.15. // -#include "detectors/global3D/detection/ODCADDetector3DGlobal.h" +#include "od/detectors/global3D/detection/ODCADDetector3DGlobal.h" diff --git a/detectors/src/global3D/training/ODCADDetectTrainer3DGlobal.cpp b/detectors/src/global3D/training/ODCADDetectTrainer3DGlobal.cpp index 5cc821c6..37cb9f22 100644 --- a/detectors/src/global3D/training/ODCADDetectTrainer3DGlobal.cpp +++ b/detectors/src/global3D/training/ODCADDetectTrainer3DGlobal.cpp @@ -27,7 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 16.06.15. // -#include "detectors/global3D/training/ODCADDetectTrainer3DGlobal.h" +#include "od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h" #include <pcl/pcl_macros.h> #include <pcl/apps/3d_rec_framework/pipeline/global_nn_classifier.h> diff --git a/detectors/src/local2D/CMakeLists.txt b/detectors/src/local2D/CMakeLists.txt index d16129dc..bc2c7ee7 100644 --- a/detectors/src/local2D/CMakeLists.txt +++ b/detectors/src/local2D/CMakeLists.txt @@ -15,9 +15,9 @@ set(build TRUE) if(build) set(INCLUDES - "${DETECTORS_INCLUDE_DIR}/detectors/local2D/ODImageLocalMatching.h" - "${DETECTORS_INCLUDE_DIR}/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h" - "${DETECTORS_INCLUDE_DIR}/detectors/local2D/detection/ODCADRecognizer2DLocal.h" + "${DETECTORS_INCLUDE_DIR}/od/detectors/local2D/ODImageLocalMatching.h" + "${DETECTORS_INCLUDE_DIR}/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h" + "${DETECTORS_INCLUDE_DIR}/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h" ) set(IMPL_INCLUDES "") diff --git a/detectors/src/local2D/ODImageLocalMatching.cpp b/detectors/src/local2D/ODImageLocalMatching.cpp index 7df54ed7..b09fe256 100644 --- a/detectors/src/local2D/ODImageLocalMatching.cpp +++ b/detectors/src/local2D/ODImageLocalMatching.cpp @@ -28,4 +28,4 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 05.06.15. // -#include "detectors/local2D/ODImageLocalMatching.h" +#include "od/detectors/local2D/ODImageLocalMatching.h" diff --git a/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp b/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp index 1db20315..8e939d1a 100644 --- a/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp +++ b/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp @@ -28,8 +28,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 08.06.15. // -#include "detectors/local2D/detection/ODCADRecognizer2DLocal.h" - +#include "od/detectors/local2D/detection/ODCADRecognizer2DLocal.h" #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> @@ -38,7 +37,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <opencv2/xfeatures2d.hpp> #include <opencv2/viz.hpp> #include <sys/time.h> -#include <common/utils/utils.h> +#include "od/common/utils/utils.h" // PnP Tutorial #include "simple_ransac_detection/Mesh.h" diff --git a/detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp b/detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp index 20cd488a..8b81b4db 100644 --- a/detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp +++ b/detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp @@ -24,7 +24,7 @@ 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. */ -#include "detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h" +#include "od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h" //simplecube/newcube.obj simplecube/flower.jpeg //Lion/Final.obj Lion/Texture.png //BigDaddy/Param.obj BigDaddy/Parameterization.png diff --git a/detectors/src/misc/CMakeLists.txt b/detectors/src/misc/CMakeLists.txt index 7cdcf363..d46b0f77 100644 --- a/detectors/src/misc/CMakeLists.txt +++ b/detectors/src/misc/CMakeLists.txt @@ -10,7 +10,7 @@ set(build TRUE) if(build) set(INCLUDES - "${DETECTORS_INCLUDE_DIR}/detectors/misc/detection/ODDetectorMultiAlgo.h" + "${DETECTORS_INCLUDE_DIR}/od/detectors/misc/detection/ODDetectorMultiAlgo.h" ) set(IMPL_INCLUDES "") diff --git a/detectors/src/misc/detection/ODDetectorMultiAlgo.cpp b/detectors/src/misc/detection/ODDetectorMultiAlgo.cpp index bbf95016..a573ff01 100644 --- a/detectors/src/misc/detection/ODDetectorMultiAlgo.cpp +++ b/detectors/src/misc/detection/ODDetectorMultiAlgo.cpp @@ -27,16 +27,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 06.08.15. // -#include "detectors/misc/detection/ODDetectorMultiAlgo.h" -#include "detectors/global2D/detection/ODCascadeDetector.h" -#include "detectors/global2D/detection/ODHOGDetector.h" -#include "detectors/global2D/ODFaceRecognizer.h" - -#include "detectors/local2D/detection/ODCADRecognizer2DLocal.h" +#include "od/detectors/misc/detection/ODDetectorMultiAlgo.h" +#include "od/detectors/global2D/detection/ODCascadeDetector.h" +#include "od/detectors/global2D/detection/ODHOGDetector.h" +#include "od/detectors/global2D/ODFaceRecognizer.h" +#include "od/detectors/local2D/detection/ODCADRecognizer2DLocal.h" //3D detectors -#include "detectors/global3D/detection/ODCADDetector3DGlobal.h" +#include "od/detectors/global3D/detection/ODCADDetector3DGlobal.h" using namespace std; diff --git a/examples/apps/cadrecog2D/od_test_single_db_single_model.cpp b/examples/apps/cadrecog2D/od_test_single_db_single_model.cpp index 882b4882..6dac963d 100644 --- a/examples/apps/cadrecog2D/od_test_single_db_single_model.cpp +++ b/examples/apps/cadrecog2D/od_test_single_db_single_model.cpp @@ -24,30 +24,30 @@ 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. */ -#include "detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h" -#include "detectors/local2D/detection/ODCADRecognizer2DLocal.h" -#include "common/utils/ODFrameGenerator.h" - -#include "common/pipeline/ObjectDetector.h" -#include "common/pipeline/ODDetection.h" -#include "detectors/local2D/ODImageLocalMatching.h" -///home/sarkar/work/opencv/real_time_pose_estimation/Data/Lion/test_images/IMG_*.JPG /home/sarkar/work/opencv/face /home/sarkar/models/MODELS_DATA/Lion/test_images/Lion_old/original/out_camera_data_lion_old.xml /home/sarkar/models/MODELS_DATA/Lion/test_images/Lion_old/original/ransac_detection_txtmap.txt - - -using namespace od; -using namespace std; +#include "od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h" +#include "od/detectors/local2D/detection/ODCADRecognizer2DLocal.h" +#include "od/common/utils/ODFrameGenerator.h" int main(int argc, char *argv[]) { - std::string imagespath(argv[1]), modelsPath(argv[2]), camerapath(argv[3]), outputfile(argv[4]); - ofstream logfile(outputfile.c_str()); + + if(argc < 5){ + std::cout << "Wrong number of parameters: image_path model_path camera_path output_file" << std::endl; + return -1; + } + + std::string imagespath(argv[1]); + std::string modelsPath(argv[2]); + std::string camerapath(argv[3]); + std::string outputfile(argv[4]); + std::ofstream logfile(outputfile.c_str()); //detector - od::l2d::ODCADRecognizer2DLocal *detector = new od::l2d::ODCADRecognizer2DLocal(modelsPath); + od::l2d::ODCADRecognizer2DLocal detector(modelsPath); //set commandline options type inputs - detector->parseParameterString("--use_gpu --method=1 --error=2 --confidence=0.5 --iterations=1000 --inliers=6 --metainfo"); - detector->setCameraIntrinsicFile(camerapath); //set some other inputs - detector->init(); + detector.parseParameterString("--use_gpu --method=1 --error=2 --confidence=0.5 --iterations=1000 --inliers=6 --metainfo"); + detector.setCameraIntrinsicFile(camerapath); //set some other inputs + detector.init(); //get scenes od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(imagespath); @@ -59,7 +59,7 @@ int main(int argc, char *argv[]) //cv::imshow("Overlay", scene->getCVImage()); //Detect - ODDetections3D *detections = detector->detectOmni(scene); + od::ODDetections3D *detections = detector.detectOmni(scene); if(detections->size() > 0) { @@ -68,7 +68,7 @@ int main(int argc, char *argv[]) for (int i = 0; i < detections->size(); i++) { - ODDetection3D *detection = detections->at(i); + od::ODDetection3D *detection = detections->at(i); detection->printSelf(); logfile << detection->getId() << endl; } diff --git a/examples/apps/global2D/od_multihog_app.cpp b/examples/apps/global2D/od_multihog_app.cpp index 4189bd1b..27e5e2f3 100644 --- a/examples/apps/global2D/od_multihog_app.cpp +++ b/examples/apps/global2D/od_multihog_app.cpp @@ -30,15 +30,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -#include "detectors/global2D/detection/ODHOGDetector.h" -#include "common/utils/ODFrameGenerator.h" - -#include "common/pipeline/ObjectDetector.h" -#include "common/pipeline/ODDetection.h" - - -using namespace od; -using namespace std; +#include "od/detectors/global2D/detection/ODHOGDetector.h" +#include "od/common/utils/ODFrameGenerator.h" cv::Size sizesingle(640, 480); @@ -49,19 +42,26 @@ int main(int argc, char *argv[]) //get 3 detectors of different types - vector<string> messages; messages.push_back("Original"); - vector<g2d::ODHOGDetector*> detectors; - g2d::ODHOGDetector *detector1 = new g2d::ODHOGDetector; // - messages.push_back("OpenCV Default People"); detectors.push_back(detector1); + std::vector<string> messages; + messages.push_back("Original"); + + std::vector<g2d::ODHOGDetector> detectors; + od::g2d::ODHOGDetector detector1; // + messages.push_back("OpenCV Default People"); + detectors.push_back(detector1); - g2d::ODHOGDetector *detector2 = new g2d::ODHOGDetector; detector2->setSvmtype(g2d::ODHOGDetector::OD_DAIMLER_PEOPLE); - messages.push_back("OpenCV Daimler People"); detectors.push_back(detector2); + od::g2d::ODHOGDetector detector2; + detector2->setSvmtype(g2d::ODHOGDetector::OD_DAIMLER_PEOPLE); + messages.push_back("OpenCV Daimler People"); + detectors.push_back(detector2); - g2d::ODHOGDetector *detector3 = new g2d::ODHOGDetector(trained_data_dir); - messages.push_back("Custom HOG from trained data"); detectors.push_back(detector3); + od::g2d::ODHOGDetector detector3 (trained_data_dir); + messages.push_back("Custom HOG from trained data"); + detectors.push_back(detector3); //init all detectors - for (int i = 0; i < detectors.size(); i++) detectors[i]->init(); + for (size_t i = 0; i < detectors.size(); i++) + detectors[i].init(); //get scenes od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_DEVICE> frameGenerator(input_video); @@ -75,13 +75,13 @@ int main(int argc, char *argv[]) { od::ODSceneImage * scene = frameGenerator.getNextFrame(); - vector<cv::Mat> images_to_show; + std::vector<cv::Mat> images_to_show; images_to_show.push_back(scene->getCVImage()); //push the first image //detect 3 times - for (int i = 0; i < detectors.size(); i++) + for (size_t i = 0; i < detectors.size(); i++) { - ODDetections2D *detections = detectors[i]->detectOmni(scene); + od::ODDetections2D *detections = detectors[i].detectOmni(scene); if(detections->size() > 0) images_to_show.push_back(detections->renderMetainfo(*scene).getCVImage()); else images_to_show.push_back(scene->getCVImage()); diff --git a/examples/objectdetector/od_cascade_cam.cpp b/examples/objectdetector/od_cascade_cam.cpp index d4da5728..dc50d16d 100644 --- a/examples/objectdetector/od_cascade_cam.cpp +++ b/examples/objectdetector/od_cascade_cam.cpp @@ -29,8 +29,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -#include <detectors/global2D/detection/ODCascadeDetector.h> -#include <common/utils/ODFrameGenerator.h> +#include "od/detectors/global2D/detection/ODCascadeDetector.h" +#include "od/common/utils/ODFrameGenerator.h" int main(int argc, char *argv[]) { diff --git a/examples/objectdetector/od_cascade_files.cpp b/examples/objectdetector/od_cascade_files.cpp index e168bc1f..962ca61e 100644 --- a/examples/objectdetector/od_cascade_files.cpp +++ b/examples/objectdetector/od_cascade_files.cpp @@ -29,8 +29,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -#include <detectors/global2D/detection/ODCascadeDetector.h> -#include <common/utils/ODFrameGenerator.h> +#include "od/detectors/global2D/detection/ODCascadeDetector.h" +#include "od/common/utils/ODFrameGenerator.h" int main(int argc, char *argv[]) { diff --git a/examples/objectdetector/od_example_framegenerator.cpp b/examples/objectdetector/od_example_framegenerator.cpp index 4b809c1b..16eb1a4d 100644 --- a/examples/objectdetector/od_example_framegenerator.cpp +++ b/examples/objectdetector/od_example_framegenerator.cpp @@ -33,7 +33,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -#include <common/utils/ODFrameGenerator.h> +#include "od/common/utils/ODFrameGenerator.h" int main(int argc, char *argv[]) { diff --git a/examples/objectdetector/od_hog_train.cpp b/examples/objectdetector/od_hog_train.cpp index 6f3d2b09..a356003a 100644 --- a/examples/objectdetector/od_hog_train.cpp +++ b/examples/objectdetector/od_hog_train.cpp @@ -31,9 +31,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include <detectors/global2D/training/ODHOGTrainer.h> -#include <detectors/global2D/detection/ODHOGDetector.h> -#include <common/utils/ODFrameGenerator.h> +#include "od/detectors/global2D/training/ODHOGTrainer.h" +#include "od/detectors/global2D/detection/ODHOGDetector.h" +#include "od/common/utils/ODFrameGenerator.h" int main(int argc, char *argv[]) { diff --git a/examples/objectdetector/od_image_cadrecog_camera.cpp b/examples/objectdetector/od_image_cadrecog_camera.cpp index d30b07dd..1e3fa549 100644 --- a/examples/objectdetector/od_image_cadrecog_camera.cpp +++ b/examples/objectdetector/od_image_cadrecog_camera.cpp @@ -30,9 +30,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -#include <detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h> -#include <detectors/local2D/detection/ODCADRecognizer2DLocal.h> -#include <common/utils/ODFrameGenerator.h> +#include "od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h" +#include "od/detectors/local2D/detection/ODCADRecognizer2DLocal.h" +#include "od/common/utils/ODFrameGenerator.h" int main(int argc, char *argv[]) { diff --git a/examples/objectdetector/od_image_cadrecog_files.cpp b/examples/objectdetector/od_image_cadrecog_files.cpp index 1a6452aa..3b129785 100644 --- a/examples/objectdetector/od_image_cadrecog_files.cpp +++ b/examples/objectdetector/od_image_cadrecog_files.cpp @@ -25,9 +25,9 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include <detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h> -#include <detectors/local2D/detection/ODCADRecognizer2DLocal.h> -#include <common/utils/ODFrameGenerator.h> +#include "od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h" +#include "od/detectors/local2D/detection/ODCADRecognizer2DLocal.h" +#include "od/common/utils/ODFrameGenerator.h" int main(int argc, char *argv[]) { diff --git a/examples/objectdetector/od_image_customhog.cpp b/examples/objectdetector/od_image_customhog.cpp index 374ebee1..90d14423 100644 --- a/examples/objectdetector/od_image_customhog.cpp +++ b/examples/objectdetector/od_image_customhog.cpp @@ -31,8 +31,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include <detectors/global2D/detection/ODHOGDetector.h> -#include <common/utils/ODFrameGenerator.h> +#include "od/detectors/global2D/detection/ODHOGDetector.h" +#include "od/common/utils/ODFrameGenerator.h" int main(int argc, char *argv[]) { diff --git a/examples/objectdetector/od_image_facerecog.cpp b/examples/objectdetector/od_image_facerecog.cpp index cc21098b..23fee756 100644 --- a/examples/objectdetector/od_image_facerecog.cpp +++ b/examples/objectdetector/od_image_facerecog.cpp @@ -30,8 +30,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include <detectors/global2D/ODFaceRecognizer.h> -#include <common/utils/ODFrameGenerator.h> +#include "od/detectors/global2D/ODFaceRecognizer.h" +#include "od/common/utils/ODFrameGenerator.h" int main(int argc, char *argv[]) { diff --git a/examples/objectdetector/od_image_hog_files.cpp b/examples/objectdetector/od_image_hog_files.cpp index 3eb64199..26f441a2 100644 --- a/examples/objectdetector/od_image_hog_files.cpp +++ b/examples/objectdetector/od_image_hog_files.cpp @@ -29,13 +29,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -#include <detectors/global2D/detection/ODCascadeDetector.h> -#include <detectors/global2D/detection/ODHOGDetector.h> -//#include "detectors/global2D/detection/ODHOGDetector.h" -#include "common/utils/ODFrameGenerator.h" - -#include "common/pipeline/ObjectDetector.h" -#include "common/pipeline/ODDetection.h" +#include "od/detectors/global2D/detection/ODCascadeDetector.h" +#include "od/detectors/global2D/detection/ODHOGDetector.h" +#include "od/common/utils/ODFrameGenerator.h" int main(int argc, char *argv[]) { diff --git a/examples/objectdetector/od_multialgo_files.cpp b/examples/objectdetector/od_multialgo_files.cpp index d714927e..e1b5b771 100644 --- a/examples/objectdetector/od_multialgo_files.cpp +++ b/examples/objectdetector/od_multialgo_files.cpp @@ -24,9 +24,10 @@ 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. */ -#include <detectors/misc/detection/ODDetectorMultiAlgo.h> + #include <opencv2/highgui.hpp> -#include <common/utils/ODFrameGenerator.h> +#include "detectors/misc/detection/ODDetectorMultiAlgo.h" +#include "od/common/utils/ODFrameGenerator.h" int main(int argc, char *argv[]) { diff --git a/examples/objectdetector/od_multialgo_pc.cpp b/examples/objectdetector/od_multialgo_pc.cpp index a16ce58b..bca1ba9c 100644 --- a/examples/objectdetector/od_multialgo_pc.cpp +++ b/examples/objectdetector/od_multialgo_pc.cpp @@ -31,9 +31,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -#include <detectors/misc/detection/ODDetectorMultiAlgo.h> -#include <detectors/global3D/ODPointCloudGlobalMatching.h> -#include <common/utils/ODFrameGenerator.h> +#include "od/detectors/misc/detection/ODDetectorMultiAlgo.h" +#include "od/detectors/global3D/ODPointCloudGlobalMatching.h" +#include "common/utils/ODFrameGenerator.h" #include <string> int main(int argc, char *argv[]) diff --git a/examples/objectdetector/od_pc_global.cpp b/examples/objectdetector/od_pc_global.cpp index 5e2e8513..43cc7ef0 100644 --- a/examples/objectdetector/od_pc_global.cpp +++ b/examples/objectdetector/od_pc_global.cpp @@ -34,7 +34,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <iostream> -#include <detectors/global3D/ODPointCloudGlobalMatching.h> +#include "od/detectors/global3D/ODPointCloudGlobalMatching.h" int main(int argc, char *argv[]) { diff --git a/examples/objectdetector/od_pc_global_files.cpp b/examples/objectdetector/od_pc_global_files.cpp index 72f3c600..2e2dc38f 100644 --- a/examples/objectdetector/od_pc_global_files.cpp +++ b/examples/objectdetector/od_pc_global_files.cpp @@ -34,8 +34,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <vector> -#include <common/utils/ODFrameGenerator.h> -#include <detectors/global3D/ODPointCloudGlobalMatching.h> +#include "od/common/utils/ODFrameGenerator.h" +#include "od/detectors/global3D/ODPointCloudGlobalMatching.h" int main(int argc, char *argv[]) { diff --git a/examples/objectdetector/od_pc_global_real_time.cpp b/examples/objectdetector/od_pc_global_real_time.cpp index 73327125..107c0f6b 100644 --- a/examples/objectdetector/od_pc_global_real_time.cpp +++ b/examples/objectdetector/od_pc_global_real_time.cpp @@ -34,8 +34,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include <detectors/global3D/ODPointCloudGlobalMatching.h> -#include <common/utils/ODFrameGenerator.h> +#include "od/detectors/global3D/ODPointCloudGlobalMatching.h" +#include "od/common/utils/ODFrameGenerator.h" #include <string> int main(int argc, char *argv[]) From fdc02c715c3a302d67f5dd9224968981ea260028 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Tue, 24 May 2016 14:59:48 +0200 Subject: [PATCH 09/79] restructures simple_ransac_detection into local2D and fixes the cmake structure --- detectors/CMakeLists.txt | 3 - .../detection/ODCADRecognizer2DLocal.h | 2 +- .../simple_ransac_detection/CMakeLists.txt | 13 - .../simple_ransac_detection/CsvReader.cpp | 80 --- detectors/simple_ransac_detection/CsvReader.h | 40 -- .../simple_ransac_detection/CsvWriter.cpp | 48 -- detectors/simple_ransac_detection/CsvWriter.h | 25 - detectors/simple_ransac_detection/Mesh.cpp | 82 --- detectors/simple_ransac_detection/Mesh.h | 87 --- detectors/simple_ransac_detection/Model.cpp | 181 ------ detectors/simple_ransac_detection/Model.h | 67 -- .../ModelRegistration.cpp | 35 -- .../ModelRegistration.h | 43 -- .../simple_ransac_detection/PnPProblem.cpp | 336 ---------- .../simple_ransac_detection/PnPProblem.h | 71 --- .../simple_ransac_detection/RobustMatcher.cpp | 328 ---------- .../simple_ransac_detection/RobustMatcher.h | 104 ---- detectors/simple_ransac_detection/Utils.cpp | 343 ----------- detectors/simple_ransac_detection/Utils.h | 80 --- .../main_detection_image_wo_k.cpp | 578 ------------------ .../main_detection_video.cpp | 562 ----------------- .../main_registration.cpp | 268 -------- .../scale_intrinsic.cpp | 41 -- detectors/src/local2D/CMakeLists.txt | 24 +- .../detection/ODCADRecognizer2DLocal.cpp | 12 +- 25 files changed, 27 insertions(+), 3426 deletions(-) delete mode 100644 detectors/simple_ransac_detection/CMakeLists.txt delete mode 100644 detectors/simple_ransac_detection/CsvReader.cpp delete mode 100644 detectors/simple_ransac_detection/CsvReader.h delete mode 100644 detectors/simple_ransac_detection/CsvWriter.cpp delete mode 100644 detectors/simple_ransac_detection/CsvWriter.h delete mode 100644 detectors/simple_ransac_detection/Mesh.cpp delete mode 100644 detectors/simple_ransac_detection/Mesh.h delete mode 100644 detectors/simple_ransac_detection/Model.cpp delete mode 100644 detectors/simple_ransac_detection/Model.h delete mode 100644 detectors/simple_ransac_detection/ModelRegistration.cpp delete mode 100644 detectors/simple_ransac_detection/ModelRegistration.h delete mode 100644 detectors/simple_ransac_detection/PnPProblem.cpp delete mode 100644 detectors/simple_ransac_detection/PnPProblem.h delete mode 100644 detectors/simple_ransac_detection/RobustMatcher.cpp delete mode 100644 detectors/simple_ransac_detection/RobustMatcher.h delete mode 100644 detectors/simple_ransac_detection/Utils.cpp delete mode 100644 detectors/simple_ransac_detection/Utils.h delete mode 100644 detectors/simple_ransac_detection/main_detection_image_wo_k.cpp delete mode 100644 detectors/simple_ransac_detection/main_detection_video.cpp delete mode 100644 detectors/simple_ransac_detection/main_registration.cpp delete mode 100644 detectors/simple_ransac_detection/scale_intrinsic.cpp diff --git a/detectors/CMakeLists.txt b/detectors/CMakeLists.txt index b3e84695..6e0093f5 100644 --- a/detectors/CMakeLists.txt +++ b/detectors/CMakeLists.txt @@ -1,14 +1,11 @@ set(DETECTORS_DIR ${CMAKE_CURRENT_SOURCE_DIR}) -set(SIMPLE_RANSAC_INCLUDE_DIR ${DETECTORS_DIR}) set(DETECTORS_INCLUDE_DIR ${DETECTORS_DIR}/include) set(DETECTORS_IMPL_DIR ${DETECTORS_DIR}/impl) #Add in parent scope to build examples set(DETECTORS_INCLUDE_DIR ${DETECTORS_DIR}/include PARENT_SCOPE) set(DETECTORS_IMPL_DIR ${DETECTORS_DIR}/impl PARENT_SCOPE) -set(SIMPLE_RANSAC_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR} PARENT_SCOPE) -add_subdirectory(simple_ransac_detection) add_subdirectory("src/local2D") add_subdirectory("src/global2D") add_subdirectory("src/global3D") diff --git a/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h b/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h index 0cde456f..104783d0 100644 --- a/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h +++ b/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h @@ -45,7 +45,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <opencv2/core/utility.hpp> #include <opencv2/calib3d.hpp> -#include "simple_ransac_detection/Utils.h" +#include "od/detectors/local2D/simple_ransac_detection/Utils.h" class Model; diff --git a/detectors/simple_ransac_detection/CMakeLists.txt b/detectors/simple_ransac_detection/CMakeLists.txt deleted file mode 100644 index 7ce0afc7..00000000 --- a/detectors/simple_ransac_detection/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -set(SIMPLE_RANSAC_SRC ${CMAKE_CURRENT_SOURCE_DIR}) -set(SIMPLE_RANSAC_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR} PARENT_SCOPE) - -set(SIMPLE_RANSAC_SRC_FILES - ${SIMPLE_RANSAC_SRC}/CsvReader.cpp - ${SIMPLE_RANSAC_SRC}/CsvWriter.cpp - ${SIMPLE_RANSAC_SRC}/ModelRegistration.cpp - ${SIMPLE_RANSAC_SRC}/Mesh.cpp - ${SIMPLE_RANSAC_SRC}/Model.cpp - ${SIMPLE_RANSAC_SRC}/PnPProblem.cpp - ${SIMPLE_RANSAC_SRC}/Utils.cpp - ${SIMPLE_RANSAC_SRC}/RobustMatcher.cpp -PARENT_SCOPE) \ No newline at end of file diff --git a/detectors/simple_ransac_detection/CsvReader.cpp b/detectors/simple_ransac_detection/CsvReader.cpp deleted file mode 100644 index 4da06dd4..00000000 --- a/detectors/simple_ransac_detection/CsvReader.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include "CsvReader.h" -#include <stdlib.h> - -/** The default constructor of the CSV reader Class */ -CsvReader::CsvReader(const string &path, const char &separator){ - _file.open(path.c_str(), ifstream::in); - _separator = separator; -} - -/* Read a plane text file with .ply format */ -void CsvReader::readPLY(vector<Point3f> &list_vertex, vector<vector<int> > &list_triangles) -{ - std::string line, tmp_str, n; - int num_vertex = 0, num_triangles = 0; - int count = 0; - bool end_header = false; - bool end_vertex = false; - - // Read the whole *.ply file - while (getline(_file, line)) { - stringstream liness(line); - - // read header - if(!end_header) - { - getline(liness, tmp_str, _separator); - if( tmp_str == "element" ) - { - getline(liness, tmp_str, _separator); - getline(liness, n); - if(tmp_str == "vertex") num_vertex = StringToInt(n); - if(tmp_str == "face") num_triangles = StringToInt(n); - } - if(tmp_str == "end_header") end_header = true; - } - - // read file content - else if(end_header) - { - // read vertex and add into 'list_vertex' - if(!end_vertex && count < num_vertex) - { - string x, y, z; - getline(liness, x, _separator); - getline(liness, y, _separator); - getline(liness, z); - - cv::Point3f tmp_p; - tmp_p.x = atof(x.c_str()); - tmp_p.y = atof(y.c_str()); - tmp_p.z = atof(z.c_str()); - list_vertex.push_back(tmp_p); - - count++; - if(count == num_vertex) - { - count = 0; - end_vertex = !end_vertex; - } - } - // read faces and add into 'list_triangles' - else if(end_vertex && count < num_triangles) - { - string num_pts_per_face, id0, id1, id2; - getline(liness, num_pts_per_face, _separator); - getline(liness, id0, _separator); - getline(liness, id1, _separator); - getline(liness, id2); - - std::vector<int> tmp_triangle(3); - tmp_triangle[0] = StringToInt(id0); - tmp_triangle[1] = StringToInt(id1); - tmp_triangle[2] = StringToInt(id2); - list_triangles.push_back(tmp_triangle); - - count++; - } - } - } -} diff --git a/detectors/simple_ransac_detection/CsvReader.h b/detectors/simple_ransac_detection/CsvReader.h deleted file mode 100644 index 6f7774b6..00000000 --- a/detectors/simple_ransac_detection/CsvReader.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef CSVREADER_H -#define CSVREADER_H - -#include <iostream> -#include <fstream> -#include <opencv2/core/core.hpp> -#include "Utils.h" - -using namespace std; -using namespace cv; - -class CsvReader { -public: - /** - * The default constructor of the CSV reader Class. - * The default separator is ' ' (empty space) - * - * @param path - The path of the file to read - * @param separator - The separator character between words per line - * @return - */ - CsvReader(const string &path, const char &separator = ' '); - - /** - * Read a plane text file with .ply format - * - * @param list_vertex - The container of the vertices list of the mesh - * @param list_triangles - The container of the triangles list of the mesh - * @return - */ - void readPLY(vector<Point3f> &list_vertex, vector<vector<int> > &list_triangles); - -private: - /** The current stream file for the reader */ - ifstream _file; - /** The separator character between words for each line */ - char _separator; -}; - -#endif diff --git a/detectors/simple_ransac_detection/CsvWriter.cpp b/detectors/simple_ransac_detection/CsvWriter.cpp deleted file mode 100644 index a64fb691..00000000 --- a/detectors/simple_ransac_detection/CsvWriter.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "CsvWriter.h" - -CsvWriter::CsvWriter(const string &path, const string &separator){ - _file.open(path.c_str(), ofstream::out); - _isFirstTerm = true; - _separator = separator; -} - -CsvWriter::~CsvWriter() { - _file.flush(); - _file.close(); -} - -void CsvWriter::writeXYZ(const vector<Point3f> &list_points3d) -{ - string x, y, z; - for(unsigned int i = 0; i < list_points3d.size(); ++i) - { - x = FloatToString(list_points3d[i].x); - y = FloatToString(list_points3d[i].y); - z = FloatToString(list_points3d[i].z); - - _file << x << _separator << y << _separator << z << std::endl; - } - -} - -void CsvWriter::writeUVXYZ(const vector<Point3f> &list_points3d, const vector<Point2f> &list_points2d, const Mat &descriptors) -{ - string u, v, x, y, z, descriptor_str; - for(unsigned int i = 0; i < list_points3d.size(); ++i) - { - u = FloatToString(list_points2d[i].x); - v = FloatToString(list_points2d[i].y); - x = FloatToString(list_points3d[i].x); - y = FloatToString(list_points3d[i].y); - z = FloatToString(list_points3d[i].z); - - _file << u << _separator << v << _separator << x << _separator << y << _separator << z; - - for(int j = 0; j < 32; ++j) - { - descriptor_str = FloatToString(descriptors.at<float>(i,j)); - _file << _separator << descriptor_str; - } - _file << std::endl; - } -} diff --git a/detectors/simple_ransac_detection/CsvWriter.h b/detectors/simple_ransac_detection/CsvWriter.h deleted file mode 100644 index d8132f5b..00000000 --- a/detectors/simple_ransac_detection/CsvWriter.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef CSVWRITER_H -#define CSVWRITER_H - -#include <iostream> -#include <fstream> -#include <opencv2/core/core.hpp> -#include "Utils.h" - -using namespace std; -using namespace cv; - -class CsvWriter { -public: - CsvWriter(const string &path, const string &separator = " "); - ~CsvWriter(); - void writeXYZ(const vector<Point3f> &list_points3d); - void writeUVXYZ(const vector<Point3f> &list_points3d, const vector<Point2f> &list_points2d, const Mat &descriptors); - -private: - ofstream _file; - string _separator; - bool _isFirstTerm; -}; - -#endif diff --git a/detectors/simple_ransac_detection/Mesh.cpp b/detectors/simple_ransac_detection/Mesh.cpp deleted file mode 100644 index e6c84c47..00000000 --- a/detectors/simple_ransac_detection/Mesh.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Mesh.cpp - * - * Created on: Apr 9, 2014 - * Author: edgar - */ - -#include "Mesh.h" -#include "CsvReader.h" - - -// --------------------------------------------------- // -// TRIANGLE CLASS // -// --------------------------------------------------- // - -/** The custom constructor of the Triangle Class */ -Triangle::Triangle(int id, cv::Point3f V0, cv::Point3f V1, cv::Point3f V2) -{ - id_ = id; v0_ = V0; v1_ = V1; v2_ = V2; -} - -/** The default destructor of the Class */ -Triangle::~Triangle() -{ - // TODO Auto-generated destructor stub -} - - -// --------------------------------------------------- // -// RAY CLASS // -// --------------------------------------------------- // - -/** The custom constructor of the Ray Class */ -Ray::Ray(cv::Point3f P0, cv::Point3f P1) { - p0_ = P0; p1_ = P1; -} - -/** The default destructor of the Class */ -Ray::~Ray() -{ - // TODO Auto-generated destructor stub -} - - -// --------------------------------------------------- // -// OBJECT MESH CLASS // -// --------------------------------------------------- // - -/** The default constructor of the ObjectMesh Class */ -Mesh::Mesh() : list_vertex_(0) , list_triangles_(0) -{ - id_ = 0; - num_vertexs_ = 0; - num_triangles_ = 0; -} - -/** The default destructor of the ObjectMesh Class */ -Mesh::~Mesh() -{ - // TODO Auto-generated destructor stub -} - - -/** Load a CSV with *.ply format **/ -void Mesh::load(const std::string path) -{ - - // Create the reader - CsvReader csvReader(path); - - // Clear previous data - list_vertex_.clear(); - list_triangles_.clear(); - - // Read from .ply file - csvReader.readPLY(list_vertex_, list_triangles_); - - // Update mesh attributes - num_vertexs_ = (int)list_vertex_.size(); - num_triangles_ = (int)list_triangles_.size(); - -} diff --git a/detectors/simple_ransac_detection/Mesh.h b/detectors/simple_ransac_detection/Mesh.h deleted file mode 100644 index d82c8d48..00000000 --- a/detectors/simple_ransac_detection/Mesh.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Mesh.h - * - * Created on: Apr 9, 2014 - * Author: edgar - */ - -#ifndef MESH_H_ -#define MESH_H_ - -#include <iostream> -#include <opencv2/core/core.hpp> - - -// --------------------------------------------------- // -// TRIANGLE CLASS // -// --------------------------------------------------- // - -class Triangle { -public: - - explicit Triangle(int id, cv::Point3f V0, cv::Point3f V1, cv::Point3f V2); - virtual ~Triangle(); - - cv::Point3f getV0() const { return v0_; } - cv::Point3f getV1() const { return v1_; } - cv::Point3f getV2() const { return v2_; } - -private: - /** The identifier number of the triangle */ - int id_; - /** The three vertices that defines the triangle */ - cv::Point3f v0_, v1_, v2_; -}; - - -// --------------------------------------------------- // -// RAY CLASS // -// --------------------------------------------------- // - -class Ray { -public: - - explicit Ray(cv::Point3f P0, cv::Point3f P1); - virtual ~Ray(); - - cv::Point3f getP0() { return p0_; } - cv::Point3f getP1() { return p1_; } - -private: - /** The two points that defines the ray */ - cv::Point3f p0_, p1_; -}; - - -// --------------------------------------------------- // -// OBJECT MESH CLASS // -// --------------------------------------------------- // - -class Mesh -{ -public: - - Mesh(); - virtual ~Mesh(); - - std::vector<std::vector<int> > getTrianglesList() const { return list_triangles_; } - std::vector<cv::Point3f> getVertices() const { return list_vertex_; } - cv::Point3f getVertex(int pos) const { return list_vertex_[pos]; } - int getNumVertices() const { return num_vertexs_; } - - void load(const std::string path_file); - -private: - /** The identification number of the mesh */ - int id_; - /** The current number of vertices in the mesh */ - int num_vertexs_; - /** The current number of triangles in the mesh */ - int num_triangles_; - /* The list of triangles of the mesh */ - std::vector<cv::Point3f> list_vertex_; - /* The list of triangles of the mesh */ - std::vector<std::vector<int> > list_triangles_; -}; - -#endif /* OBJECTMESH_H_ */ diff --git a/detectors/simple_ransac_detection/Model.cpp b/detectors/simple_ransac_detection/Model.cpp deleted file mode 100644 index 660199f2..00000000 --- a/detectors/simple_ransac_detection/Model.cpp +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Model.cpp - * - * Created on: Apr 9, 2014 - * Author: edgar - */ - -#include <fstream> -#include <iostream> -#include <pugixml.hpp> -#include "Model.h" -#include "CsvWriter.h" -#include <sstream> -#include <ostream> -#include <boost/algorithm/string.hpp> - -Model::Model() : list_points2d_in_(0), list_points2d_out_(0), list_points3d_in_(0) -{ - n_correspondences_ = 0; -} - -Model::~Model() -{ - // TODO Auto-generated destructor stub -} - -void Model::add_correspondence(const cv::Point2f &point2d, const cv::Point3f &point3d) -{ - list_points2d_in_.push_back(point2d); - list_points3d_in_.push_back(point3d); - n_correspondences_++; -} - -void Model::add_outlier(const cv::Point2f &point2d) -{ - list_points2d_out_.push_back(point2d); -} - -void Model::add_descriptor(const cv::Mat &descriptor) -{ - descriptors_.push_back(descriptor); -} - -void Model::add_keypoint(const cv::KeyPoint &kp) -{ - list_keypoints_.push_back(kp); -} - - -/** Save a CSV file and fill the object mesh */ -void Model::save(const std::string path) -{ - cv::Mat points3dmatrix = cv::Mat(list_points3d_in_); - cv::Mat points2dmatrix = cv::Mat(list_points2d_in_); - //cv::Mat keyPointmatrix = cv::Mat(list_keypoints_); - - cv::FileStorage storage(path, cv::FileStorage::WRITE); - storage << "points_3d" << points3dmatrix; - storage << "points_2d" << points2dmatrix; - storage << "keypoints" << list_keypoints_; - storage << "descriptors" << descriptors_; - - storage.release(); -} - -/** Load a YAML file using OpenCv functions **/ -void Model::load(const std::string path) -{ - cv::Mat points3d_mat; - - cv::FileStorage storage(path, cv::FileStorage::READ); - storage["points_3d"] >> points3d_mat; - storage["descriptors"] >> descriptors_; - - points3d_mat.copyTo(list_points3d_in_); - - std::cout << list_points3d_in_.size() << endl; - std::cout << descriptors_; - storage.release(); - id = path; -} - -void Model::load_new_desc(const std::string path) -{ - f_type = "SIFT"; - - std::fstream infile(path.c_str()); - if (!infile.is_open()) - { - std::cout << "ERRROR opening model file!!"; - return ; - - } - int nop, desc_size, point_size; - infile >> nop; - infile >> point_size >> desc_size; - - descriptors_ = Mat(nop, desc_size, CV_32FC1); - - - for (int i = 0; i < nop; i++) - { - - float x, y, z; - infile >> x >> y >> z; - cv::Point3f p3d(x, y, z); - list_points3d_in_.push_back(p3d); - - cv::KeyPoint kp; - infile >> kp.pt.x >> kp.pt.y >> kp.octave >> kp.angle >> kp.response >> kp.size; - list_keypoints_.push_back(kp); - - for (int j = 0; j < desc_size; j++) - { - infile >> descriptors_.at<float>(i, j); - } - } - id = path; -} - -template <typename T> -string ps ( T thing ) -{ - cout << thing; - ostringstream ss; - ss << thing; - return ss.str(); -} - -void Model::load_new_xml(const std::string path) -{ - - pugi::xml_document doc; - pugi::xml_parse_result result = doc.load_file(path.c_str()); - - if (!result) - { - std::cout << "ERRROR opening model file!!"; - return ; - - } - pugi::xml_node pts_node = doc.child("Model").child("Points"); - - if (pts_node.first_child()) - f_type = pts_node.first_child().attribute("desc_type").as_string(); - else return; - - - for (pugi::xml_node pt_node = pts_node.first_child(); pt_node; pt_node = pt_node.next_sibling()) - { - Mat single_desc = Mat::zeros(1, get_descriptor_size(), CV_32FC1); - stringstream ss; - ss.str(pt_node.attribute("p3d").as_string()); - float x, y, z; - ss >> x >> y >> z; ss.clear(); - cv::Point3f p3d(x, y, z); - list_points3d_in_.push_back(p3d); - - ss.str(pt_node.attribute("p2d_prop").as_string()); - cv::KeyPoint kp; - ss >> kp.pt.x >> kp.pt.y >> kp.octave >> kp.angle >> kp.response >> kp.size; ss.clear(); - list_keypoints_.push_back(kp); - - ss.str(pt_node.attribute("desc").as_string()); - float d; - for(int it = 0; it < get_descriptor_size(); it ++) - { - ss >> d; - //cout <<it << " " << ss.eof() << " " << d << endl; - single_desc.at<float> (0, it) = d; - } - ss.clear(); - - descriptors_.push_back(single_desc); - } - - id = path; - //std::cout << list_points3d_in_.size() << endl; - //std::cout << descriptors_; -} - diff --git a/detectors/simple_ransac_detection/Model.h b/detectors/simple_ransac_detection/Model.h deleted file mode 100644 index ef029cd4..00000000 --- a/detectors/simple_ransac_detection/Model.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Model.h - * - * Created on: Apr 9, 2014 - * Author: edgar - */ - -#ifndef MODEL_H_ -#define MODEL_H_ - -#include <iostream> -#include <fstream> -#include <opencv2/core/core.hpp> -#include <opencv2/features2d/features2d.hpp> - -class Model -{ -public: - Model(); - virtual ~Model(); - - std::vector<cv::Point2f> get_points2d_in() const { return list_points2d_in_; } - std::vector<cv::Point2f> get_points2d_out() const { return list_points2d_out_; } - std::vector<cv::Point3f> get_points3d() const { return list_points3d_in_; } - std::vector<cv::KeyPoint> get_keypoints() const { return list_keypoints_; } - cv::Mat get_descriptors() const { return descriptors_; } - int get_numDescriptors() const { return descriptors_.rows; } - - - void add_correspondence(const cv::Point2f &point2d, const cv::Point3f &point3d); - void add_outlier(const cv::Point2f &point2d); - void add_descriptor(const cv::Mat &descriptor); - void add_keypoint(const cv::KeyPoint &kp); - - - void save(const std::string path); - void load(const std::string path); - void load_new_desc(const std::string path); - void load_new_xml(const std::string path); - - - std::string f_type; - std::string id; - -private: - - /** The current number of correspondecnes */ - int n_correspondences_; - /** The list of 2D points on the model surface */ - std::vector<cv::KeyPoint> list_keypoints_; - /** The list of 2D points on the model surface */ - std::vector<cv::Point2f> list_points2d_in_; - /** The list of 2D points outside the model surface */ - std::vector<cv::Point2f> list_points2d_out_; - /** The list of 3D points on the model surface */ - std::vector<cv::Point3f> list_points3d_in_; - /** The list of 2D points descriptors */ - cv::Mat descriptors_; - - int get_descriptor_size() - { - if (f_type == "SIFT") return 128; - return 128; - } -}; - -#endif /* OBJECTMODEL_H_ */ diff --git a/detectors/simple_ransac_detection/ModelRegistration.cpp b/detectors/simple_ransac_detection/ModelRegistration.cpp deleted file mode 100644 index e8da8856..00000000 --- a/detectors/simple_ransac_detection/ModelRegistration.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * ModelRegistration.cpp - * - * Created on: Apr 18, 2014 - * Author: edgar - */ - -#include "ModelRegistration.h" - -ModelRegistration::ModelRegistration() -{ - n_registrations_ = 0; - max_registrations_ = 0; -} - -ModelRegistration::~ModelRegistration() -{ - // TODO Auto-generated destructor stub -} - -void ModelRegistration::registerPoint(const cv::Point2f &point2d, const cv::Point3f &point3d) - { - // add correspondence at the end of the vector - list_points2d_.push_back(point2d); - list_points3d_.push_back(point3d); - n_registrations_++; - } - -void ModelRegistration::reset() -{ - n_registrations_ = 0; - max_registrations_ = 0; - list_points2d_.clear(); - list_points3d_.clear(); -} diff --git a/detectors/simple_ransac_detection/ModelRegistration.h b/detectors/simple_ransac_detection/ModelRegistration.h deleted file mode 100644 index f1e7aca4..00000000 --- a/detectors/simple_ransac_detection/ModelRegistration.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * ModelRegistration.h - * - * Created on: Apr 18, 2014 - * Author: edgar - */ - -#ifndef MODELREGISTRATION_H_ -#define MODELREGISTRATION_H_ - -#include <iostream> -#include <opencv2/core/core.hpp> - -class ModelRegistration -{ -public: - - ModelRegistration(); - virtual ~ModelRegistration(); - - void setNumMax(int n) { max_registrations_ = n; } - - std::vector<cv::Point2f> get_points2d() const { return list_points2d_; } - std::vector<cv::Point3f> get_points3d() const { return list_points3d_; } - int getNumMax() const { return max_registrations_; } - int getNumRegist() const { return n_registrations_; } - - bool is_registrable() const { return (n_registrations_ < max_registrations_); } - void registerPoint(const cv::Point2f &point2d, const cv::Point3f &point3d); - void reset(); - -private: -/** The current number of registered points */ -int n_registrations_; -/** The total number of points to register */ -int max_registrations_; -/** The list of 2D points to register the model */ -std::vector<cv::Point2f> list_points2d_; -/** The list of 3D points to register the model */ -std::vector<cv::Point3f> list_points3d_; -}; - -#endif /* MODELREGISTRATION_H_ */ diff --git a/detectors/simple_ransac_detection/PnPProblem.cpp b/detectors/simple_ransac_detection/PnPProblem.cpp deleted file mode 100644 index 3b361fc7..00000000 --- a/detectors/simple_ransac_detection/PnPProblem.cpp +++ /dev/null @@ -1,336 +0,0 @@ -/* - * PnPProblem.cpp - * - * Created on: Mar 28, 2014 - * Author: Edgar Riba - */ - -#include <iostream> -#include <sstream> - -#include "PnPProblem.h" -#include "Mesh.h" - -#include <opencv2/calib3d/calib3d.hpp> - -/* Functions headers */ -cv::Point3f CROSS(cv::Point3f v1, cv::Point3f v2); -double DOT(cv::Point3f v1, cv::Point3f v2); -cv::Point3f SUB(cv::Point3f v1, cv::Point3f v2); -cv::Point3f get_nearest_3D_point(std::vector<cv::Point3f> &points_list, cv::Point3f origin); - - -/* Functions for Möller–Trumbore intersection algorithm */ - -cv::Point3f CROSS(cv::Point3f v1, cv::Point3f v2) -{ - cv::Point3f tmp_p; - tmp_p.x = v1.y*v2.z - v1.z*v2.y; - tmp_p.y = v1.z*v2.x - v1.x*v2.z; - tmp_p.z = v1.x*v2.y - v1.y*v2.x; - return tmp_p; -} - -double DOT(cv::Point3f v1, cv::Point3f v2) -{ - return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z; -} - -cv::Point3f SUB(cv::Point3f v1, cv::Point3f v2) -{ - cv::Point3f tmp_p; - tmp_p.x = v1.x - v2.x; - tmp_p.y = v1.y - v2.y; - tmp_p.z = v1.z - v2.z; - return tmp_p; -} - -/* End functions for Möller–Trumbore intersection algorithm - * */ - -// Function to get the nearest 3D point to the Ray origin -cv::Point3f get_nearest_3D_point(std::vector<cv::Point3f> &points_list, cv::Point3f origin) -{ - cv::Point3f p1 = points_list[0]; - cv::Point3f p2 = points_list[1]; - - double d1 = std::sqrt( std::pow(p1.x-origin.x, 2) + std::pow(p1.y-origin.y, 2) + std::pow(p1.z-origin.z, 2) ); - double d2 = std::sqrt( std::pow(p2.x-origin.x, 2) + std::pow(p2.y-origin.y, 2) + std::pow(p2.z-origin.z, 2) ); - - if(d1 < d2) - { - return p1; - } - else - { - return p2; - } -} - -// Custom constructor given the intrinsic camera parameters - -PnPProblem::PnPProblem(double const params[], double const dists[]) -{ - _A_matrix = cv::Mat::zeros(3, 3, CV_64FC1); // intrinsic camera parameters - _A_matrix.at<double>(0, 0) = params[0]; // [ fx 0 cx ] - _A_matrix.at<double>(1, 1) = params[1]; // [ 0 fy cy ] - _A_matrix.at<double>(0, 2) = params[2]; // [ 0 0 1 ] - _A_matrix.at<double>(1, 2) = params[3]; - _A_matrix.at<double>(2, 2) = 1; - _dist = cv::Mat::zeros(4, 1, CV_64FC1); - _dist.at<double>(0,0) = dists[0]; _dist.at<double>(1,0) = dists[1]; _dist.at<double>(2,0) = dists[2]; _dist.at<double>(3,0) = dists[3]; - - _R_matrix = cv::Mat::zeros(3, 3, CV_64FC1); // rotation matrix - _t_matrix = cv::Mat::zeros(3, 1, CV_64FC1); // translation matrix - _P_matrix = cv::Mat::zeros(3, 4, CV_64FC1); // rotation-translation matrix - -} - -PnPProblem::PnPProblem(cv::Mat intrin, cv::Mat dist) { - intrin.copyTo(_A_matrix); - dist.copyTo(_dist); - - _R_matrix = cv::Mat::zeros(3, 3, CV_64FC1); // rotation matrix - _t_matrix = cv::Mat::zeros(3, 1, CV_64FC1); // translation matrix - _P_matrix = cv::Mat::zeros(3, 4, CV_64FC1); // rotation-translation matrix -} - - -PnPProblem::~PnPProblem() -{ - // TODO Auto-generated destructor stub -} - -void PnPProblem::set_P_matrix( const cv::Mat &R_matrix, const cv::Mat &t_matrix) -{ - // Rotation-Translation Matrix Definition - _P_matrix.at<double>(0,0) = R_matrix.at<double>(0,0); - _P_matrix.at<double>(0,1) = R_matrix.at<double>(0,1); - _P_matrix.at<double>(0,2) = R_matrix.at<double>(0,2); - _P_matrix.at<double>(1,0) = R_matrix.at<double>(1,0); - _P_matrix.at<double>(1,1) = R_matrix.at<double>(1,1); - _P_matrix.at<double>(1,2) = R_matrix.at<double>(1,2); - _P_matrix.at<double>(2,0) = R_matrix.at<double>(2,0); - _P_matrix.at<double>(2,1) = R_matrix.at<double>(2,1); - _P_matrix.at<double>(2,2) = R_matrix.at<double>(2,2); - - _P_matrix.at<double>(0,3) = t_matrix.at<double>(0); - _P_matrix.at<double>(1,3) = t_matrix.at<double>(1); - _P_matrix.at<double>(2,3) = t_matrix.at<double>(2); - -} - - -// Estimate the pose given a list of 2D/3D correspondences and the method to use -bool PnPProblem::estimatePose( const std::vector<cv::Point3f> &list_points3d, - const std::vector<cv::Point2f> &list_points2d, - int flags) -{ - //cv::Mat distCoeffs = cv::Mat::zeros(4, 1, CV_64FC1); - cv::Mat rvec = cv::Mat::zeros(3, 1, CV_64FC1); - cv::Mat tvec = cv::Mat::zeros(3, 1, CV_64FC1); - - bool useExtrinsicGuess = false; - - // Pose estimation - bool correspondence = cv::solvePnP( list_points3d, list_points2d, _A_matrix, _dist, rvec, tvec, - useExtrinsicGuess, flags); - - // Transforms Rotation Vector to Matrix - Rodrigues(rvec,_R_matrix); - _t_matrix = tvec; - - // Set projection matrix - this->set_P_matrix(_R_matrix, _t_matrix); - - return correspondence; -} - -// Estimate the pose given a list of 2D/3D correspondences with RANSAC and the method to use - -void PnPProblem::estimatePoseRANSAC( const std::vector<cv::Point3f> &list_points3d, // list with model 3D coordinates - const std::vector<cv::Point2f> &list_points2d, // list with scene 2D coordinates - int flags, cv::Mat &inliers, int iterationsCount, // PnP method; inliers container - float reprojectionError, double confidence ) // Ransac parameters -{ - //cv::Mat distCoeffs = cv::Mat::zeros(4, 1, CV_64FC1); // vector of distortion coefficients - cv::Mat rvec = cv::Mat::zeros(3, 1, CV_64FC1); // output rotation vector - cv::Mat tvec = cv::Mat::zeros(3, 1, CV_64FC1); // output translation vector - - bool useExtrinsicGuess = false; // if true the function uses the provided rvec and tvec values as - // initial approximations of the rotation and translation vectors - - cv::solvePnPRansac( list_points3d, list_points2d, _A_matrix, _dist, rvec, tvec, - useExtrinsicGuess, iterationsCount, reprojectionError, confidence, - inliers, flags ); - - rvec.copyTo(_R_vect); - Rodrigues(rvec,_R_matrix); // converts Rotation Vector to Matrix - _t_matrix = tvec; // set translation matrix - - this->set_P_matrix(_R_matrix, _t_matrix); // set rotation-translation matrix - -} - -// Given the mesh, backproject the 3D points to 2D to verify the pose estimation -std::vector<cv::Point2f> PnPProblem::verify_points(Mesh *mesh) -{ - std::vector<cv::Point2f> verified_points_2d; - for( int i = 0; i < mesh->getNumVertices(); i++) - { - cv::Point3f point3d = mesh->getVertex(i); - cv::Point2f point2d = this->backproject3DPoint(point3d); - verified_points_2d.push_back(point2d); - } - - return verified_points_2d; -} - - -// Backproject a 3D point to 2D using the estimated pose parameters - -cv::Point2f PnPProblem::backproject3DPoint(const cv::Point3f &point3d) -{ - // 3D point vector [x y z 1]' - cv::Mat point3d_vec = cv::Mat(4, 1, CV_64FC1); - point3d_vec.at<double>(0) = point3d.x; - point3d_vec.at<double>(1) = point3d.y; - point3d_vec.at<double>(2) = point3d.z; - point3d_vec.at<double>(3) = 1; - - // 2D point vector [u v 1]' - cv::Mat point2d_vec = cv::Mat(4, 1, CV_64FC1); - point2d_vec = _A_matrix * _P_matrix * point3d_vec; - - // Normalization of [u v]' - cv::Point2f point2d; - point2d.x = (float)(point2d_vec.at<double>(0) / point2d_vec.at<double>(2)); - point2d.y = (float)(point2d_vec.at<double>(1) / point2d_vec.at<double>(2)); - - - return point2d; -} - -// Back project a 2D point to 3D and returns if it's on the object surface -bool PnPProblem::backproject2DPoint(const Mesh *mesh, const cv::Point2f &point2d, cv::Point3f &point3d) -{ - // Triangles list of the object mesh - std::vector<std::vector<int> > triangles_list = mesh->getTrianglesList(); - - double lambda = 8; - double u = point2d.x; - double v = point2d.y; - - // Point in vector form - cv::Mat point2d_vec = cv::Mat::ones(3, 1, CV_64F); // 3x1 - point2d_vec.at<double>(0) = u * lambda; - point2d_vec.at<double>(1) = v * lambda; - point2d_vec.at<double>(2) = lambda; - - // Point in camera coordinates - cv::Mat X_c = _A_matrix.inv() * point2d_vec ; // 3x1 - - // Point in world coordinates - cv::Mat X_w = _R_matrix.inv() * ( X_c - _t_matrix ); // 3x1 - - // Center of projection - cv::Mat C_op = cv::Mat(_R_matrix.inv()).mul(-1) * _t_matrix; // 3x1 - - // Ray direction vector - cv::Mat ray = X_w - C_op; // 3x1 - ray = ray / cv::norm(ray); // 3x1 - - // Set up Ray - Ray R((cv::Point3f)C_op, (cv::Point3f)ray); - - // A vector to store the intersections found - std::vector<cv::Point3f> intersections_list; - - // Loop for all the triangles and check the intersection - for (unsigned int i = 0; i < triangles_list.size(); i++) - { - cv::Point3f V0 = mesh->getVertex(triangles_list[i][0]); - cv::Point3f V1 = mesh->getVertex(triangles_list[i][1]); - cv::Point3f V2 = mesh->getVertex(triangles_list[i][2]); - - Triangle T(i, V0, V1, V2); - - double out; - if(this->intersect_MollerTrumbore(R, T, &out)) - { - cv::Point3f tmp_pt = R.getP0() + out*R.getP1(); // P = O + t*D - intersections_list.push_back(tmp_pt); - } - } - - // If there are intersection, find the nearest one - if (!intersections_list.empty()) - { - point3d = get_nearest_3D_point(intersections_list, R.getP0()); - return true; - } - else - { - return false; - } -} - -// Möller–Trumbore intersection algorithm -bool PnPProblem::intersect_MollerTrumbore(Ray &Ray, Triangle &Triangle, double *out) -{ - const double EPSILON = 0.000001; - - cv::Point3f e1, e2; - cv::Point3f P, Q, T; - double det, inv_det, u, v; - double t; - - cv::Point3f V1 = Triangle.getV0(); // Triangle vertices - cv::Point3f V2 = Triangle.getV1(); - cv::Point3f V3 = Triangle.getV2(); - - cv::Point3f O = Ray.getP0(); // Ray origin - cv::Point3f D = Ray.getP1(); // Ray direction - - //Find vectors for two edges sharing V1 - e1 = SUB(V2, V1); - e2 = SUB(V3, V1); - - // Begin calculation determinant - also used to calculate U parameter - P = CROSS(D, e2); - - // If determinant is near zero, ray lie in plane of triangle - det = DOT(e1, P); - - //NOT CULLING - if(det > -EPSILON && det < EPSILON) return false; - inv_det = 1.f / det; - - //calculate distance from V1 to ray origin - T = SUB(O, V1); - - //Calculate u parameter and test bound - u = DOT(T, P) * inv_det; - - //The intersection lies outside of the triangle - if(u < 0.f || u > 1.f) return false; - - //Prepare to test v parameter - Q = CROSS(T, e1); - - //Calculate V parameter and test bound - v = DOT(D, Q) * inv_det; - - //The intersection lies outside of the triangle - if(v < 0.f || u + v > 1.f) return false; - - t = DOT(e2, Q) * inv_det; - - if(t > EPSILON) { //ray intersection - *out = t; - return true; - } - - // No hit, no win - return false; -} diff --git a/detectors/simple_ransac_detection/PnPProblem.h b/detectors/simple_ransac_detection/PnPProblem.h deleted file mode 100644 index d8de5413..00000000 --- a/detectors/simple_ransac_detection/PnPProblem.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * PnPProblem.h - * - * Created on: Mar 28, 2014 - * Author: Edgar Riba - */ - -#ifndef PNPPROBLEM_H_ -#define PNPPROBLEM_H_ - -#include <iostream> - -#include <opencv2/core/core.hpp> -#include <opencv2/highgui/highgui.hpp> - -#include "Mesh.h" -#include "ModelRegistration.h" - -class PnPProblem -{ - -public: - explicit PnPProblem(double const param[], double const dists[]); // custom constructor - PnPProblem(cv::Mat intrin = cv::Mat::eye(3, 4, CV_64F), cv::Mat dist = cv::Mat::zeros(1, 5, CV_64F)); - virtual ~PnPProblem(); - - bool backproject2DPoint(const Mesh *mesh, const cv::Point2f &point2d, cv::Point3f &point3d); - bool intersect_MollerTrumbore(Ray &R, Triangle &T, double *out); - std::vector<cv::Point2f> verify_points(Mesh *mesh); - cv::Point2f backproject3DPoint(const cv::Point3f &point3d); - bool estimatePose(const std::vector<cv::Point3f> &list_points3d, const std::vector<cv::Point2f> &list_points2d, int flags); - void estimatePoseRANSAC( const std::vector<cv::Point3f> &list_points3d, const std::vector<cv::Point2f> &list_points2d, - int flags, cv::Mat &inliers, - int iterationsCount, float reprojectionError, double confidence ); - - cv::Mat get_A_matrix() const { return _A_matrix; } - cv::Mat get_dist_coef() const { return _dist; } - cv::Mat get_R_matrix() const { return _R_matrix; } - cv::Mat get_R_vect() const { return _R_vect; } - cv::Mat get_t_matrix() const { return _t_matrix; } - cv::Mat get_P_matrix() const { return _P_matrix; } - - void set_P_matrix( const cv::Mat &R_matrix, const cv::Mat &t_matrix); - - void clearExtrinsics() - { - _R_matrix = cv::Mat::zeros(3, 3, CV_64FC1); // rotation matrix - _t_matrix = cv::Mat::zeros(3, 1, CV_64FC1); // translation matrix - _P_matrix = cv::Mat::zeros(3, 4, CV_64FC1); - } - -private: - /** The calibration matrix */ - cv::Mat _A_matrix; - //dist mat - cv::Mat _dist; - /** The computed rotation matrix */ - cv::Mat _R_matrix; - cv::Mat _R_vect; - /** The computed translation matrix */ - cv::Mat _t_matrix; - /** The computed projection matrix */ - cv::Mat _P_matrix; -}; - -// Functions for Möller–Trumbore intersection algorithm -cv::Point3f CROSS(cv::Point3f v1, cv::Point3f v2); -double DOT(cv::Point3f v1, cv::Point3f v2); -cv::Point3f SUB(cv::Point3f v1, cv::Point3f v2); - -#endif /* PNPPROBLEM_H_ */ diff --git a/detectors/simple_ransac_detection/RobustMatcher.cpp b/detectors/simple_ransac_detection/RobustMatcher.cpp deleted file mode 100644 index 49844cea..00000000 --- a/detectors/simple_ransac_detection/RobustMatcher.cpp +++ /dev/null @@ -1,328 +0,0 @@ -/* - * RobustMatcher.cpp - * - * Created on: Jun 4, 2014 - * Author: eriba - */ - -#include "RobustMatcher.h" -#include "Utils.h" -#include "Model.h" -#include <time.h> - -#include <opencv2/features2d/features2d.hpp> -#include <opencv2/features2d.hpp> -#include <opencv2/xfeatures2d.hpp> -#include <opencv2/ml.hpp> - - -void convertToUnsignedSiftGPU(cv::Mat const &cv_des, vector<unsigned char> &siftgpu_des) -{ - int totdes = cv_des.rows * 128; - siftgpu_des.resize(cv_des.rows * 128); - - int desi = 0; - - for (int i = 0; i < cv_des.rows; i++){ - for (int j = 0; j < 128; j++, desi++) - siftgpu_des[desi] = (unsigned char)cv_des.at<float>(i,j); - } -} - -void convertToDmatch(int siftgpu_matches[][2], int num_match, vector<cv::DMatch> &cv_matches) -{ - cv_matches.resize(num_match); - for(int i = 0; i < num_match; ++i) - { - //How to get the feature matches: - //SiftGPU::SiftKeypoint & key1 = keys1[match_buf[i][0]]; - //SiftGPU::SiftKeypoint & key2 = keys2[match_buf[i][1]]; - //key1 in the first image matches with key2 in the second image - cv_matches[i].trainIdx = siftgpu_matches[i][0]; //Assigned first at init - cv_matches[i].queryIdx = siftgpu_matches[i][1]; - } -} - - -void RobustMatcher::instantiateMatcher(Model const &model, bool use_gpu) -{ - - if (use_gpu_ == true) { - - //####GPU - - //1. for SIFT - matcher_gpu_ = cv::cuda::DescriptorMatcher::createBFMatcher(cv::NORM_L2); - cv::cuda::GpuMat train_descriptors(model.get_descriptors()); - matcher_gpu_->add(std::vector<cv::cuda::GpuMat>(1, train_descriptors)); - cout << "GPU matcher instantiated ..." << endl; - - } - else - { - matcher_ = cv::makePtr<cv::FlannBasedMatcher>(); - } -} - -RobustMatcher::RobustMatcher(Model const &model, bool use_gpu, bool use_gpu_match) -{ - - use_gpu_ = use_gpu_match; - - - - // ORB is the default feature - ratio_ =0.8f; - detector_ = cv::ORB::create(); - extractor_ = cv::ORB::create(); - - - - instantiateMatcher(model, use_gpu_match); - -// // BruteFroce matcher with Norm Hamming is the default matcher -// //matcher_ = cv::makePtr<cv::BFMatcher>((int)cv::NORM_HAMMING, false); -// //matcher_ = cv::makePtr<cv::BFMatcher>((int)cv::NORM_L2, false); -// -// -// //####################TEMPORARY CODE BELOW!!!!!!!!!!!!!!!!!!!!!!!! -// -// cv::Ptr<cv::flann::IndexParams> indexParams = cv::makePtr<cv::flann::LshIndexParams>(6, 12, 1); // instantiate LSH index parameters -// cv::Ptr<cv::flann::SearchParams> searchParams = cv::makePtr<cv::flann::SearchParams>(50); // instantiate flann search parameters -// // instantiate FlannBased matcher -// //Ptr<DescriptorMatcher> matcher = makePtr<FlannBasedMatcher>(indexParams, searchParams); -// //matcher_ = cv::makePtr<cv::FlannBasedMatcher>(indexParams, searchParams); -// matcher_ = cv::makePtr<cv::FlannBasedMatcher>(); - -} - -RobustMatcher::~RobustMatcher() -{ - // TODO Auto-generated destructor stub -} - -void RobustMatcher::computeKeyPoints( const cv::Mat& image, std::vector<cv::KeyPoint>& keypoints) -{ - detector_->detect(image, keypoints); -} - -void RobustMatcher::computeDescriptors( const cv::Mat& image, std::vector<cv::KeyPoint>& keypoints, cv::Mat& descriptors) -{ - extractor_->compute(image, keypoints, descriptors); -} - -int RobustMatcher::ratioTest(std::vector<std::vector<cv::DMatch> > &matches) -{ - int removed = 0; - // for all matches - for ( std::vector<std::vector<cv::DMatch> >::iterator - matchIterator= matches.begin(); matchIterator!= matches.end(); ++matchIterator) - { - // if 2 NN has been identified - if (matchIterator->size() > 1) - { - // check distance ratio - if ((*matchIterator)[0].distance / (*matchIterator)[1].distance > ratio_) - { - matchIterator->clear(); // remove match - removed++; - } - } - else - { // does not have 2 neighbours - matchIterator->clear(); // remove match - removed++; - } - } - return removed; -} - -void get_good_matches(const std::vector<std::vector<cv::DMatch> >& matches, std::vector<cv::DMatch>& goodMatches) -{ - for (std::vector<std::vector<cv::DMatch> >::const_iterator - matchIterator1 = matches.begin(); matchIterator1 != matches.end(); ++matchIterator1) - { - // ignore deleted matches - if (matchIterator1->empty() || matchIterator1->size() < 2) - continue; - if ((*matchIterator1)[0].distance < (*matchIterator1)[1].distance) - goodMatches.push_back((*matchIterator1)[0]); - } - -} - -void RobustMatcher::symmetryTest( const std::vector<std::vector<cv::DMatch> >& matches1, - const std::vector<std::vector<cv::DMatch> >& matches2, - std::vector<cv::DMatch>& symMatches ) -{ - - // for all matches image 1 -> image 2 - for (std::vector<std::vector<cv::DMatch> >::const_iterator - matchIterator1 = matches1.begin(); matchIterator1 != matches1.end(); ++matchIterator1) - { - - // ignore deleted matches - if (matchIterator1->empty() || matchIterator1->size() < 2) - continue; - - // for all matches image 2 -> image 1 - for (std::vector<std::vector<cv::DMatch> >::const_iterator - matchIterator2 = matches2.begin(); matchIterator2 != matches2.end(); ++matchIterator2) - { - // ignore deleted matches - if (matchIterator2->empty() || matchIterator2->size() < 2) - continue; - - // Match symmetry test - if ((*matchIterator1)[0].queryIdx == - (*matchIterator2)[0].trainIdx && - (*matchIterator2)[0].queryIdx == - (*matchIterator1)[0].trainIdx) - { - // add symmetrical match - symMatches.push_back( - cv::DMatch((*matchIterator1)[0].queryIdx, - (*matchIterator1)[0].trainIdx, - (*matchIterator1)[0].distance)); - break; // next match in image 1 -> image 2 - } - } - } - -} - -void RobustMatcher::match(const cv::Mat & descriptors_frame, const cv::Mat &descriptors_model, std::vector<cv::DMatch>& good_matches) -{ - std::vector<std::vector<cv::DMatch> > matches; - - if (use_gpu_) - { - matcher_gpu_->knnMatch(cv::cuda::GpuMat(descriptors_frame), matches, 2); // return 2 nearest neighbours - ratioTest(matches); - for ( std::vector<std::vector<cv::DMatch> >::iterator - matchIterator= matches.begin(); matchIterator!= matches.end(); ++matchIterator) - { - if (!matchIterator->empty()) good_matches.push_back((*matchIterator)[0]); - } - } - else - { - matcher_->knnMatch(descriptors_frame, descriptors_model, matches, 2); // return 2 nearest neighbours - ratioTest(matches); - // 4. Fill good matches container - for ( std::vector<std::vector<cv::DMatch> >::iterator - matchIterator= matches.begin(); matchIterator!= matches.end(); ++matchIterator) - { - if (!matchIterator->empty()) good_matches.push_back((*matchIterator)[0]); - } - - } -} - -void RobustMatcher::matchNormalized(cv::Mat & descriptors_frame, cv::Mat &descriptors_model, std::vector<cv::DMatch>& good_matches) -{ - od::normL2(descriptors_frame); od::normL2(descriptors_model); - match(descriptors_frame, descriptors_model, good_matches); -} - -void RobustMatcher::robustMatch( const cv::Mat& frame, std::vector<cv::DMatch>& good_matches, - std::vector<cv::KeyPoint>& keypoints_frame, const cv::Mat& descriptors_model ) -{ - // 1a. Detection of the ORB features - //this->computeKeyPoints(frame, keypoints_frame); - - // 1b. Extraction of the ORB descriptors - cv::Mat descriptors_frame; - //this->computeDescriptors(frame, keypoints_frame, descriptors_frame); - - featureDetector_->computeKeypointsAndDescriptors(frame, descriptors_frame, keypoints_frame); - //cout << "After Restacking: \n" << descriptors_frame << endl; - // 2. Match the two image descriptors - std::vector<std::vector<cv::DMatch> > matches12, matches21; - - // 2a. From image 1 to image 2 - matcher_->knnMatch(descriptors_frame, descriptors_model, matches12, 2); // return 2 nearest neighbours - - // 2b. From image 2 to image 1 - matcher_->knnMatch(descriptors_model, descriptors_frame, matches21, 2); // return 2 nearest neighbours - - - // 3. Remove matches for which NN ratio is > than threshold - // clean image 1 -> image 2 matches - ratioTest(matches12); - // clean image 2 -> image 1 matches - ratioTest(matches21); - - //get_good_matches(matches12, good_matches); - - // 4. Remove non-symmetrical matches - symmetryTest(matches12, matches21, good_matches); - - -} - -void RobustMatcher::findFeatureAndMatch( const cv::Mat& frame, std::vector<cv::DMatch>& good_matches, - std::vector<cv::KeyPoint>& keypoints_frame, - const cv::Mat& descriptors_model ) -{ - good_matches.clear(); - - // 1b. Extraction of the ORB descriptors - cv::Mat descriptors_frame; - featureDetector_->computeKeypointsAndDescriptors(frame, descriptors_frame, keypoints_frame); - - cout << "df " << descriptors_model.rows << endl; - cout << "kf" << descriptors_frame.rows << endl; - - match(descriptors_frame, descriptors_model, good_matches); - -} - - -void RobustMatcher::fastRobustMatch( const cv::Mat& frame, std::vector<cv::DMatch>& good_matches, - std::vector<cv::KeyPoint>& keypoints_frame, - const cv::Mat& descriptors_model ) -{ - good_matches.clear(); - - cv::Mat d_f, d_m; - // 1a. Detection of the ORB features - //this->computeKeyPoints(frame, keypoints_frame); - - // 1b. Extraction of the ORB descriptors - cv::Mat descriptors_frame; - //this->computeDescriptors(frame, keypoints_frame, descriptors_frame); - - featureDetector_->computeKeypointsAndDescriptors(frame, descriptors_frame, keypoints_frame); - - if (descriptors_frame.type() != CV_32F) { - descriptors_frame.convertTo(d_f, CV_32F); - } - else { - d_f = descriptors_frame; - } - if (descriptors_model.type() != CV_32F) { - descriptors_model.convertTo(d_f, CV_32F); - } - else - { - d_m = descriptors_model; - } - - // 2. Match the two image descriptors - std::vector<std::vector<cv::DMatch> > matches; - matcher_->knnMatch(d_f, d_m, matches, 2); - - // 3. Remove matches for which NN ratio is > than threshold - ratioTest(matches); - - // 4. Fill good matches container - for ( std::vector<std::vector<cv::DMatch> >::iterator - matchIterator= matches.begin(); matchIterator!= matches.end(); ++matchIterator) - { - if (!matchIterator->empty()) good_matches.push_back((*matchIterator)[0]); - } - -} - - diff --git a/detectors/simple_ransac_detection/RobustMatcher.h b/detectors/simple_ransac_detection/RobustMatcher.h deleted file mode 100644 index 3801e7d5..00000000 --- a/detectors/simple_ransac_detection/RobustMatcher.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * RobustMatcher.h - * - * Created on: Jun 4, 2014 - * Author: eriba - */ - -#ifndef ROBUSTMATCHER_H_ -#define ROBUSTMATCHER_H_ - -#include <iostream> - -#include <opencv2/core/core.hpp> -#include <opencv2/highgui/highgui.hpp> -#include <opencv2/features2d/features2d.hpp> -#include "od/common/utils/ODFeatureDetector2D.h" -#include "od/common/utils/utils.h" -#include "Model.h" - -using namespace std; - -class -RobustMatcher { -public: - RobustMatcher(Model const &model, bool use_gpu, bool b); - - - int mode_; - bool use_gpu_; - - virtual ~RobustMatcher(); - - // Set the feature detector - void setFeatureDetector(const cv::Ptr<cv::FeatureDetector>& detect) { detector_ = detect; } - - // Set the descriptor extractor - void setDescriptorExtractor(const cv::Ptr<cv::DescriptorExtractor>& desc) { extractor_ = desc; } - - // Set the matcher - void setDescriptorMatcher(const cv::Ptr<cv::DescriptorMatcher>& match) { matcher_ = match; } - - // Compute the keypoints of an image - void computeKeyPoints( const cv::Mat& image, std::vector<cv::KeyPoint>& keypoints); - - // Compute the descriptors of an image given its keypoints - void computeDescriptors( const cv::Mat& image, std::vector<cv::KeyPoint>& keypoints, cv::Mat& descriptors); - - // Set ratio parameter for the ratio test - void setRatio( float rat) { ratio_ = rat; } - - // Clear matches for which NN ratio is > than threshold - // return the number of removed points - // (corresponding entries being cleared, - // i.e. size will be 0) - int ratioTest(std::vector<std::vector<cv::DMatch> > &matches); - - // Insert symmetrical matches in symMatches vector - void symmetryTest( const std::vector<std::vector<cv::DMatch> >& matches1, - const std::vector<std::vector<cv::DMatch> >& matches2, - std::vector<cv::DMatch>& symMatches ); - - // Match feature points using ratio and symmetry test - void robustMatch( const cv::Mat& frame, std::vector<cv::DMatch>& good_matches, - std::vector<cv::KeyPoint>& keypoints_frame, - const cv::Mat& descriptors_model ); - - // Match feature points using ratio test - void fastRobustMatch( const cv::Mat& frame, std::vector<cv::DMatch>& good_matches, - std::vector<cv::KeyPoint>& keypoints_frame, - const cv::Mat& descriptors_model ); - - void findFeatureAndMatch(cv::Mat const &frame, vector<cv::DMatch> &good_matches, vector<cv::KeyPoint> &keypoints_frame, - cv::Mat const &descriptors_model); - - void match(const cv::Mat & descriptors_frame, const cv::Mat &descriptors_model, std::vector<cv::DMatch>& good_matches); - - void matchNormalized(cv::Mat &descriptors_frame, cv::Mat &descriptors_model, vector<cv::DMatch> &good_matches); - -private: - // pointer to the feature point detector object - cv::Ptr<od::ODFeatureDetector2D> featureDetector_; - - cv::Ptr<cv::FeatureDetector> detector_; - // pointer to the feature descriptor extractor object - cv::Ptr<cv::DescriptorExtractor> extractor_; - // pointer to the matcher object - - //The important feature of this class starts here - //DIFFERENT TYPES OF MATCHERS, add here in this list (based on the construction - cv::Ptr<cv::DescriptorMatcher> matcher_; - cv::Ptr<cv::cuda::DescriptorMatcher> matcher_gpu_; - // max ratio between 1st and 2nd NN - float ratio_; - - void instantiateMatcher(Model const &feature_type, bool use_gpu); - - - - - void instantiateMatcher1(Model const &model, bool use_gpu); - -}; - -#endif /* ROBUSTMATCHER_H_ */ diff --git a/detectors/simple_ransac_detection/Utils.cpp b/detectors/simple_ransac_detection/Utils.cpp deleted file mode 100644 index 3501eac1..00000000 --- a/detectors/simple_ransac_detection/Utils.cpp +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Utils.cpp - * - * Created on: Mar 28, 2014 - * Author: Edgar Riba - */ - -#include <iostream> - - - -#include "PnPProblem.h" -#include "ModelRegistration.h" -#include "Utils.h" -#include "Model.h" - -#include <opencv2/imgproc/imgproc.hpp> -#include <opencv2/calib3d/calib3d.hpp> - -// For text -int fontFace = cv::FONT_ITALIC; -double fontScale = 0.75; -int thickness_font = 2; - -// For circles -int lineType = 16; -int radius = 1; -double thickness_circ = -1; - - -using namespace std; - - - -void viewImage(cv::Mat image, vector<cv::KeyPoint> keypoints) -{ - cv::Mat outimage = image; - cv::drawKeypoints(image, keypoints, outimage, cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS); - - cv::namedWindow( "Keypoints", cv::WINDOW_NORMAL ); // Create a window for display. - cv::resizeWindow("Keypoints", 800, 600); - - cv::imshow("Keypoints", outimage); - cv::waitKey(0); -} - -// Draw a text with the question point -void drawQuestion(cv::Mat image, cv::Point3f point, cv::Scalar color) -{ - std::string x = IntToString((int)point.x); - std::string y = IntToString((int)point.y); - std::string z = IntToString((int)point.z); - - std::string text = " Where is point (" + x + "," + y + "," + z + ") ?"; - cv::putText(image, text, cv::Point(25,50), fontFace, fontScale, color, thickness_font, 8); -} - -// Draw a text with the number of entered points -void drawText(cv::Mat image, std::string text, cv::Scalar color) -{ - cv::putText(image, text, cv::Point(25,50), fontFace, fontScale, color, thickness_font, 8); -} - -// Draw a text with the number of entered points -void drawText2(cv::Mat image, std::string text, cv::Scalar color) -{ - cv::putText(image, text, cv::Point(25,75), fontFace, fontScale, color, thickness_font, 8); -} - -// Draw a text with the frame ratio -void drawFPS(cv::Mat image, double fps, cv::Scalar color) -{ - std::string fps_str = IntToString((int)fps); - std::string text = fps_str + " FPS"; - cv::putText(image, text, cv::Point(500,50), fontFace, fontScale, color, thickness_font, 8); -} - -// Draw a text with the frame ratio -void drawConfidence(cv::Mat image, double confidence, cv::Scalar color) -{ - std::string conf_str = IntToString((int)confidence); - std::string text = conf_str + " %"; - cv::putText(image, text, cv::Point(500,75), fontFace, fontScale, color, thickness_font, 8); -} - -// Draw a text with the number of entered points -void drawCounter(cv::Mat image, int n, int n_max, cv::Scalar color) -{ - std::string n_str = IntToString(n); - std::string n_max_str = IntToString(n_max); - std::string text = n_str + " of " + n_max_str + " points"; - cv::putText(image, text, cv::Point(500,50), fontFace, fontScale, color, thickness_font, 8); -} - -// Draw the points and the coordinates -void drawPoints(cv::Mat image, std::vector<cv::Point2f> &list_points_2d, std::vector<cv::Point3f> &list_points_3d, cv::Scalar color) -{ - for (unsigned int i = 0; i < list_points_2d.size(); ++i) - { - cv::Point2f point_2d = list_points_2d[i]; - cv::Point3f point_3d = list_points_3d[i]; - - // Draw Selected points - cv::circle(image, point_2d, radius, color, -1, lineType ); - - std::string idx = IntToString(i+1); - std::string x = IntToString((int)point_3d.x); - std::string y = IntToString((int)point_3d.y); - std::string z = IntToString((int)point_3d.z); - std::string text = "P" + idx + " (" + x + "," + y + "," + z +")"; - - point_2d.x = point_2d.x + 10; - point_2d.y = point_2d.y - 10; - cv::putText(image, text, point_2d, fontFace, fontScale*0.5, color, thickness_font, 8); - } -} - -// Draw only the 2D points -void draw2DPoints(cv::Mat image, std::vector<cv::Point2f> &list_points, cv::Scalar color) -{ - for( size_t i = 0; i < list_points.size(); i++) - { - cv::Point2f point_2d = list_points[i]; - - // Draw Selected points - cv::circle(image, point_2d, radius, color, -1, lineType ); - } -} - -// Draw an arrow into the image -void drawArrow(cv::Mat image, cv::Point2i p, cv::Point2i q, cv::Scalar color, int arrowMagnitude, int thickness, int line_type, int shift) -{ - //Draw the principle line - cv::line(image, p, q, color, thickness, line_type, shift); - const double PI = CV_PI; - //compute the angle alpha - double angle = atan2((double)p.y-q.y, (double)p.x-q.x); - //compute the coordinates of the first segment - p.x = (int) ( q.x + arrowMagnitude * cos(angle + PI/4)); - p.y = (int) ( q.y + arrowMagnitude * sin(angle + PI/4)); - //Draw the first segment - cv::line(image, p, q, color, thickness, line_type, shift); - //compute the coordinates of the second segment - p.x = (int) ( q.x + arrowMagnitude * cos(angle - PI/4)); - p.y = (int) ( q.y + arrowMagnitude * sin(angle - PI/4)); - //Draw the second segment - cv::line(image, p, q, color, thickness, line_type, shift); -} - -// Draw the 3D coordinate axes -void draw3DCoordinateAxes(cv::Mat image, const std::vector<cv::Point2f> &list_points2d) -{ - cv::Scalar red(0, 0, 255); - cv::Scalar green(0,255,0); - cv::Scalar blue(255,0,0); - cv::Scalar black(0,0,0); - - cv::Point2i origin = list_points2d[0]; - cv::Point2i pointX = list_points2d[1]; - cv::Point2i pointY = list_points2d[2]; - cv::Point2i pointZ = list_points2d[3]; - - drawArrow(image, origin, pointX, red, 9, 2); - drawArrow(image, origin, pointY, blue, 9, 2); - drawArrow(image, origin, pointZ, green, 9, 2); - cv::circle(image, origin, radius/2, black, -1, lineType ); - -} - -// Draw the object mesh -void drawObjectMesh(cv::Mat image, const Mesh *mesh, PnPProblem *pnpProblem, cv::Scalar color) -{ - std::vector<std::vector<int> > list_triangles = mesh->getTrianglesList(); - int skip = 1; -// if (pnpProblem->get_A_matrix().at<float>(0,2) < 300 && list_triangles.size() > 1000) -// skip = 10; - - for( size_t i = 0; i < list_triangles.size(); i+= skip) - { - std::vector<int> tmp_triangle = list_triangles.at(i); - - cv::Point3f point_3d_0 = mesh->getVertex(tmp_triangle[0]); - cv::Point3f point_3d_1 = mesh->getVertex(tmp_triangle[1]); - cv::Point3f point_3d_2 = mesh->getVertex(tmp_triangle[2]); - - //std::cout << point_3d_0 << std::endl << point_3d_1 << std::endl << point_3d_2 << std::endl << std::endl; - - cv::Point2f point_2d_0 = pnpProblem->backproject3DPoint(point_3d_0); - cv::Point2f point_2d_1 = pnpProblem->backproject3DPoint(point_3d_1); - cv::Point2f point_2d_2 = pnpProblem->backproject3DPoint(point_3d_2); - - cv::line(image, point_2d_0, point_2d_1, color, 1); - cv::line(image, point_2d_1, point_2d_2, color, 1); - cv::line(image, point_2d_2, point_2d_0, color, 1); - } -} - -void drawObjectMesh1(cv::Mat image, const Mesh *mesh, const Model *model, PnPProblem *pnpProblem, cv::Scalar color) -{ - std::vector<cv::Point2f> imgpoints; - cv::projectPoints(model->get_points3d(), pnpProblem->get_R_vect(), pnpProblem->get_t_matrix(), pnpProblem->get_A_matrix(), pnpProblem->get_dist_coef(), imgpoints); - draw2DPoints(image, imgpoints, color); - -} - -void drawModel(cv::Mat image, const Model *model, PnPProblem *pnpProblem, cv::Scalar color) -{ - std::vector<cv::Point2f> imgpoints; - cv::projectPoints(model->get_points3d(), pnpProblem->get_R_vect(), pnpProblem->get_t_matrix(), pnpProblem->get_A_matrix(), pnpProblem->get_dist_coef(), imgpoints); - draw2DPoints(image, imgpoints, color); - -} - -void drawModel(cv::Mat image, const Model *model, cv::Mat R_vect, cv::Mat t_mat, cv::Mat A_mat, cv::Mat dist, cv::Scalar color) -{ - std::vector<cv::Point2f> imgpoints; - cv::projectPoints(model->get_points3d(), R_vect, t_mat, A_mat, dist, imgpoints); - draw2DPoints(image, imgpoints, color); - -} - - -// Computes the norm of the translation error -double get_translation_error(const cv::Mat &t_true, const cv::Mat &t) -{ - return cv::norm( t_true - t ); -} - -// Computes the norm of the rotation error -double get_rotation_error(const cv::Mat &R_true, const cv::Mat &R) -{ - cv::Mat error_vec, error_mat; - error_mat = R_true * cv::Mat(R.inv()).mul(-1); - cv::Rodrigues(error_mat, error_vec); - - return cv::norm(error_vec); -} - -// Converts a given Rotation Matrix to Euler angles -cv::Mat rot2euler(const cv::Mat & rotationMatrix) -{ - cv::Mat euler(3,1,CV_64F); - - double m00 = rotationMatrix.at<double>(0,0); - double m02 = rotationMatrix.at<double>(0,2); - double m10 = rotationMatrix.at<double>(1,0); - double m11 = rotationMatrix.at<double>(1,1); - double m12 = rotationMatrix.at<double>(1,2); - double m20 = rotationMatrix.at<double>(2,0); - double m22 = rotationMatrix.at<double>(2,2); - - double x, y, z; - - // Assuming the angles are in radians. - if (m10 > 0.998) { // singularity at north pole - x = 0; - y = CV_PI/2; - z = atan2(m02,m22); - } - else if (m10 < -0.998) { // singularity at south pole - x = 0; - y = -CV_PI/2; - z = atan2(m02,m22); - } - else - { - x = atan2(-m12,m11); - y = asin(m10); - z = atan2(-m20,m00); - } - - euler.at<double>(0) = x; - euler.at<double>(1) = y; - euler.at<double>(2) = z; - - return euler; -} - -// Converts a given Euler angles to Rotation Matrix -cv::Mat euler2rot(const cv::Mat & euler) -{ - cv::Mat rotationMatrix(3,3,CV_64F); - - double x = euler.at<double>(0); - double y = euler.at<double>(1); - double z = euler.at<double>(2); - - // Assuming the angles are in radians. - double ch = cos(z); - double sh = sin(z); - double ca = cos(y); - double sa = sin(y); - double cb = cos(x); - double sb = sin(x); - - double m00, m01, m02, m10, m11, m12, m20, m21, m22; - - m00 = ch * ca; - m01 = sh*sb - ch*sa*cb; - m02 = ch*sa*sb + sh*cb; - m10 = sa; - m11 = ca*cb; - m12 = -ca*sb; - m20 = -sh*ca; - m21 = sh*sa*cb + ch*sb; - m22 = -sh*sa*sb + ch*cb; - - rotationMatrix.at<double>(0,0) = m00; - rotationMatrix.at<double>(0,1) = m01; - rotationMatrix.at<double>(0,2) = m02; - rotationMatrix.at<double>(1,0) = m10; - rotationMatrix.at<double>(1,1) = m11; - rotationMatrix.at<double>(1,2) = m12; - rotationMatrix.at<double>(2,0) = m20; - rotationMatrix.at<double>(2,1) = m21; - rotationMatrix.at<double>(2,2) = m22; - - return rotationMatrix; -} - -// Converts a given string to an integer -int StringToInt ( const std::string &Text ) -{ - std::istringstream ss(Text); - int result; - return ss >> result ? result : 0; -} - -// Converts a given float to a string -std::string FloatToString ( float Number ) -{ - std::ostringstream ss; - ss << Number; - return ss.str(); -} - -// Converts a given integer to a string -std::string IntToString ( int Number ) -{ - std::ostringstream ss; - ss << Number; - return ss.str(); -} diff --git a/detectors/simple_ransac_detection/Utils.h b/detectors/simple_ransac_detection/Utils.h deleted file mode 100644 index 29ff27af..00000000 --- a/detectors/simple_ransac_detection/Utils.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Utils.h - * - * Created on: Mar 28, 2014 - * Author: Edgar Riba - */ - -#ifndef UTILS_H_ -#define UTILS_H_ - -#include <iostream> -#include <sys/time.h> - -#include "PnPProblem.h" -#include "Model.h" - -using namespace std; - -void viewImage(cv::Mat image, vector<cv::KeyPoint> keypoints = vector<cv::KeyPoint>(0)); - -// Draw a text with the question point -void drawQuestion(cv::Mat image, cv::Point3f point, cv::Scalar color); - -// Draw a text with the number of entered points -void drawText(cv::Mat image, std::string text, cv::Scalar color); - -// Draw a text with the number of entered points -void drawText2(cv::Mat image, std::string text, cv::Scalar color); - -// Draw a text with the frame ratio -void drawFPS(cv::Mat image, double fps, cv::Scalar color); - -// Draw a text with the frame ratio -void drawConfidence(cv::Mat image, double confidence, cv::Scalar color); - -// Draw a text with the number of entered points -void drawCounter(cv::Mat image, int n, int n_max, cv::Scalar color); - -// Draw the points and the coordinates -void drawPoints(cv::Mat image, std::vector<cv::Point2f> &list_points_2d, std::vector<cv::Point3f> &list_points_3d, cv::Scalar color); - -// Draw only the 2D points -void draw2DPoints(cv::Mat image, std::vector<cv::Point2f> &list_points, cv::Scalar color); - -// Draw an arrow into the image -void drawArrow(cv::Mat image, cv::Point2i p, cv::Point2i q, cv::Scalar color, int arrowMagnitude = 9, int thickness=1, int line_type=8, int shift=0); - -// Draw the 3D coordinate axes -void draw3DCoordinateAxes(cv::Mat image, const std::vector<cv::Point2f> &list_points2d); - -// Draw the object mesh -void drawObjectMesh(cv::Mat image, const Mesh *mesh, PnPProblem *pnpProblem, cv::Scalar color); -void drawObjectMesh1(cv::Mat image, const Mesh *mesh, const Model *model, PnPProblem *pnpProblem, cv::Scalar color); -void drawModel(cv::Mat image, const Model *model, PnPProblem *pnpProblem, cv::Scalar color); -void drawModel(cv::Mat image, const Model *model, cv::Mat R_vect, cv::Mat t_mat, cv::Mat A_mat, cv::Mat dist, cv::Scalar color); - -// Computes the norm of the translation error -double get_translation_error(const cv::Mat &t_true, const cv::Mat &t); - -// Computes the norm of the rotation error -double get_rotation_error(const cv::Mat &R_true, const cv::Mat &R); - -// Converts a given Rotation Matrix to Euler angles -cv::Mat rot2euler(const cv::Mat & rotationMatrix); - -// Converts a given Euler angles to Rotation Matrix -cv::Mat euler2rot(const cv::Mat & euler); - -// Converts a given string to an integer -int StringToInt ( const std::string &Text ); - -// Converts a given float to a string -std::string FloatToString ( float Number ); - -// Converts a given integer to a string -std::string IntToString ( int Number ); - - - -#endif /* UTILS_H_ */ diff --git a/detectors/simple_ransac_detection/main_detection_image_wo_k.cpp b/detectors/simple_ransac_detection/main_detection_image_wo_k.cpp deleted file mode 100644 index c8e0f96a..00000000 --- a/detectors/simple_ransac_detection/main_detection_image_wo_k.cpp +++ /dev/null @@ -1,578 +0,0 @@ -//--model=Data/Chicken/Mesh.xml --mesh=Data/Chicken/Mesh.ply -// --use_gpu --fast --method=1 --error=2 --confidence=0.9 --iterations=500 --inliers=30 --model=Data/Chicken/Mesh.xml --mesh=Data/Chicken/Mesh.ply --test_images=./Data/Chicken/Chicken/*.JPG --camera_intrinsic_file=Data/out_camera_dataset_101.yml -// --use_gpu --fast --method=1 --error=2 --confidence=0.9 --iterations=500 --inliers=30 --test_images=./Data/Lion/test_images/IMG_*.JPG --camera_intrinsic_file=Data/out_camera_data_lion_old.yml -// --fast --method=1 --error=2 --confidence=0.9 --iterations=500 --inliers=30 --test_images=./Data/Lion/Lion2/IMG_*.JPG --camera_intrinsic_file=Data/out_camera_dataset_101.yml -//--fast --method=1 --error=2 --confidence=0.9 --iterations=500 --inliers=10 --model=Data/Totem/Param.SIFT.xml --mesh=Data/Totem/Param.ply --test_images=./Data/Totem/Totem/IMG_0251.JPG --camera_intrinsic_file=Data/out_camera_dataset_101.yml - -// C++ -#include <iostream> -#include <time.h> -// OpenCV -#include <opencv2/core/core.hpp> -#include <opencv2/core/utility.hpp> -#include <opencv2/highgui/highgui.hpp> -#include <opencv2/imgproc/imgproc.hpp> -#include <opencv2/calib3d/calib3d.hpp> -#include <opencv2/video/tracking.hpp> -#include <opencv2/xfeatures2d.hpp> -#include <opencv2/viz.hpp> - - -#include <sys/time.h> - -// PnP Tutorial -#include "Mesh.h" -#include "Model.h" -#include "PnPProblem.h" -#include "RobustMatcher.h" -#include "ModelRegistration.h" -#include "Utils.h" - -/** GLOBAL VARIABLES **/ - -using namespace cv; -using namespace std; -using namespace cv::xfeatures2d; - - -string tutorial_path = ""; // path to tutorial - - - -//string yml_read_path = tutorial_path + "Data/cookies_ORB.yml"; // 3dpts + descriptors -//string ply_read_path = tutorial_path + "Data/box.ply"; // mesh - -//string video_read_path = tutorial_path + "Data/box.mp4"; // recorded video -string yml_read_path = tutorial_path + "Data/Lion/Final.SIFT.xml"; // 3dpts + descriptors -string ply_read_path = tutorial_path + "Data/Lion/Final.ply"; // mesh -string ply_tex_path = tutorial_path + "Data/Totem/Texture.png"; // mesh - - -string camera_intrinsic_file = tutorial_path + "Data/out_camera_data_lion_old.yml"; // mesh - -// -//string yml_read_path = tutorial_path + "Data/Lion/low_res_texture/Final.pairs"; // 3dpts + descriptors -//string ply_read_path = tutorial_path + "Data/Lion/low_res_texture/Final.ply"; // mesh -//string ply_tex_path = tutorial_path + "Data/Lion/low_res_texture/Texture_1024.png"; // mesh - - -//// Intrinsic camera parameters: UVC WEBCAM -//double fx = 3.51567979e+03; // focal length in mm -//double fy = 3.52343259e+03; -//double sx = 1, sy = 1; // sensor size -//double cx = 2.11793527e+03, cy = 1.49318756e+03; // image size - -// Intrinsic camera parameters: UVC WEBCAM -double fx = 3.51567979e+03; // focal length in mm -double fy = 3.52343259e+03; -double sx = 1, sy = 1; // sensor size -double cx = 2.11793527e+03, cy = 1.49318756e+03; // image size - -double params_WEBCAM[] = { fx, // fx - fy, // fy - cx, // cx - cy}; // cy -//double param_dist[] = {-0.18835486, 0.09173112, 0.00188108, 0.00033594, 0.16224491}; -double param_dist[] = {1.4481173997393210e-01, -3.5247562241022906e-01, 0, 0, 1.4483165395214911e-01}; - -// Some basic colors -Scalar red(0, 0, 255); -Scalar green(0,255,0); -Scalar blue(255,0,0); -Scalar yellow(0,255,255); - - -// Robust Matcher parameters -int numKeyPoints = 2000; // number of detected keypoints -float ratioTest = 0.70f; // ratio test -bool fast_match = true; // fastRobustMatch() or robustMatch() -bool use_gpu = false; -bool use_gpu_match = false; - -// RANSAC parameters -int iterationsCount = 500; // number of Ransac iterations. -float reprojectionError = 2.0; // maximum allowed distance to consider it an inlier. -double confidence = 0.95; // ransac successful confidence. - -// Kalman Filter parameters -int minInliersKalman = 30; // Kalman threshold updating - -// PnP parameters -int pnpMethod = SOLVEPNP_EPNP; - - -/** Functions headers **/ -void help(); -void initKalmanFilter( KalmanFilter &KF, int nStates, int nMeasurements, int nInputs, double dt); -void updateKalmanFilter( KalmanFilter &KF, Mat &measurements, - Mat &translation_estimated, Mat &rotation_estimated ); -void fillMeasurements( Mat &measurements, - const Mat &translation_measured, const Mat &rotation_measured); - - -class FrameGenerator -{ -public: - FrameGenerator(CommandLineParser parser) - { - //some hardcoded default values (TEST ONE IMAGES), the program will run even without any arguments - cameraID = 0; - curr_image = -1; - string test_images = tutorial_path + "Data/Lion/test_images/IMG_20150226_102852.jpg"; - image_list = myglob(test_images); - - inputType = IMAGE_LIST; - - exhausted = false; - cout << "yo " << parser.get<int>("cam_id") << endl; - - //parsing input from the arguments now - if (parser.get<string>("test_images").size() > 0) - { - test_images = parser.get<string>("test_images"); - inputType = IMAGE_LIST; - image_list = myglob(test_images); - } - if (parser.get<string>("video").size() > 0) { - video_read_path = parser.get<string>("video"); - inputType = VIDEO_FILE; - inputCapture.open(video_read_path); - if (!inputCapture.isOpened()) { cout << "FATAL: Cannot open video capture!"; exhausted = true;} - } - if(parser.has("cam_id")) //MAX PREFERENCE - { - cameraID = !parser.get<int>("cam_id") ? parser.get<int>("cam_id") : cameraID; - inputType = CAMERA; - inputCapture.open(cameraID); - if (!inputCapture.isOpened()) { cout << "FATAL: Cannot open video capture!"; exhausted = true;} - } - - - } - - Mat getNextFrame() - { - Mat result; - if (inputType == IMAGE_LIST) - { - curr_image ++ ; - - //sleep(3); //wait for 3 seconds - - if (curr_image == image_list.size() - 1) - exhausted = true; - cout << "Frame: " << image_list[curr_image] << endl; - - -// KFeatureDetector fd("SIFT", true); -// cv::Mat descriptors; vector<KeyPoint> kps; - //fd.findSiftGPUDescriptors(image_list[curr_image].c_str(), descriptors, kps); - - result = imread(image_list[curr_image], IMREAD_COLOR); -// Mat result1 = imread(image_list[curr_image], IMREAD_GRAYSCALE); -// Mat temp; -// cv::cvtColor(result, temp, cv::COLOR_BGR2GRAY); - - //2 -// fd.findSiftGPUDescriptors(result1, descriptors, kps ); -// fd.findSiftGPUDescriptors(temp, descriptors, kps ); -// fd.findSiftGPUDescriptors(result, descriptors, kps ); - - } - else - { - if( inputCapture.isOpened() ) { - //as real time as possible - inputCapture.read(result); - } - else exhausted = false; - } - - return result; - } - -public: - enum InputType { INVALID, CAMERA, VIDEO_FILE, IMAGE_LIST }; - - string input; - - int cameraID; - vector<string> image_list; - string video_read_path; - int curr_image; - VideoCapture inputCapture; - - InputType inputType; - bool exhausted; - - -private: - string patternToUse; - - -}; - -Mat drawMatchesAfterRansac(Mat frame_vis, vector<Point2f> l_pt_scene, vector<KeyPoint> l_kp_model, Mat inliner_idx) { - - Mat img_object = imread(ply_tex_path, IMREAD_GRAYSCALE ); - - vector<KeyPoint> l_kp_scene; - - for (int i =0; i < l_pt_scene.size(); i++) - { - KeyPoint kp(l_pt_scene[i].x, l_pt_scene[i].y, 1); l_kp_scene.push_back(kp); - } - - //make matches - vector<DMatch> match; - for (int i =0; i < inliner_idx.rows; i++) - { - int good_index = inliner_idx.at<int>(i); // i-inlier - DMatch m(good_index, good_index, 1); match.push_back(m); - } - - Mat img_matches; - drawMatches(frame_vis, l_kp_scene, - img_object, l_kp_model, - match, img_matches, Scalar::all(-1), Scalar::all(-1), - vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS ); - - return img_matches; -} - -viz::Mesh getMesh() -{ - Mat lena = imread("Data/simplecube/flower.jpeg"); - - std::vector<Vec3d> points; - std::vector<Vec2d> tcoords; - std::vector<int> polygons; - for(size_t i = 0; i < 64; ++i) - { - double angle = CV_PI/2 * i/64.0; - points.push_back(Vec3d(0.00, cos(angle), sin(angle))*0.75); - points.push_back(Vec3d(1.57, cos(angle), sin(angle))*0.75); - tcoords.push_back(Vec2d(0.0, i/64.0)); - tcoords.push_back(Vec2d(1.0, i/64.0)); - } - - for(int i = 0; i < (int)points.size()/2-1; ++i) - { - int polys[] = {3, 2*i, 2*i+1, 2*i+2, 3, 2*i+1, 2*i+2, 2*i+3}; - polygons.insert(polygons.end(), polys, polys + sizeof(polys)/sizeof(polys[0])); - } - - cv::viz::Mesh mesh; - mesh.cloud = Mat(points, true).reshape(3, 1); - mesh.tcoords = Mat(tcoords, true).reshape(2, 1); - mesh.polygons = Mat(polygons, true).reshape(1, 1); - mesh.texture = lena; - return mesh; -} - -void viz_exp() -{ - viz::Mesh mesh = getMesh(); - - double w = 1024, h = 800; - viz::Camera camera(606, 606, w/2, h/2, Size(w, h)); - - viz::Viz3d viz("show_textured_mesh"); - viz.setCamera(camera); - - - viz.setBackgroundMeshLab(); - - viz.showWidget("mesh", viz::WMesh(mesh)); - viz.setRenderingProperty("mesh", viz::SHADING, viz::SHADING_PHONG); - viz.showWidget("text2d", viz::WText("Textured mesh", Point(20, 20), 20, viz::Color::green())); - viz.spin(); -} - -/** Main program **/ -int main(int argc, char *argv[]) -{ - - //viz_exp(); - - - help(); - - const String keys = - "{help h | | print this message }" - "{video v | | path to recorded video }" - "{test_images img | | image for detection }" - "{cam_id | | pass true if you want the input from the camera }" - "{camera_intrinsic_file | | yml file containing camera parameters }" - "{model | | path to yml model }" - "{mesh | | path to ply mesh }" - "{use_gpu | | use gpu or cpu }" - "{use_gpu_match | | use gpu or cpu for matching }" - "{keypoints k |2000 | number of keypoints to detect }" - "{ratio r |0.7 | threshold for ratio test }" - "{iterations it |500 | RANSAC maximum iterations count }" - "{error e |2.0 | RANSAC reprojection errror }" - "{confidence c |0.95 | RANSAC confidence }" - "{inliers in |30 | minimum inliers for Kalman update }" - "{method pnp |0 | PnP method: (0) ITERATIVE - (1) EPNP - (2) P3P - (3) DLS}" - "{fast f |false | use of robust fast match }" - ; - CommandLineParser parser(argc, argv, keys); - if (parser.has("help")) - { - parser.printMessage(); - return 0; - } - else - { - camera_intrinsic_file = parser.get<string>("camera_intrinsic_file").size() > 0 ? parser.get<string>("camera_intrinsic_file") : camera_intrinsic_file; - yml_read_path = parser.get<string>("model").size() > 0 ? parser.get<string>("model") : yml_read_path; - ply_read_path = parser.get<string>("mesh").size() > 0 ? parser.get<string>("mesh") : ply_read_path; - - use_gpu = parser.has("use_gpu"); - use_gpu_match = parser.has("use_gpu_match"); - - numKeyPoints = !parser.has("keypoints") ? parser.get<int>("keypoints") : numKeyPoints; - ratioTest = !parser.has("ratio") ? parser.get<float>("ratio") : ratioTest; - fast_match = !parser.has("fast") ? parser.get<bool>("fast") : fast_match; - iterationsCount = !parser.has("iterations") ? parser.get<int>("iterations") : iterationsCount; - reprojectionError = !parser.has("error") ? parser.get<float>("error") : reprojectionError; - confidence = !parser.has("confidence") ? parser.get<float>("confidence") : confidence; - minInliersKalman = !parser.has("inliers") ? parser.get<int>("inliers") : minInliersKalman; - pnpMethod = !parser.has("method") ? parser.get<int>("method") : pnpMethod; - } - - - FileStorage fs(camera_intrinsic_file, FileStorage::READ); - Mat cam_man, dist_coeff; - fs["Camera_Matrix"] >> cam_man; - fs["Distortion_Coefficients"] >> dist_coeff; - PnPProblem pnp_detection(cam_man, dist_coeff); - PnPProblem pnp_detection_est(cam_man, dist_coeff); - - - - KalmanFilter KF; // instantiate Kalman Filter - int nStates = 18; // the number of states - int nMeasurements = 6; // the number of measured states - int nInputs = 0; // the number of control actions - double dt = 0.125; // time between measurements (1/FPS) - - Mat measurements(nMeasurements, 1, CV_64F); measurements.setTo(Scalar(0)); - bool good_measurement = false; - - - - // 3D model and its correspondenses - Model model; // instantiate Model object - model.load_new_xml(yml_read_path); // load a 3D textured object model - Mesh mesh; // instantiate Mesh object - mesh.load(ply_read_path); // load an object mesh - - vector<Point3f> list_points3d_model = model.get_points3d(); // list with model 3D coordinates - vector<KeyPoint> list_keypoints_model = model.get_keypoints(); // list with model 3D coordinates - Mat descriptors_model = model.get_descriptors(); // list with descriptors of each 3D coordinate - - - - RobustMatcher rmatcher(model, use_gpu, use_gpu_match); // instantiate RobustMatcher - - - //Timers! - timeval start, end, end_detect; - Timer timer_algo = Timer(); - Timer timer_display = Timer(); - Timer timer_fps = Timer(); - timer_fps.start(); - - - double fps, sec; - // frame counter - int counter = 0; - - - //Frames! - FrameGenerator frameGenerator(parser); - Mat frame_vis; - Mat img_all_matches; - Mat img_selective_matches; - Mat frame; - namedWindow("Overlay", WINDOW_NORMAL); - - - - //namedWindow("Before", WINDOW_NORMAL); namedWindow("After", WINDOW_NORMAL); - - while(!frameGenerator.exhausted && waitKey(30) != 27) // while valid frame - { - //read the frame - cout << "Processing next frame ...." << endl; - frame = frameGenerator.getNextFrame(); - frame_vis = frame.clone(); // refresh visualisation frame - - - // start the clock - timer_algo.start(); timer_display.start(); - - - // -- Step 1: Robust matching between model descriptors and scene descriptors - vector<DMatch> good_matches; // to obtain the 3D points of the model - vector<KeyPoint> keypoints_scene; // to obtain the 2D points of the scene - - - if (fast_match) { - rmatcher.findFeatureAndMatch(frame, good_matches, keypoints_scene, descriptors_model); - } - else { - rmatcher.robustMatch(frame, good_matches, keypoints_scene, descriptors_model); - } - - - Mat img_object = imread(ply_tex_path, IMREAD_GRAYSCALE); - drawMatches(frame, keypoints_scene, - img_object, model.get_keypoints(), - good_matches, img_all_matches, Scalar::all(-1), Scalar::all(-1), - vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS); - //imshow("Before", img_all_matches); - - // -- Step 2: Find out the 2D/3D correspondences - - vector<Point3f> list_points3d_model_match; // container for the model 3D coordinates found in the scene - vector<KeyPoint> list_keypoints_model_match; // container for the model 2D coordinates in the textured image of the corresponding 3D coordinates - vector<Point2f> list_points2d_scene_match; // container for the model 2D coordinates found in the scene - - for (unsigned int match_index = 0; match_index < good_matches.size(); ++match_index) { - Point3f point3d_model = list_points3d_model[good_matches[match_index].trainIdx]; // 3D point from model - KeyPoint kp_model = list_keypoints_model[good_matches[match_index].trainIdx]; // 2D point from model - Point2f point2d_scene = keypoints_scene[good_matches[match_index].queryIdx].pt; // 2D point from the scene - - list_points3d_model_match.push_back(point3d_model); // add 3D point - list_keypoints_model_match.push_back(kp_model); - list_points2d_scene_match.push_back(point2d_scene); // add 2D point - } - - // Draw outliers - draw2DPoints(frame_vis, list_points2d_scene_match, red); - - - Mat inliers_idx; - vector<Point2f> list_points2d_inliers; - - if (good_matches.size() > 0) // None matches, then RANSAC crashes - { - - // -- Step 3: Estimate the pose using RANSAC approach - pnp_detection.estimatePoseRANSAC(list_points3d_model_match, list_points2d_scene_match, - pnpMethod, inliers_idx, - iterationsCount, reprojectionError, confidence); - - // -- Step 4: Catch the inliers keypoints to draw - for (int inliers_index = 0; inliers_index < inliers_idx.rows; ++inliers_index) { - int n = inliers_idx.at<int>(inliers_index); // i-inlier - Point2f point2d = list_points2d_scene_match[n]; // i-inlier point 2D - list_points2d_inliers.push_back(point2d); // add i-inlier to list - - } - - /*std::cout << pnp_detection.get_R_matrix() << std::endl; - std::cout << pnp_detection.get_t_matrix() << std::endl; - std::cout << pnp_detection.get_P_matrix() << std::endl; - std::cout << pnp_detection.get_A_matrix() << std::endl << std::endl;*/ - // Draw inliers points 2D - draw2DPoints(frame_vis, list_points2d_inliers, blue); - - - //##########DEBUG DRAWING########### - img_selective_matches = drawMatchesAfterRansac(frame_vis, list_points2d_scene_match, list_keypoints_model_match, inliers_idx); - //imshow("After", img_selective_matches); - - - if (inliers_idx.rows >= minInliersKalman) good_measurement = true; - else good_measurement = false; - - } - - // -- Step X: Draw pose - timer_algo.stop(); - cout << "Time taken algo: " << timer_algo.getDuration() << endl; - - if (good_measurement) { - //drawObjectMesh1(frame_vis, &mesh, &model, &pnp_detection, green); // draw current pose - pnp_detection_est.set_P_matrix(pnp_detection.get_R_matrix(), pnp_detection.get_t_matrix()); - drawObjectMesh(frame_vis, &mesh, &pnp_detection_est, yellow); // draw estimated pose - cout << "Object detected!!!" << endl; - if(frameGenerator.inputType == FrameGenerator::IMAGE_LIST) - imwrite(frameGenerator.image_list[frameGenerator.curr_image] + ".detected.jpg", frame_vis); - } - else { - //drawObjectMesh1(frame_vis, &mesh, &model, &pnp_detection_est, yellow); // draw estimated pose - //drawObjectMesh(frame_vis, &mesh, &pnp_detection_est, yellow); // draw estimated pose - - } - - - // FRAME RATE - - // see how much time has elapsed - timer_display.stop(); - timer_fps.stop(); - - // calculate current FPS - ++counter; - sec = timer_fps.getDuration(); - cout << "Time taken frame: " << timer_display.getDuration() << endl; - fps = counter / sec; - - drawFPS(frame_vis, fps, yellow); // frame ratio - double detection_ratio = ((double) inliers_idx.rows / (double) good_matches.size()) * 100; - drawConfidence(frame_vis, detection_ratio, yellow); - - - // -- Step X: Draw some debugging text - - // Draw some debug text - int inliers_int = inliers_idx.rows; - int outliers_int = (int) good_matches.size() - inliers_int; - string inliers_str = IntToString(inliers_int); - string outliers_str = IntToString(outliers_int); - string n = IntToString((int) good_matches.size()); - string text = "Found " + inliers_str + " of " + n + " matches"; - string text2 = "Inliers: " + inliers_str + " - Outliers: " + outliers_str; - - drawText(frame_vis, text, green); - drawText2(frame_vis, text2, red); - - imshow("Overlay", frame_vis); - - if (good_measurement) { - if(frameGenerator.inputType == FrameGenerator::IMAGE_LIST) - imwrite(frameGenerator.image_list[frameGenerator.curr_image] + ".detected.jpg", frame_vis); - } - - } - - - waitKey(0); - - - - - cout << "GOODBYE ..." << endl; - -} - -/**********************************************************************************************************/ -void help() -{ -cout -<< "--------------------------------------------------------------------------" << endl -<< "This program shows how to detect an object given its 3D textured model. You can choose to " -<< "use a recorded video or the webcam." << endl -<< "Usage:" << endl -<< "./cpp-tutorial-pnp_detection -help" << endl -<< "Keys:" << endl -<< "'esc' - to quit." << endl -<< "--------------------------------------------------------------------------" << endl -<< endl; -} - diff --git a/detectors/simple_ransac_detection/main_detection_video.cpp b/detectors/simple_ransac_detection/main_detection_video.cpp deleted file mode 100644 index ebabfff7..00000000 --- a/detectors/simple_ransac_detection/main_detection_video.cpp +++ /dev/null @@ -1,562 +0,0 @@ -// C++ -#include <iostream> -#include <time.h> -// OpenCV -#include <opencv2/core/core.hpp> -#include <opencv2/core/utility.hpp> -#include <opencv2/highgui/highgui.hpp> -#include <opencv2/imgproc/imgproc.hpp> -#include <opencv2/calib3d/calib3d.hpp> -#include <opencv2/video/tracking.hpp> -#include <opencv2/xfeatures2d.hpp> - -// PnP Tutorial -#include "Mesh.h" -#include "Model.h" -#include "PnPProblem.h" -#include "RobustMatcher.h" -#include "ModelRegistration.h" -#include "Utils.h" - -/** GLOBAL VARIABLES **/ - -using namespace cv; -using namespace std; -using namespace cv::xfeatures2d; - - -string tutorial_path = "../"; // path to tutorial - -//string video_read_path = tutorial_path + "Data/box.mp4"; // recorded video -//string yml_read_path = tutorial_path + "Data/cookies_ORB.yml"; // 3dpts + descriptors -//string ply_read_path = tutorial_path + "Data/box.ply"; // mesh - -string video_read_path = tutorial_path + "Data/box.mp4"; // recorded video -string yml_read_path = tutorial_path + "Data/Lion/low_res_texture/Final.pairs"; // 3dpts + descriptors -string ply_read_path = tutorial_path + "Data/Lion/low_res_texture/Final.ply"; // mesh -string ply_tex_path = tutorial_path + "Data/Lion/low_res_texture/Texture_1024.png"; // mesh - -string test_image = tutorial_path + "Data/Lion/test_images/IMG_20150226_102852.jpg"; - -// Intrinsic camera parameters: UVC WEBCAM -//double fx = 3.51567979e+03; // focal length in mm -//double fy = 3.52343259e+03; -//double sx = 1, sy = 1; // sensor size -//double cx = 2.11793527e+03, cy = 1.49318756e+03; // image size - -// Intrinsic camera parameters: UVC WEBCAM -double fx = 5.3349052641864398e+02; // focal length in mm -double fy = 5.3349052641864398e+02; -double sx = 1, sy = 1; // sensor size -double cx = 3.1950000000000000e+02, cy = 2.3950000000000000e+02; // image size - -double params_WEBCAM[] = { fx, // fx - fy, // fy - cx, // cx - cy}; // cy -//double param_dist[] = {-0.18835486, 0.09173112, 0.00188108, 0.00033594, 0.16224491}; -double param_dist[] = {1.4481173997393210e-01, -3.5247562241022906e-01, 0, 0, 1.4483165395214911e-01}; - -// Some basic colors -Scalar red(0, 0, 255); -Scalar green(0,255,0); -Scalar blue(255,0,0); -Scalar yellow(0,255,255); - - -// Robust Matcher parameters -int numKeyPoints = 2000; // number of detected keypoints -float ratioTest = 0.70f; // ratio test -bool fast_match = true; // fastRobustMatch() or robustMatch() - -// RANSAC parameters -int iterationsCount = 500; // number of Ransac iterations. -float reprojectionError = 2.0; // maximum allowed distance to consider it an inlier. -double confidence = 0.95; // ransac successful confidence. - -// Kalman Filter parameters -int minInliersKalman = 30; // Kalman threshold updating - -// PnP parameters -int pnpMethod = SOLVEPNP_EPNP; - - -/** Functions headers **/ -void help(); -void initKalmanFilter( KalmanFilter &KF, int nStates, int nMeasurements, int nInputs, double dt); -void updateKalmanFilter( KalmanFilter &KF, Mat &measurements, - Mat &translation_estimated, Mat &rotation_estimated ); -void fillMeasurements( Mat &measurements, - const Mat &translation_measured, const Mat &rotation_measured); - - -Mat drawMatchesAfterRansac(Mat frame_vis, vector<Point2f> l_pt_scene, vector<KeyPoint> l_kp_model, Mat inliner_idx) { - - Mat img_object = imread(ply_tex_path, IMREAD_GRAYSCALE ); - - vector<KeyPoint> l_kp_scene; - - for (int i =0; i < l_pt_scene.size(); i++) - { - KeyPoint kp(l_pt_scene[i].x, l_pt_scene[i].y, 1); l_kp_scene.push_back(kp); - } - - //make matches - vector<DMatch> match; - for (int i =0; i < inliner_idx.rows; i++) - { - int good_index = inliner_idx.at<int>(i); // i-inlier - DMatch m(good_index, good_index, 1); match.push_back(m); - } - - Mat img_matches; - drawMatches(frame_vis, l_kp_scene, - img_object, l_kp_model, - match, img_matches, Scalar::all(-1), Scalar::all(-1), - vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS ); - - return img_matches; -} - -/** Main program **/ -int main(int argc, char *argv[]) -{ - - help(); - - const String keys = - "{help h | | print this message }" - "{video v | | path to recorded video }" - "{test_image img | | image for detection }" - "{model | | path to yml model }" - "{mesh | | path to ply mesh }" - "{keypoints k |2000 | number of keypoints to detect }" - "{ratio r |0.7 | threshold for ratio test }" - "{iterations it |500 | RANSAC maximum iterations count }" - "{error e |2.0 | RANSAC reprojection errror }" - "{confidence c |0.95 | RANSAC confidence }" - "{inliers in |30 | minimum inliers for Kalman update }" - "{method pnp |0 | PnP method: (0) ITERATIVE - (1) EPNP - (2) P3P - (3) DLS}" - "{fast f |false | use of robust fast match }" - ; - CommandLineParser parser(argc, argv, keys); - - if (parser.has("help")) - { - parser.printMessage(); - return 0; - } - else - { - video_read_path = parser.get<string>("video").size() > 0 ? parser.get<string>("video") : video_read_path; - test_image = parser.get<string>("test_image").size() > 0 ? parser.get<string>("test_image") : test_image; - yml_read_path = parser.get<string>("model").size() > 0 ? parser.get<string>("model") : yml_read_path; - ply_read_path = parser.get<string>("mesh").size() > 0 ? parser.get<string>("mesh") : ply_read_path; - numKeyPoints = !parser.has("keypoints") ? parser.get<int>("keypoints") : numKeyPoints; - ratioTest = !parser.has("ratio") ? parser.get<float>("ratio") : ratioTest; - fast_match = !parser.has("fast") ? parser.get<bool>("fast") : fast_match; - iterationsCount = !parser.has("iterations") ? parser.get<int>("iterations") : iterationsCount; - reprojectionError = !parser.has("error") ? parser.get<float>("error") : reprojectionError; - confidence = !parser.has("confidence") ? parser.get<float>("confidence") : confidence; - minInliersKalman = !parser.has("inliers") ? parser.get<int>("inliers") : minInliersKalman; - pnpMethod = !parser.has("method") ? parser.get<int>("method") : pnpMethod; - } - - PnPProblem pnp_detection(params_WEBCAM, param_dist); - PnPProblem pnp_detection_est(params_WEBCAM, param_dist); - - Model model; // instantiate Model object - model.load_new_desc(yml_read_path); // load a 3D textured object model - - Mesh mesh; // instantiate Mesh object - mesh.load(ply_read_path); // load an object mesh - - RobustMatcher rmatcher; // instantiate RobustMatcher - - //Ptr<FeatureDetector> orb = ORB::create(); - Ptr<FeatureDetector> fdetector = SIFT::create(); - - rmatcher.setFeatureDetector(fdetector); // set feature detector - rmatcher.setDescriptorExtractor(fdetector); // set descriptor extractor - - Ptr<flann::IndexParams> indexParams = makePtr<flann::LshIndexParams>(6, 12, 1); // instantiate LSH index parameters - Ptr<flann::SearchParams> searchParams = makePtr<flann::SearchParams>(50); // instantiate flann search parameters - - // instantiate FlannBased matcher - Ptr<DescriptorMatcher> matcher = makePtr<FlannBasedMatcher>(indexParams, searchParams); - //rmatcher.setDescriptorMatcher(matcher); // set matcher - //rmatcher.setRatio(ratioTest); // set ratio test parameter - - KalmanFilter KF; // instantiate Kalman Filter - int nStates = 18; // the number of states - int nMeasurements = 6; // the number of measured states - int nInputs = 0; // the number of control actions - double dt = 0.125; // time between measurements (1/FPS) - - initKalmanFilter(KF, nStates, nMeasurements, nInputs, dt); // init function - Mat measurements(nMeasurements, 1, CV_64F); measurements.setTo(Scalar(0)); - bool good_measurement = false; - - - // Get the MODEL INFO - vector<Point3f> list_points3d_model = model.get_points3d(); // list with model 3D coordinates - vector<KeyPoint> list_keypoints_model = model.get_keypoints(); // list with model 3D coordinates - Mat descriptors_model = model.get_descriptors(); // list with descriptors of each 3D coordinate - - // Create & Open Window - namedWindow("REAL TIME DEMO", WINDOW_NORMAL); - - - VideoCapture cap; // instantiate VideoCapture - cap.open(0); // open a recorded video - - if(!cap.isOpened()) // check if we succeeded - { - cout << "Could not open the camera device" << endl; - return -1; - } - - // start and end times - time_t start, end; - - // fps calculated using number of frames / seconds - // floating point seconds elapsed since start - double fps, sec; - - // frame counter - int counter = 0; - - // start the clock - time(&start); - - Mat frame_vis; - Mat img_all_matches; - Mat img_selective_matches; - Mat frame; - - //Mat frame = imread(test_image, IMREAD_COLOR); - - while(cap.read(frame) && waitKey(30) != 27) // capture frame until ESC is pressed - { - frame_vis = frame.clone(); // refresh visualisation frame - - - // -- Step 1: Robust matching between model descriptors and scene descriptors - - vector<DMatch> good_matches; // to obtain the 3D points of the model - vector<KeyPoint> keypoints_scene; // to obtain the 2D points of the scene - - - if (fast_match) { - rmatcher.fastRobustMatch(frame, good_matches, keypoints_scene, descriptors_model); - } - else { - rmatcher.robustMatch(frame, good_matches, keypoints_scene, descriptors_model); - } - - - Mat img_object = imread(ply_tex_path, IMREAD_GRAYSCALE); - drawMatches(frame, keypoints_scene, - img_object, model.get_keypoints(), - good_matches, img_all_matches, Scalar::all(-1), Scalar::all(-1), - vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS); - - - // -- Step 2: Find out the 2D/3D correspondences - - vector<Point3f> list_points3d_model_match; // container for the model 3D coordinates found in the scene - vector<KeyPoint> list_keypoints_model_match; // container for the model 2D coordinates in the textured image of the corresponding 3D coordinates - vector<Point2f> list_points2d_scene_match; // container for the model 2D coordinates found in the scene - - for (unsigned int match_index = 0; match_index < good_matches.size(); ++match_index) { - Point3f point3d_model = list_points3d_model[good_matches[match_index].trainIdx]; // 3D point from model - KeyPoint kp_model = list_keypoints_model[good_matches[match_index].trainIdx]; // 2D point from model - Point2f point2d_scene = keypoints_scene[good_matches[match_index].queryIdx].pt; // 2D point from the scene - - list_points3d_model_match.push_back(point3d_model); // add 3D point - list_keypoints_model_match.push_back(kp_model); - list_points2d_scene_match.push_back(point2d_scene); // add 2D point - } - - // Draw outliers - draw2DPoints(frame_vis, list_points2d_scene_match, red); - - - Mat inliers_idx; - vector<Point2f> list_points2d_inliers; - - if (good_matches.size() > 0) // None matches, then RANSAC crashes - { - - // -- Step 3: Estimate the pose using RANSAC approach - pnp_detection.estimatePoseRANSAC(list_points3d_model_match, list_points2d_scene_match, - pnpMethod, inliers_idx, - iterationsCount, reprojectionError, confidence); - - // -- Step 4: Catch the inliers keypoints to draw - for (int inliers_index = 0; inliers_index < inliers_idx.rows; ++inliers_index) { - int n = inliers_idx.at<int>(inliers_index); // i-inlier - Point2f point2d = list_points2d_scene_match[n]; // i-inlier point 2D - list_points2d_inliers.push_back(point2d); // add i-inlier to list - - } - - std::cout << pnp_detection.get_R_matrix() << std::endl; - std::cout << pnp_detection.get_t_matrix() << std::endl; - std::cout << pnp_detection.get_P_matrix() << std::endl; - std::cout << pnp_detection.get_A_matrix() << std::endl << std::endl; - // Draw inliers points 2D - draw2DPoints(frame_vis, list_points2d_inliers, blue); - - img_selective_matches = drawMatchesAfterRansac(frame_vis, list_points2d_scene_match, - list_keypoints_model_match, inliers_idx); - - - - - - // -- Step 5: Kalman Filter - - good_measurement = false; - - // GOOD MEASUREMENT - if (inliers_idx.rows >= minInliersKalman) { - - // Get the measured translation - Mat translation_measured(3, 1, CV_64F); - translation_measured = pnp_detection.get_t_matrix(); - - // Get the measured rotation - Mat rotation_measured(3, 3, CV_64F); - rotation_measured = pnp_detection.get_R_matrix(); - - // fill the measurements vector - fillMeasurements(measurements, translation_measured, rotation_measured); - - good_measurement = true; - - } - - // Instantiate estimated translation and rotation - Mat translation_estimated(3, 1, CV_64F); - Mat rotation_estimated(3, 3, CV_64F); - - // update the Kalman filter with good measurements - updateKalmanFilter(KF, measurements, - translation_estimated, rotation_estimated); - - - // -- Step 6: Set estimated projection matrix - pnp_detection_est.set_P_matrix(rotation_estimated, translation_estimated); - - - std::cout << pnp_detection.get_R_matrix() << std::endl; - std::cout << pnp_detection.get_t_matrix() << std::endl; - std::cout << pnp_detection.get_P_matrix() << std::endl; - std::cout << pnp_detection.get_A_matrix() << std::endl << std::endl; - } - - // -- Step X: Draw pose - - if (good_measurement) { - //drawObjectMesh1(frame_vis, &mesh, &model, &pnp_detection, green); // draw current pose - drawObjectMesh(frame_vis, &mesh, &pnp_detection_est, yellow); // draw estimated pose - } - else { - //drawObjectMesh1(frame_vis, &mesh, &model, &pnp_detection_est, yellow); // draw estimated pose - //drawObjectMesh(frame_vis, &mesh, &pnp_detection_est, yellow); // draw estimated pose - - } - - float l = 5; - vector<Point2f> pose_points2d; - pose_points2d.push_back(pnp_detection_est.backproject3DPoint(Point3f(0, 0, 0))); // axis center - pose_points2d.push_back(pnp_detection_est.backproject3DPoint(Point3f(l, 0, 0))); // axis x - pose_points2d.push_back(pnp_detection_est.backproject3DPoint(Point3f(0, l, 0))); // axis y - pose_points2d.push_back(pnp_detection_est.backproject3DPoint(Point3f(0, 0, l))); // axis z - draw3DCoordinateAxes(frame_vis, pose_points2d); // draw axes - - // FRAME RATE - - // see how much time has elapsed - time(&end); - - // calculate current FPS - ++counter; - sec = difftime(end, start); - - fps = counter / sec; - - drawFPS(frame_vis, fps, yellow); // frame ratio - double detection_ratio = ((double) inliers_idx.rows / (double) good_matches.size()) * 100; - drawConfidence(frame_vis, detection_ratio, yellow); - - - // -- Step X: Draw some debugging text - - // Draw some debug text - int inliers_int = inliers_idx.rows; - int outliers_int = (int) good_matches.size() - inliers_int; - string inliers_str = IntToString(inliers_int); - string outliers_str = IntToString(outliers_int); - string n = IntToString((int) good_matches.size()); - string text = "Found " + inliers_str + " of " + n + " matches"; - string text2 = "Inliers: " + inliers_str + " - Outliers: " + outliers_str; - - drawText(frame_vis, text, green); - drawText2(frame_vis, text2, red); - - imshow("REAL TIME DEMO", frame_vis); - - } - - namedWindow("Matches before PNP", WINDOW_NORMAL); - namedWindow("Matches after PNP", WINDOW_NORMAL); - namedWindow("Overlay", WINDOW_NORMAL); - - imshow( "Matches before PNP", img_all_matches ); - imshow( "Matches after PNP", img_selective_matches ); - imshow( "Overlay", frame_vis); - - while(1) { - - char key = waitKey(0); - if (key == 'Q' || key == 'q') break; - } - - - cout << "GOODBYE ..." << endl; - -} - -/**********************************************************************************************************/ -void help() -{ -cout -<< "--------------------------------------------------------------------------" << endl -<< "This program shows how to detect an object given its 3D textured model. You can choose to " -<< "use a recorded video or the webcam." << endl -<< "Usage:" << endl -<< "./cpp-tutorial-pnp_detection -help" << endl -<< "Keys:" << endl -<< "'esc' - to quit." << endl -<< "--------------------------------------------------------------------------" << endl -<< endl; -} - -/**********************************************************************************************************/ -void initKalmanFilter(KalmanFilter &KF, int nStates, int nMeasurements, int nInputs, double dt) -{ - - KF.init(nStates, nMeasurements, nInputs, CV_64F); // init Kalman Filter - - setIdentity(KF.processNoiseCov, Scalar::all(1e-5)); // set process noise - setIdentity(KF.measurementNoiseCov, Scalar::all(1e-2)); // set measurement noise - setIdentity(KF.errorCovPost, Scalar::all(1)); // error covariance - - - /** DYNAMIC MODEL **/ - - // [1 0 0 dt 0 0 dt2 0 0 0 0 0 0 0 0 0 0 0] - // [0 1 0 0 dt 0 0 dt2 0 0 0 0 0 0 0 0 0 0] - // [0 0 1 0 0 dt 0 0 dt2 0 0 0 0 0 0 0 0 0] - // [0 0 0 1 0 0 dt 0 0 0 0 0 0 0 0 0 0 0] - // [0 0 0 0 1 0 0 dt 0 0 0 0 0 0 0 0 0 0] - // [0 0 0 0 0 1 0 0 dt 0 0 0 0 0 0 0 0 0] - // [0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0] - // [0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0] - // [0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0] - // [0 0 0 0 0 0 0 0 0 1 0 0 dt 0 0 dt2 0 0] - // [0 0 0 0 0 0 0 0 0 0 1 0 0 dt 0 0 dt2 0] - // [0 0 0 0 0 0 0 0 0 0 0 1 0 0 dt 0 0 dt2] - // [0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 dt 0 0] - // [0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 dt 0] - // [0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 dt] - // [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0] - // [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0] - // [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1] - - // position - KF.transitionMatrix.at<double>(0,3) = dt; - KF.transitionMatrix.at<double>(1,4) = dt; - KF.transitionMatrix.at<double>(2,5) = dt; - KF.transitionMatrix.at<double>(3,6) = dt; - KF.transitionMatrix.at<double>(4,7) = dt; - KF.transitionMatrix.at<double>(5,8) = dt; - KF.transitionMatrix.at<double>(0,6) = 0.5*pow(dt,2); - KF.transitionMatrix.at<double>(1,7) = 0.5*pow(dt,2); - KF.transitionMatrix.at<double>(2,8) = 0.5*pow(dt,2); - - // orientation - KF.transitionMatrix.at<double>(9,12) = dt; - KF.transitionMatrix.at<double>(10,13) = dt; - KF.transitionMatrix.at<double>(11,14) = dt; - KF.transitionMatrix.at<double>(12,15) = dt; - KF.transitionMatrix.at<double>(13,16) = dt; - KF.transitionMatrix.at<double>(14,17) = dt; - KF.transitionMatrix.at<double>(9,15) = 0.5*pow(dt,2); - KF.transitionMatrix.at<double>(10,16) = 0.5*pow(dt,2); - KF.transitionMatrix.at<double>(11,17) = 0.5*pow(dt,2); - - - /** MEASUREMENT MODEL **/ - - // [1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] - // [0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] - // [0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] - // [0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0] - // [0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0] - // [0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0] - - KF.measurementMatrix.at<double>(0,0) = 1; // x - KF.measurementMatrix.at<double>(1,1) = 1; // y - KF.measurementMatrix.at<double>(2,2) = 1; // z - KF.measurementMatrix.at<double>(3,9) = 1; // roll - KF.measurementMatrix.at<double>(4,10) = 1; // pitch - KF.measurementMatrix.at<double>(5,11) = 1; // yaw - -} - -/**********************************************************************************************************/ -void updateKalmanFilter( KalmanFilter &KF, Mat &measurement, - Mat &translation_estimated, Mat &rotation_estimated ) -{ - - // First predict, to update the internal statePre variable - Mat prediction = KF.predict(); - - // The "correct" phase that is going to use the predicted value and our measurement - Mat estimated = KF.correct(measurement); - - // Estimated translation - translation_estimated.at<double>(0) = estimated.at<double>(0); - translation_estimated.at<double>(1) = estimated.at<double>(1); - translation_estimated.at<double>(2) = estimated.at<double>(2); - - // Estimated euler angles - Mat eulers_estimated(3, 1, CV_64F); - eulers_estimated.at<double>(0) = estimated.at<double>(9); - eulers_estimated.at<double>(1) = estimated.at<double>(10); - eulers_estimated.at<double>(2) = estimated.at<double>(11); - - // Convert estimated quaternion to rotation matrix - rotation_estimated = euler2rot(eulers_estimated); - -} - -/**********************************************************************************************************/ -void fillMeasurements( Mat &measurements, - const Mat &translation_measured, const Mat &rotation_measured) -{ - // Convert rotation matrix to euler angles - Mat measured_eulers(3, 1, CV_64F); - measured_eulers = rot2euler(rotation_measured); - - // Set measurement to predict - measurements.at<double>(0) = translation_measured.at<double>(0); // x - measurements.at<double>(1) = translation_measured.at<double>(1); // y - measurements.at<double>(2) = translation_measured.at<double>(2); // z - measurements.at<double>(3) = measured_eulers.at<double>(0); // roll - measurements.at<double>(4) = measured_eulers.at<double>(1); // pitch - measurements.at<double>(5) = measured_eulers.at<double>(2); // yaw -} diff --git a/detectors/simple_ransac_detection/main_registration.cpp b/detectors/simple_ransac_detection/main_registration.cpp deleted file mode 100644 index da775a06..00000000 --- a/detectors/simple_ransac_detection/main_registration.cpp +++ /dev/null @@ -1,268 +0,0 @@ -// C++ -#include <iostream> -// OpenCV -#include <opencv2/core/core.hpp> -#include <opencv2/imgproc/imgproc.hpp> -#include <opencv2/calib3d/calib3d.hpp> -#include <opencv2/features2d/features2d.hpp> -// PnP Tutorial -#include "Mesh.h" -#include "Model.h" -#include "PnPProblem.h" -#include "RobustMatcher.h" -#include "ModelRegistration.h" -#include "Utils.h" - -using namespace cv; -using namespace std; - -/** GLOBAL VARIABLES **/ - -string tutorial_path = "../../samples/cpp/tutorial_code/calib3d/real_time_pose_estimation/"; // path to tutorial - -string img_path = tutorial_path + "Data/resized_IMG_3875.JPG"; // image to register -string ply_read_path = tutorial_path + "Data/box.ply"; // object mesh -string write_path = tutorial_path + "Data/cookies_ORB.yml"; // output file - -// Boolean the know if the registration it's done -bool end_registration = false; - -// Intrinsic camera parameters: UVC WEBCAM -double f = 45; // focal length in mm -double sx = 22.3, sy = 14.9; -double width = 2592, height = 1944; -double params_CANON[] = { width*f/sx, // fx - height*f/sy, // fy - width/2, // cx - height/2}; // cy - -// Setup the points to register in the image -// In the order of the *.ply file and starting at 1 -int n = 8; -int pts[] = {1, 2, 3, 4, 5, 6, 7, 8}; // 3 -> 4 - -// Some basic colors -Scalar red(0, 0, 255); -Scalar green(0,255,0); -Scalar blue(255,0,0); -Scalar yellow(0,255,255); - -/* - * CREATE MODEL REGISTRATION OBJECT - * CREATE OBJECT MESH - * CREATE OBJECT MODEL - * CREATE PNP OBJECT - */ -ModelRegistration registration; -Model model; -Mesh mesh; -PnPProblem pnp_registration(params_CANON); - -/** Functions headers **/ -void help(); - -// Mouse events for model registration -static void onMouseModelRegistration( int event, int x, int y, int, void* ) -{ - if ( event == EVENT_LBUTTONUP ) - { - int n_regist = registration.getNumRegist(); - int n_vertex = pts[n_regist]; - - Point2f point_2d = Point2f((float)x,(float)y); - Point3f point_3d = mesh.getVertex(n_vertex-1); - - bool is_registrable = registration.is_registrable(); - if (is_registrable) - { - registration.registerPoint(point_2d, point_3d); - if( registration.getNumRegist() == registration.getNumMax() ) end_registration = true; - } - } -} - -/** Main program **/ -int main() -{ - - help(); - - // load a mesh given the *.ply file path - mesh.load(ply_read_path); - - // set parameters - int numKeyPoints = 10000; - - //Instantiate robust matcher: detector, extractor, matcher - RobustMatcher rmatcher; - Ptr<FeatureDetector> detector = ORB::create(numKeyPoints); - rmatcher.setFeatureDetector(detector); - - /** GROUND TRUTH OF THE FIRST IMAGE **/ - - // Create & Open Window - namedWindow("MODEL REGISTRATION", WINDOW_KEEPRATIO); - - // Set up the mouse events - setMouseCallback("MODEL REGISTRATION", onMouseModelRegistration, 0 ); - - // Open the image to register - Mat img_in = imread(img_path, IMREAD_COLOR); - Mat img_vis = img_in.clone(); - - if (!img_in.data) { - cout << "Could not open or find the image" << endl; - return -1; - } - - // Set the number of points to register - int num_registrations = n; - registration.setNumMax(num_registrations); - - cout << "Click the box corners ..." << endl; - cout << "Waiting ..." << endl; - - // Loop until all the points are registered - while ( waitKey(30) < 0 ) - { - // Refresh debug image - img_vis = img_in.clone(); - - // Current registered points - vector<Point2f> list_points2d = registration.get_points2d(); - vector<Point3f> list_points3d = registration.get_points3d(); - - // Draw current registered points - drawPoints(img_vis, list_points2d, list_points3d, red); - - // If the registration is not finished, draw which 3D point we have to register. - // If the registration is finished, breaks the loop. - if (!end_registration) - { - // Draw debug text - int n_regist = registration.getNumRegist(); - int n_vertex = pts[n_regist]; - Point3f current_poin3d = mesh.getVertex(n_vertex-1); - - drawQuestion(img_vis, current_poin3d, green); - drawCounter(img_vis, registration.getNumRegist(), registration.getNumMax(), red); - } - else - { - // Draw debug text - drawText(img_vis, "END REGISTRATION", green); - drawCounter(img_vis, registration.getNumRegist(), registration.getNumMax(), green); - break; - } - - // Show the image - imshow("MODEL REGISTRATION", img_vis); - } - - /** COMPUTE CAMERA POSE **/ - - cout << "COMPUTING POSE ..." << endl; - - // The list of registered points - vector<Point2f> list_points2d = registration.get_points2d(); - vector<Point3f> list_points3d = registration.get_points3d(); - - // Estimate pose given the registered points - bool is_correspondence = pnp_registration.estimatePose(list_points3d, list_points2d, SOLVEPNP_ITERATIVE); - if ( is_correspondence ) - { - cout << "Correspondence found" << endl; - - // Compute all the 2D points of the mesh to verify the algorithm and draw it - vector<Point2f> list_points2d_mesh = pnp_registration.verify_points(&mesh); - draw2DPoints(img_vis, list_points2d_mesh, green); - - } else { - cout << "Correspondence not found" << endl << endl; - } - - // Show the image - imshow("MODEL REGISTRATION", img_vis); - - // Show image until ESC pressed - waitKey(0); - - - /** COMPUTE 3D of the image Keypoints **/ - - // Containers for keypoints and descriptors of the model - vector<KeyPoint> keypoints_model; - Mat descriptors; - - // Compute keypoints and descriptors - rmatcher.computeKeyPoints(img_in, keypoints_model); - rmatcher.computeDescriptors(img_in, keypoints_model, descriptors); - - // Check if keypoints are on the surface of the registration image and add to the model - for (unsigned int i = 0; i < keypoints_model.size(); ++i) { - Point2f point2d(keypoints_model[i].pt); - Point3f point3d; - bool on_surface = pnp_registration.backproject2DPoint(&mesh, point2d, point3d); - if (on_surface) - { - model.add_correspondence(point2d, point3d); - model.add_descriptor(descriptors.row(i)); - model.add_keypoint(keypoints_model[i]); - } - else - { - model.add_outlier(point2d); - } - } - - // save the model into a *.yaml file - model.save(write_path); - - // Out image - img_vis = img_in.clone(); - - // The list of the points2d of the model - vector<Point2f> list_points_in = model.get_points2d_in(); - vector<Point2f> list_points_out = model.get_points2d_out(); - - // Draw some debug text - string num = IntToString((int)list_points_in.size()); - string text = "There are " + num + " inliers"; - drawText(img_vis, text, green); - - // Draw some debug text - num = IntToString((int)list_points_out.size()); - text = "There are " + num + " outliers"; - drawText2(img_vis, text, red); - - // Draw the object mesh - drawObjectMesh(img_vis, &mesh, &pnp_registration, blue); - - // Draw found keypoints depending on if are or not on the surface - draw2DPoints(img_vis, list_points_in, green); - draw2DPoints(img_vis, list_points_out, red); - - // Show the image - imshow("MODEL REGISTRATION", img_vis); - - // Wait until ESC pressed - waitKey(0); - - // Close and Destroy Window - destroyWindow("MODEL REGISTRATION"); - - cout << "GOODBYE" << endl; - -} - -/**********************************************************************************************************/ -void help() -{ - cout - << "--------------------------------------------------------------------------" << endl - << "This program shows how to create your 3D textured model. " << endl - << "Usage:" << endl - << "./cpp-tutorial-pnp_registration" << endl - << "--------------------------------------------------------------------------" << endl - << endl; -} diff --git a/detectors/simple_ransac_detection/scale_intrinsic.cpp b/detectors/simple_ransac_detection/scale_intrinsic.cpp deleted file mode 100644 index 6a1e15f4..00000000 --- a/detectors/simple_ransac_detection/scale_intrinsic.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// -// Created by sarkar on 04.05.15. -// - - -// C++ -#include <iostream> -#include <time.h> -// OpenCV -#include <opencv2/core/core.hpp> -#include <opencv2/calib3d/calib3d.hpp> - - -using namespace std; -using namespace cv; - -int main(int argc, char *argv[]) -{ - if( argc != 4 ) - { cout << "Not sufficient arguments!!"; return -1; } - - string matname = argv[1]; - string matnamescaled = argv[2]; - float scale = atof(argv[3]); - - FileStorage fr(matname, FileStorage::READ); - FileStorage fw(matnamescaled, FileStorage::WRITE); - - Mat K, d; - fr["Camera_Matrix"] >> K; - fr["Distortion_Coefficients"] >> d; - - - K = scale * K; - K.at<double>(2, 2) = 1; - - fw << "Camera_Matrix" << K; - fw << "Distortion_Coefficients" << d; - - return 1; -} \ No newline at end of file diff --git a/detectors/src/local2D/CMakeLists.txt b/detectors/src/local2D/CMakeLists.txt index bc2c7ee7..17a2ba39 100644 --- a/detectors/src/local2D/CMakeLists.txt +++ b/detectors/src/local2D/CMakeLists.txt @@ -1,11 +1,12 @@ set(SUBSYS_NAME local_image_detector) set(LIB_NAME od_${SUBSYS_NAME}) set(SUBSYS_DESC "The local feature matching based detector") - set(SUBSYS_DEPS od_common ${PCL_LIBRARIES} ${VTK_LIBRARIES} ${OpenCV_LIBS}) + + include_directories(${CMAKE_SOURCE_DIR}/3rdparty/pugixml/src/) -link_directories(${CMAKE_SOURCE_DIR}/3rdparty/pugixml/build/) +#link_directories(${CMAKE_SOURCE_DIR}/3rdparty/pugixml/build/) set(build TRUE) #PCL_SUBSYS_OPTION(build "${SUBSYS_NAME}" "${SUBSYS_DESC}" ON) @@ -18,20 +19,35 @@ if(build) "${DETECTORS_INCLUDE_DIR}/od/detectors/local2D/ODImageLocalMatching.h" "${DETECTORS_INCLUDE_DIR}/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h" "${DETECTORS_INCLUDE_DIR}/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h" + "${DETECTORS_INCLUDE_DIR}/od/detectors/local2D/simple_ransac_detection/CsvReader.h" + "${DETECTORS_INCLUDE_DIR}/od/detectors/local2D/simple_ransac_detection/CsvWriter.h" + "${DETECTORS_INCLUDE_DIR}/od/detectors/local2D/simple_ransac_detection/Mesh.h" + "${DETECTORS_INCLUDE_DIR}/od/detectors/local2D/simple_ransac_detection/Model.h" + "${DETECTORS_INCLUDE_DIR}/od/detectors/local2D/simple_ransac_detection/ModelRegistration.h" + "${DETECTORS_INCLUDE_DIR}/od/detectors/local2D/simple_ransac_detection/PnPProblem.h" + "${DETECTORS_INCLUDE_DIR}/od/detectors/local2D/simple_ransac_detection/RobustMatcher.h" + "${DETECTORS_INCLUDE_DIR}/od/detectors/local2D/simple_ransac_detection/Utils.h" ) set(IMPL_INCLUDES "") + set(SOURCES "ODImageLocalMatching.cpp" "training/ODCADRecogTrainerSnapshotBased.cpp" "detection/ODCADRecognizer2DLocal.cpp" - ${SIMPLE_RANSAC_SRC_FILES} + "simple_ransac_detection/CsvReader.cpp" + "simple_ransac_detection/CsvWriter.cpp" + "simple_ransac_detection/ModelRegistration.cpp" + "simple_ransac_detection/Mesh.cpp" + "simple_ransac_detection/Model.cpp" + "simple_ransac_detection/PnPProblem.cpp" + "simple_ransac_detection/Utils.cpp" + "simple_ransac_detection/RobustMatcher.cpp" ) include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) - include_directories(${SIMPLE_RANSAC_INCLUDE_DIR}) OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${SOURCES} INCS ${INCLUDES} ${IMPL_INCLUDES}) install(FILES ${INCLUDES} DESTINATION ${OD_INSTALL_INCLUDE_DIR}/${SUBSYS_NAME} COMPONENT ${LIB_NAME}) diff --git a/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp b/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp index 8e939d1a..37056126 100644 --- a/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp +++ b/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp @@ -40,12 +40,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/common/utils/utils.h" // PnP Tutorial -#include "simple_ransac_detection/Mesh.h" -#include "simple_ransac_detection/Model.h" -#include "simple_ransac_detection/PnPProblem.h" -#include "simple_ransac_detection/RobustMatcher.h" -#include "simple_ransac_detection/ModelRegistration.h" -#include "simple_ransac_detection/Utils.h" +#include "od/detectors/local2D/simple_ransac_detection/Mesh.h" +#include "od/detectors/local2D/simple_ransac_detection/Model.h" +#include "od/detectors/local2D/simple_ransac_detection/PnPProblem.h" +#include "od/detectors/local2D/simple_ransac_detection/RobustMatcher.h" +#include "od/detectors/local2D/simple_ransac_detection/ModelRegistration.h" +#include "od/detectors/local2D/simple_ransac_detection/Utils.h" using namespace cv; From 1f71f5a7753b429dcafc3cec31998f1f1a78f18a Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Tue, 24 May 2016 15:00:10 +0200 Subject: [PATCH 10/79] restructures simple_ransac_detection into local2D and fixes the cmake structure --- .../simple_ransac_detection/CsvReader.h | 40 ++ .../simple_ransac_detection/CsvWriter.h | 25 ++ .../local2D/simple_ransac_detection/Mesh.h | 87 +++++ .../local2D/simple_ransac_detection/Model.h | 67 ++++ .../ModelRegistration.h | 43 +++ .../simple_ransac_detection/PnPProblem.h | 71 ++++ .../simple_ransac_detection/RobustMatcher.h | 104 ++++++ .../local2D/simple_ransac_detection/Utils.h | 80 ++++ .../simple_ransac_detection/CsvReader.cpp | 80 ++++ .../simple_ransac_detection/CsvWriter.cpp | 48 +++ .../local2D/simple_ransac_detection/Mesh.cpp | 82 +++++ .../local2D/simple_ransac_detection/Model.cpp | 181 ++++++++++ .../ModelRegistration.cpp | 35 ++ .../simple_ransac_detection/PnPProblem.cpp | 336 +++++++++++++++++ .../simple_ransac_detection/RobustMatcher.cpp | 328 +++++++++++++++++ .../local2D/simple_ransac_detection/Utils.cpp | 341 ++++++++++++++++++ 16 files changed, 1948 insertions(+) create mode 100644 detectors/include/od/detectors/local2D/simple_ransac_detection/CsvReader.h create mode 100644 detectors/include/od/detectors/local2D/simple_ransac_detection/CsvWriter.h create mode 100644 detectors/include/od/detectors/local2D/simple_ransac_detection/Mesh.h create mode 100644 detectors/include/od/detectors/local2D/simple_ransac_detection/Model.h create mode 100644 detectors/include/od/detectors/local2D/simple_ransac_detection/ModelRegistration.h create mode 100644 detectors/include/od/detectors/local2D/simple_ransac_detection/PnPProblem.h create mode 100644 detectors/include/od/detectors/local2D/simple_ransac_detection/RobustMatcher.h create mode 100644 detectors/include/od/detectors/local2D/simple_ransac_detection/Utils.h create mode 100644 detectors/src/local2D/simple_ransac_detection/CsvReader.cpp create mode 100644 detectors/src/local2D/simple_ransac_detection/CsvWriter.cpp create mode 100644 detectors/src/local2D/simple_ransac_detection/Mesh.cpp create mode 100644 detectors/src/local2D/simple_ransac_detection/Model.cpp create mode 100644 detectors/src/local2D/simple_ransac_detection/ModelRegistration.cpp create mode 100644 detectors/src/local2D/simple_ransac_detection/PnPProblem.cpp create mode 100644 detectors/src/local2D/simple_ransac_detection/RobustMatcher.cpp create mode 100644 detectors/src/local2D/simple_ransac_detection/Utils.cpp diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvReader.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvReader.h new file mode 100644 index 00000000..e4a85c16 --- /dev/null +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvReader.h @@ -0,0 +1,40 @@ +#ifndef CSVREADER_H +#define CSVREADER_H + +#include <iostream> +#include <fstream> +#include <opencv2/core/core.hpp> +#include "od/detectors/local2D/simple_ransac_detection/Utils.h" + +using namespace std; +using namespace cv; + +class CsvReader { +public: + /** + * The default constructor of the CSV reader Class. + * The default separator is ' ' (empty space) + * + * @param path - The path of the file to read + * @param separator - The separator character between words per line + * @return + */ + CsvReader(const string &path, const char &separator = ' '); + + /** + * Read a plane text file with .ply format + * + * @param list_vertex - The container of the vertices list of the mesh + * @param list_triangles - The container of the triangles list of the mesh + * @return + */ + void readPLY(vector<Point3f> &list_vertex, vector<vector<int> > &list_triangles); + +private: + /** The current stream file for the reader */ + ifstream _file; + /** The separator character between words for each line */ + char _separator; +}; + +#endif diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvWriter.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvWriter.h new file mode 100644 index 00000000..c49dbe16 --- /dev/null +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvWriter.h @@ -0,0 +1,25 @@ +#ifndef CSVWRITER_H +#define CSVWRITER_H + +#include <iostream> +#include <fstream> +#include <opencv2/core/core.hpp> +#include "od/detectors/local2D/simple_ransac_detection/Utils.h" + +using namespace std; +using namespace cv; + +class CsvWriter { +public: + CsvWriter(const string &path, const string &separator = " "); + ~CsvWriter(); + void writeXYZ(const vector<Point3f> &list_points3d); + void writeUVXYZ(const vector<Point3f> &list_points3d, const vector<Point2f> &list_points2d, const Mat &descriptors); + +private: + ofstream _file; + string _separator; + bool _isFirstTerm; +}; + +#endif diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/Mesh.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/Mesh.h new file mode 100644 index 00000000..d82c8d48 --- /dev/null +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/Mesh.h @@ -0,0 +1,87 @@ +/* + * Mesh.h + * + * Created on: Apr 9, 2014 + * Author: edgar + */ + +#ifndef MESH_H_ +#define MESH_H_ + +#include <iostream> +#include <opencv2/core/core.hpp> + + +// --------------------------------------------------- // +// TRIANGLE CLASS // +// --------------------------------------------------- // + +class Triangle { +public: + + explicit Triangle(int id, cv::Point3f V0, cv::Point3f V1, cv::Point3f V2); + virtual ~Triangle(); + + cv::Point3f getV0() const { return v0_; } + cv::Point3f getV1() const { return v1_; } + cv::Point3f getV2() const { return v2_; } + +private: + /** The identifier number of the triangle */ + int id_; + /** The three vertices that defines the triangle */ + cv::Point3f v0_, v1_, v2_; +}; + + +// --------------------------------------------------- // +// RAY CLASS // +// --------------------------------------------------- // + +class Ray { +public: + + explicit Ray(cv::Point3f P0, cv::Point3f P1); + virtual ~Ray(); + + cv::Point3f getP0() { return p0_; } + cv::Point3f getP1() { return p1_; } + +private: + /** The two points that defines the ray */ + cv::Point3f p0_, p1_; +}; + + +// --------------------------------------------------- // +// OBJECT MESH CLASS // +// --------------------------------------------------- // + +class Mesh +{ +public: + + Mesh(); + virtual ~Mesh(); + + std::vector<std::vector<int> > getTrianglesList() const { return list_triangles_; } + std::vector<cv::Point3f> getVertices() const { return list_vertex_; } + cv::Point3f getVertex(int pos) const { return list_vertex_[pos]; } + int getNumVertices() const { return num_vertexs_; } + + void load(const std::string path_file); + +private: + /** The identification number of the mesh */ + int id_; + /** The current number of vertices in the mesh */ + int num_vertexs_; + /** The current number of triangles in the mesh */ + int num_triangles_; + /* The list of triangles of the mesh */ + std::vector<cv::Point3f> list_vertex_; + /* The list of triangles of the mesh */ + std::vector<std::vector<int> > list_triangles_; +}; + +#endif /* OBJECTMESH_H_ */ diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/Model.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/Model.h new file mode 100644 index 00000000..ef029cd4 --- /dev/null +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/Model.h @@ -0,0 +1,67 @@ +/* + * Model.h + * + * Created on: Apr 9, 2014 + * Author: edgar + */ + +#ifndef MODEL_H_ +#define MODEL_H_ + +#include <iostream> +#include <fstream> +#include <opencv2/core/core.hpp> +#include <opencv2/features2d/features2d.hpp> + +class Model +{ +public: + Model(); + virtual ~Model(); + + std::vector<cv::Point2f> get_points2d_in() const { return list_points2d_in_; } + std::vector<cv::Point2f> get_points2d_out() const { return list_points2d_out_; } + std::vector<cv::Point3f> get_points3d() const { return list_points3d_in_; } + std::vector<cv::KeyPoint> get_keypoints() const { return list_keypoints_; } + cv::Mat get_descriptors() const { return descriptors_; } + int get_numDescriptors() const { return descriptors_.rows; } + + + void add_correspondence(const cv::Point2f &point2d, const cv::Point3f &point3d); + void add_outlier(const cv::Point2f &point2d); + void add_descriptor(const cv::Mat &descriptor); + void add_keypoint(const cv::KeyPoint &kp); + + + void save(const std::string path); + void load(const std::string path); + void load_new_desc(const std::string path); + void load_new_xml(const std::string path); + + + std::string f_type; + std::string id; + +private: + + /** The current number of correspondecnes */ + int n_correspondences_; + /** The list of 2D points on the model surface */ + std::vector<cv::KeyPoint> list_keypoints_; + /** The list of 2D points on the model surface */ + std::vector<cv::Point2f> list_points2d_in_; + /** The list of 2D points outside the model surface */ + std::vector<cv::Point2f> list_points2d_out_; + /** The list of 3D points on the model surface */ + std::vector<cv::Point3f> list_points3d_in_; + /** The list of 2D points descriptors */ + cv::Mat descriptors_; + + int get_descriptor_size() + { + if (f_type == "SIFT") return 128; + return 128; + } +}; + +#endif /* OBJECTMODEL_H_ */ diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/ModelRegistration.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/ModelRegistration.h new file mode 100644 index 00000000..f1e7aca4 --- /dev/null +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/ModelRegistration.h @@ -0,0 +1,43 @@ +/* + * ModelRegistration.h + * + * Created on: Apr 18, 2014 + * Author: edgar + */ + +#ifndef MODELREGISTRATION_H_ +#define MODELREGISTRATION_H_ + +#include <iostream> +#include <opencv2/core/core.hpp> + +class ModelRegistration +{ +public: + + ModelRegistration(); + virtual ~ModelRegistration(); + + void setNumMax(int n) { max_registrations_ = n; } + + std::vector<cv::Point2f> get_points2d() const { return list_points2d_; } + std::vector<cv::Point3f> get_points3d() const { return list_points3d_; } + int getNumMax() const { return max_registrations_; } + int getNumRegist() const { return n_registrations_; } + + bool is_registrable() const { return (n_registrations_ < max_registrations_); } + void registerPoint(const cv::Point2f &point2d, const cv::Point3f &point3d); + void reset(); + +private: +/** The current number of registered points */ +int n_registrations_; +/** The total number of points to register */ +int max_registrations_; +/** The list of 2D points to register the model */ +std::vector<cv::Point2f> list_points2d_; +/** The list of 3D points to register the model */ +std::vector<cv::Point3f> list_points3d_; +}; + +#endif /* MODELREGISTRATION_H_ */ diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/PnPProblem.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/PnPProblem.h new file mode 100644 index 00000000..41b15f03 --- /dev/null +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/PnPProblem.h @@ -0,0 +1,71 @@ +/* + * PnPProblem.h + * + * Created on: Mar 28, 2014 + * Author: Edgar Riba + */ + +#ifndef PNPPROBLEM_H_ +#define PNPPROBLEM_H_ + +#include <iostream> + +#include <opencv2/core/core.hpp> +#include <opencv2/highgui/highgui.hpp> + +#include "od/detectors/local2D/simple_ransac_detection/Mesh.h" +#include "od/detectors/local2D/simple_ransac_detection/ModelRegistration.h" + +class PnPProblem +{ + +public: + explicit PnPProblem(double const param[], double const dists[]); // custom constructor + PnPProblem(cv::Mat intrin = cv::Mat::eye(3, 4, CV_64F), cv::Mat dist = cv::Mat::zeros(1, 5, CV_64F)); + virtual ~PnPProblem(); + + bool backproject2DPoint(const Mesh *mesh, const cv::Point2f &point2d, cv::Point3f &point3d); + bool intersect_MollerTrumbore(Ray &R, Triangle &T, double *out); + std::vector<cv::Point2f> verify_points(Mesh *mesh); + cv::Point2f backproject3DPoint(const cv::Point3f &point3d); + bool estimatePose(const std::vector<cv::Point3f> &list_points3d, const std::vector<cv::Point2f> &list_points2d, int flags); + void estimatePoseRANSAC( const std::vector<cv::Point3f> &list_points3d, const std::vector<cv::Point2f> &list_points2d, + int flags, cv::Mat &inliers, + int iterationsCount, float reprojectionError, double confidence ); + + cv::Mat get_A_matrix() const { return _A_matrix; } + cv::Mat get_dist_coef() const { return _dist; } + cv::Mat get_R_matrix() const { return _R_matrix; } + cv::Mat get_R_vect() const { return _R_vect; } + cv::Mat get_t_matrix() const { return _t_matrix; } + cv::Mat get_P_matrix() const { return _P_matrix; } + + void set_P_matrix( const cv::Mat &R_matrix, const cv::Mat &t_matrix); + + void clearExtrinsics() + { + _R_matrix = cv::Mat::zeros(3, 3, CV_64FC1); // rotation matrix + _t_matrix = cv::Mat::zeros(3, 1, CV_64FC1); // translation matrix + _P_matrix = cv::Mat::zeros(3, 4, CV_64FC1); + } + +private: + /** The calibration matrix */ + cv::Mat _A_matrix; + //dist mat + cv::Mat _dist; + /** The computed rotation matrix */ + cv::Mat _R_matrix; + cv::Mat _R_vect; + /** The computed translation matrix */ + cv::Mat _t_matrix; + /** The computed projection matrix */ + cv::Mat _P_matrix; +}; + +// Functions for Möller–Trumbore intersection algorithm +cv::Point3f CROSS(cv::Point3f v1, cv::Point3f v2); +double DOT(cv::Point3f v1, cv::Point3f v2); +cv::Point3f SUB(cv::Point3f v1, cv::Point3f v2); + +#endif /* PNPPROBLEM_H_ */ diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/RobustMatcher.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/RobustMatcher.h new file mode 100644 index 00000000..e1e95536 --- /dev/null +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/RobustMatcher.h @@ -0,0 +1,104 @@ +/* + * RobustMatcher.h + * + * Created on: Jun 4, 2014 + * Author: eriba + */ + +#ifndef ROBUSTMATCHER_H_ +#define ROBUSTMATCHER_H_ + +#include <iostream> + +#include <opencv2/core/core.hpp> +#include <opencv2/highgui/highgui.hpp> +#include <opencv2/features2d/features2d.hpp> +#include "od/common/utils/ODFeatureDetector2D.h" +#include "od/common/utils/utils.h" +#include "od/detectors/local2D/simple_ransac_detection/Model.h" + +using namespace std; + +class +RobustMatcher { +public: + RobustMatcher(Model const &model, bool use_gpu, bool b); + + + int mode_; + bool use_gpu_; + + virtual ~RobustMatcher(); + + // Set the feature detector + void setFeatureDetector(const cv::Ptr<cv::FeatureDetector>& detect) { detector_ = detect; } + + // Set the descriptor extractor + void setDescriptorExtractor(const cv::Ptr<cv::DescriptorExtractor>& desc) { extractor_ = desc; } + + // Set the matcher + void setDescriptorMatcher(const cv::Ptr<cv::DescriptorMatcher>& match) { matcher_ = match; } + + // Compute the keypoints of an image + void computeKeyPoints( const cv::Mat& image, std::vector<cv::KeyPoint>& keypoints); + + // Compute the descriptors of an image given its keypoints + void computeDescriptors( const cv::Mat& image, std::vector<cv::KeyPoint>& keypoints, cv::Mat& descriptors); + + // Set ratio parameter for the ratio test + void setRatio( float rat) { ratio_ = rat; } + + // Clear matches for which NN ratio is > than threshold + // return the number of removed points + // (corresponding entries being cleared, + // i.e. size will be 0) + int ratioTest(std::vector<std::vector<cv::DMatch> > &matches); + + // Insert symmetrical matches in symMatches vector + void symmetryTest( const std::vector<std::vector<cv::DMatch> >& matches1, + const std::vector<std::vector<cv::DMatch> >& matches2, + std::vector<cv::DMatch>& symMatches ); + + // Match feature points using ratio and symmetry test + void robustMatch( const cv::Mat& frame, std::vector<cv::DMatch>& good_matches, + std::vector<cv::KeyPoint>& keypoints_frame, + const cv::Mat& descriptors_model ); + + // Match feature points using ratio test + void fastRobustMatch( const cv::Mat& frame, std::vector<cv::DMatch>& good_matches, + std::vector<cv::KeyPoint>& keypoints_frame, + const cv::Mat& descriptors_model ); + + void findFeatureAndMatch(cv::Mat const &frame, vector<cv::DMatch> &good_matches, vector<cv::KeyPoint> &keypoints_frame, + cv::Mat const &descriptors_model); + + void match(const cv::Mat & descriptors_frame, const cv::Mat &descriptors_model, std::vector<cv::DMatch>& good_matches); + + void matchNormalized(cv::Mat &descriptors_frame, cv::Mat &descriptors_model, vector<cv::DMatch> &good_matches); + +private: + // pointer to the feature point detector object + cv::Ptr<od::ODFeatureDetector2D> featureDetector_; + + cv::Ptr<cv::FeatureDetector> detector_; + // pointer to the feature descriptor extractor object + cv::Ptr<cv::DescriptorExtractor> extractor_; + // pointer to the matcher object + + //The important feature of this class starts here + //DIFFERENT TYPES OF MATCHERS, add here in this list (based on the construction + cv::Ptr<cv::DescriptorMatcher> matcher_; + cv::Ptr<cv::cuda::DescriptorMatcher> matcher_gpu_; + // max ratio between 1st and 2nd NN + float ratio_; + + void instantiateMatcher(Model const &feature_type, bool use_gpu); + + + + + void instantiateMatcher1(Model const &model, bool use_gpu); + +}; + +#endif /* ROBUSTMATCHER_H_ */ diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/Utils.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/Utils.h new file mode 100644 index 00000000..6603cb59 --- /dev/null +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/Utils.h @@ -0,0 +1,80 @@ +/* + * Utils.h + * + * Created on: Mar 28, 2014 + * Author: Edgar Riba + */ + +#ifndef UTILS_H_ +#define UTILS_H_ + +#include <iostream> +#include <sys/time.h> + +#include "od/detectors/local2D/simple_ransac_detection/PnPProblem.h" +#include "od/detectors/local2D/simple_ransac_detection/Model.h" + +using namespace std; + +void viewImage(cv::Mat image, vector<cv::KeyPoint> keypoints = vector<cv::KeyPoint>(0)); + +// Draw a text with the question point +void drawQuestion(cv::Mat image, cv::Point3f point, cv::Scalar color); + +// Draw a text with the number of entered points +void drawText(cv::Mat image, std::string text, cv::Scalar color); + +// Draw a text with the number of entered points +void drawText2(cv::Mat image, std::string text, cv::Scalar color); + +// Draw a text with the frame ratio +void drawFPS(cv::Mat image, double fps, cv::Scalar color); + +// Draw a text with the frame ratio +void drawConfidence(cv::Mat image, double confidence, cv::Scalar color); + +// Draw a text with the number of entered points +void drawCounter(cv::Mat image, int n, int n_max, cv::Scalar color); + +// Draw the points and the coordinates +void drawPoints(cv::Mat image, std::vector<cv::Point2f> &list_points_2d, std::vector<cv::Point3f> &list_points_3d, cv::Scalar color); + +// Draw only the 2D points +void draw2DPoints(cv::Mat image, std::vector<cv::Point2f> &list_points, cv::Scalar color); + +// Draw an arrow into the image +void drawArrow(cv::Mat image, cv::Point2i p, cv::Point2i q, cv::Scalar color, int arrowMagnitude = 9, int thickness=1, int line_type=8, int shift=0); + +// Draw the 3D coordinate axes +void draw3DCoordinateAxes(cv::Mat image, const std::vector<cv::Point2f> &list_points2d); + +// Draw the object mesh +void drawObjectMesh(cv::Mat image, const Mesh *mesh, PnPProblem *pnpProblem, cv::Scalar color); +void drawObjectMesh1(cv::Mat image, const Mesh *mesh, const Model *model, PnPProblem *pnpProblem, cv::Scalar color); +void drawModel(cv::Mat image, const Model *model, PnPProblem *pnpProblem, cv::Scalar color); +void drawModel(cv::Mat image, const Model *model, cv::Mat R_vect, cv::Mat t_mat, cv::Mat A_mat, cv::Mat dist, cv::Scalar color); + +// Computes the norm of the translation error +double get_translation_error(const cv::Mat &t_true, const cv::Mat &t); + +// Computes the norm of the rotation error +double get_rotation_error(const cv::Mat &R_true, const cv::Mat &R); + +// Converts a given Rotation Matrix to Euler angles +cv::Mat rot2euler(const cv::Mat & rotationMatrix); + +// Converts a given Euler angles to Rotation Matrix +cv::Mat euler2rot(const cv::Mat & euler); + +// Converts a given string to an integer +int StringToInt ( const std::string &Text ); + +// Converts a given float to a string +std::string FloatToString ( float Number ); + +// Converts a given integer to a string +std::string IntToString ( int Number ); + + + +#endif /* UTILS_H_ */ diff --git a/detectors/src/local2D/simple_ransac_detection/CsvReader.cpp b/detectors/src/local2D/simple_ransac_detection/CsvReader.cpp new file mode 100644 index 00000000..8522ea4e --- /dev/null +++ b/detectors/src/local2D/simple_ransac_detection/CsvReader.cpp @@ -0,0 +1,80 @@ +#include "od/detectors/local2D/simple_ransac_detection/CsvReader.h" +#include <stdlib.h> + +/** The default constructor of the CSV reader Class */ +CsvReader::CsvReader(const string &path, const char &separator){ + _file.open(path.c_str(), ifstream::in); + _separator = separator; +} + +/* Read a plane text file with .ply format */ +void CsvReader::readPLY(vector<Point3f> &list_vertex, vector<vector<int> > &list_triangles) +{ + std::string line, tmp_str, n; + int num_vertex = 0, num_triangles = 0; + int count = 0; + bool end_header = false; + bool end_vertex = false; + + // Read the whole *.ply file + while (getline(_file, line)) { + stringstream liness(line); + + // read header + if(!end_header) + { + getline(liness, tmp_str, _separator); + if( tmp_str == "element" ) + { + getline(liness, tmp_str, _separator); + getline(liness, n); + if(tmp_str == "vertex") num_vertex = StringToInt(n); + if(tmp_str == "face") num_triangles = StringToInt(n); + } + if(tmp_str == "end_header") end_header = true; + } + + // read file content + else if(end_header) + { + // read vertex and add into 'list_vertex' + if(!end_vertex && count < num_vertex) + { + string x, y, z; + getline(liness, x, _separator); + getline(liness, y, _separator); + getline(liness, z); + + cv::Point3f tmp_p; + tmp_p.x = atof(x.c_str()); + tmp_p.y = atof(y.c_str()); + tmp_p.z = atof(z.c_str()); + list_vertex.push_back(tmp_p); + + count++; + if(count == num_vertex) + { + count = 0; + end_vertex = !end_vertex; + } + } + // read faces and add into 'list_triangles' + else if(end_vertex && count < num_triangles) + { + string num_pts_per_face, id0, id1, id2; + getline(liness, num_pts_per_face, _separator); + getline(liness, id0, _separator); + getline(liness, id1, _separator); + getline(liness, id2); + + std::vector<int> tmp_triangle(3); + tmp_triangle[0] = StringToInt(id0); + tmp_triangle[1] = StringToInt(id1); + tmp_triangle[2] = StringToInt(id2); + list_triangles.push_back(tmp_triangle); + + count++; + } + } + } +} diff --git a/detectors/src/local2D/simple_ransac_detection/CsvWriter.cpp b/detectors/src/local2D/simple_ransac_detection/CsvWriter.cpp new file mode 100644 index 00000000..94e990ec --- /dev/null +++ b/detectors/src/local2D/simple_ransac_detection/CsvWriter.cpp @@ -0,0 +1,48 @@ +#include "od/detectors/local2D/simple_ransac_detection/CsvWriter.h" + +CsvWriter::CsvWriter(const string &path, const string &separator){ + _file.open(path.c_str(), ofstream::out); + _isFirstTerm = true; + _separator = separator; +} + +CsvWriter::~CsvWriter() { + _file.flush(); + _file.close(); +} + +void CsvWriter::writeXYZ(const vector<Point3f> &list_points3d) +{ + string x, y, z; + for(unsigned int i = 0; i < list_points3d.size(); ++i) + { + x = FloatToString(list_points3d[i].x); + y = FloatToString(list_points3d[i].y); + z = FloatToString(list_points3d[i].z); + + _file << x << _separator << y << _separator << z << std::endl; + } + +} + +void CsvWriter::writeUVXYZ(const vector<Point3f> &list_points3d, const vector<Point2f> &list_points2d, const Mat &descriptors) +{ + string u, v, x, y, z, descriptor_str; + for(unsigned int i = 0; i < list_points3d.size(); ++i) + { + u = FloatToString(list_points2d[i].x); + v = FloatToString(list_points2d[i].y); + x = FloatToString(list_points3d[i].x); + y = FloatToString(list_points3d[i].y); + z = FloatToString(list_points3d[i].z); + + _file << u << _separator << v << _separator << x << _separator << y << _separator << z; + + for(int j = 0; j < 32; ++j) + { + descriptor_str = FloatToString(descriptors.at<float>(i,j)); + _file << _separator << descriptor_str; + } + _file << std::endl; + } +} diff --git a/detectors/src/local2D/simple_ransac_detection/Mesh.cpp b/detectors/src/local2D/simple_ransac_detection/Mesh.cpp new file mode 100644 index 00000000..eb12a286 --- /dev/null +++ b/detectors/src/local2D/simple_ransac_detection/Mesh.cpp @@ -0,0 +1,82 @@ +/* + * Mesh.cpp + * + * Created on: Apr 9, 2014 + * Author: edgar + */ + +#include "od/detectors/local2D/simple_ransac_detection/Mesh.h" +#include "od/detectors/local2D/simple_ransac_detection/CsvReader.h" + + +// --------------------------------------------------- // +// TRIANGLE CLASS // +// --------------------------------------------------- // + +/** The custom constructor of the Triangle Class */ +Triangle::Triangle(int id, cv::Point3f V0, cv::Point3f V1, cv::Point3f V2) +{ + id_ = id; v0_ = V0; v1_ = V1; v2_ = V2; +} + +/** The default destructor of the Class */ +Triangle::~Triangle() +{ + // TODO Auto-generated destructor stub +} + + +// --------------------------------------------------- // +// RAY CLASS // +// --------------------------------------------------- // + +/** The custom constructor of the Ray Class */ +Ray::Ray(cv::Point3f P0, cv::Point3f P1) { + p0_ = P0; p1_ = P1; +} + +/** The default destructor of the Class */ +Ray::~Ray() +{ + // TODO Auto-generated destructor stub +} + + +// --------------------------------------------------- // +// OBJECT MESH CLASS // +// --------------------------------------------------- // + +/** The default constructor of the ObjectMesh Class */ +Mesh::Mesh() : list_vertex_(0) , list_triangles_(0) +{ + id_ = 0; + num_vertexs_ = 0; + num_triangles_ = 0; +} + +/** The default destructor of the ObjectMesh Class */ +Mesh::~Mesh() +{ + // TODO Auto-generated destructor stub +} + + +/** Load a CSV with *.ply format **/ +void Mesh::load(const std::string path) +{ + + // Create the reader + CsvReader csvReader(path); + + // Clear previous data + list_vertex_.clear(); + list_triangles_.clear(); + + // Read from .ply file + csvReader.readPLY(list_vertex_, list_triangles_); + + // Update mesh attributes + num_vertexs_ = (int)list_vertex_.size(); + num_triangles_ = (int)list_triangles_.size(); + +} diff --git a/detectors/src/local2D/simple_ransac_detection/Model.cpp b/detectors/src/local2D/simple_ransac_detection/Model.cpp new file mode 100644 index 00000000..47008ffd --- /dev/null +++ b/detectors/src/local2D/simple_ransac_detection/Model.cpp @@ -0,0 +1,181 @@ +/* + * Model.cpp + * + * Created on: Apr 9, 2014 + * Author: edgar + */ + +#include <fstream> +#include <iostream> +#include <pugixml.hpp> +#include "od/detectors/local2D/simple_ransac_detection/Model.h" +#include "od/detectors/local2D/simple_ransac_detection/CsvWriter.h" +#include <sstream> +#include <ostream> +#include <boost/algorithm/string.hpp> + +Model::Model() : list_points2d_in_(0), list_points2d_out_(0), list_points3d_in_(0) +{ + n_correspondences_ = 0; +} + +Model::~Model() +{ + // TODO Auto-generated destructor stub +} + +void Model::add_correspondence(const cv::Point2f &point2d, const cv::Point3f &point3d) +{ + list_points2d_in_.push_back(point2d); + list_points3d_in_.push_back(point3d); + n_correspondences_++; +} + +void Model::add_outlier(const cv::Point2f &point2d) +{ + list_points2d_out_.push_back(point2d); +} + +void Model::add_descriptor(const cv::Mat &descriptor) +{ + descriptors_.push_back(descriptor); +} + +void Model::add_keypoint(const cv::KeyPoint &kp) +{ + list_keypoints_.push_back(kp); +} + + +/** Save a CSV file and fill the object mesh */ +void Model::save(const std::string path) +{ + cv::Mat points3dmatrix = cv::Mat(list_points3d_in_); + cv::Mat points2dmatrix = cv::Mat(list_points2d_in_); + //cv::Mat keyPointmatrix = cv::Mat(list_keypoints_); + + cv::FileStorage storage(path, cv::FileStorage::WRITE); + storage << "points_3d" << points3dmatrix; + storage << "points_2d" << points2dmatrix; + storage << "keypoints" << list_keypoints_; + storage << "descriptors" << descriptors_; + + storage.release(); +} + +/** Load a YAML file using OpenCv functions **/ +void Model::load(const std::string path) +{ + cv::Mat points3d_mat; + + cv::FileStorage storage(path, cv::FileStorage::READ); + storage["points_3d"] >> points3d_mat; + storage["descriptors"] >> descriptors_; + + points3d_mat.copyTo(list_points3d_in_); + + std::cout << list_points3d_in_.size() << endl; + std::cout << descriptors_; + storage.release(); + id = path; +} + +void Model::load_new_desc(const std::string path) +{ + f_type = "SIFT"; + + std::fstream infile(path.c_str()); + if (!infile.is_open()) + { + std::cout << "ERRROR opening model file!!"; + return ; + + } + int nop, desc_size, point_size; + infile >> nop; + infile >> point_size >> desc_size; + + descriptors_ = Mat(nop, desc_size, CV_32FC1); + + + for (int i = 0; i < nop; i++) + { + + float x, y, z; + infile >> x >> y >> z; + cv::Point3f p3d(x, y, z); + list_points3d_in_.push_back(p3d); + + cv::KeyPoint kp; + infile >> kp.pt.x >> kp.pt.y >> kp.octave >> kp.angle >> kp.response >> kp.size; + list_keypoints_.push_back(kp); + + for (int j = 0; j < desc_size; j++) + { + infile >> descriptors_.at<float>(i, j); + } + } + id = path; +} + +template <typename T> +string ps ( T thing ) +{ + cout << thing; + ostringstream ss; + ss << thing; + return ss.str(); +} + +void Model::load_new_xml(const std::string path) +{ + + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load_file(path.c_str()); + + if (!result) + { + std::cout << "ERRROR opening model file!!"; + return ; + + } + pugi::xml_node pts_node = doc.child("Model").child("Points"); + + if (pts_node.first_child()) + f_type = pts_node.first_child().attribute("desc_type").as_string(); + else return; + + + for (pugi::xml_node pt_node = pts_node.first_child(); pt_node; pt_node = pt_node.next_sibling()) + { + Mat single_desc = Mat::zeros(1, get_descriptor_size(), CV_32FC1); + stringstream ss; + ss.str(pt_node.attribute("p3d").as_string()); + float x, y, z; + ss >> x >> y >> z; ss.clear(); + cv::Point3f p3d(x, y, z); + list_points3d_in_.push_back(p3d); + + ss.str(pt_node.attribute("p2d_prop").as_string()); + cv::KeyPoint kp; + ss >> kp.pt.x >> kp.pt.y >> kp.octave >> kp.angle >> kp.response >> kp.size; ss.clear(); + list_keypoints_.push_back(kp); + + ss.str(pt_node.attribute("desc").as_string()); + float d; + for(int it = 0; it < get_descriptor_size(); it ++) + { + ss >> d; + //cout <<it << " " << ss.eof() << " " << d << endl; + single_desc.at<float> (0, it) = d; + } + ss.clear(); + + descriptors_.push_back(single_desc); + } + + id = path; + //std::cout << list_points3d_in_.size() << endl; + //std::cout << descriptors_; +} + diff --git a/detectors/src/local2D/simple_ransac_detection/ModelRegistration.cpp b/detectors/src/local2D/simple_ransac_detection/ModelRegistration.cpp new file mode 100644 index 00000000..b1bd9219 --- /dev/null +++ b/detectors/src/local2D/simple_ransac_detection/ModelRegistration.cpp @@ -0,0 +1,35 @@ +/* + * ModelRegistration.cpp + * + * Created on: Apr 18, 2014 + * Author: edgar + */ + +#include "od/detectors/local2D/simple_ransac_detection/ModelRegistration.h" + +ModelRegistration::ModelRegistration() +{ + n_registrations_ = 0; + max_registrations_ = 0; +} + +ModelRegistration::~ModelRegistration() +{ + // TODO Auto-generated destructor stub +} + +void ModelRegistration::registerPoint(const cv::Point2f &point2d, const cv::Point3f &point3d) + { + // add correspondence at the end of the vector + list_points2d_.push_back(point2d); + list_points3d_.push_back(point3d); + n_registrations_++; + } + +void ModelRegistration::reset() +{ + n_registrations_ = 0; + max_registrations_ = 0; + list_points2d_.clear(); + list_points3d_.clear(); +} diff --git a/detectors/src/local2D/simple_ransac_detection/PnPProblem.cpp b/detectors/src/local2D/simple_ransac_detection/PnPProblem.cpp new file mode 100644 index 00000000..a9a7416b --- /dev/null +++ b/detectors/src/local2D/simple_ransac_detection/PnPProblem.cpp @@ -0,0 +1,336 @@ +/* + * PnPProblem.cpp + * + * Created on: Mar 28, 2014 + * Author: Edgar Riba + */ + +#include <iostream> +#include <sstream> + +#include "od/detectors/local2D/simple_ransac_detection/PnPProblem.h" +#include "od/detectors/local2D/simple_ransac_detection/Mesh.h" + +#include <opencv2/calib3d/calib3d.hpp> + +/* Functions headers */ +cv::Point3f CROSS(cv::Point3f v1, cv::Point3f v2); +double DOT(cv::Point3f v1, cv::Point3f v2); +cv::Point3f SUB(cv::Point3f v1, cv::Point3f v2); +cv::Point3f get_nearest_3D_point(std::vector<cv::Point3f> &points_list, cv::Point3f origin); + + +/* Functions for Möller–Trumbore intersection algorithm */ + +cv::Point3f CROSS(cv::Point3f v1, cv::Point3f v2) +{ + cv::Point3f tmp_p; + tmp_p.x = v1.y*v2.z - v1.z*v2.y; + tmp_p.y = v1.z*v2.x - v1.x*v2.z; + tmp_p.z = v1.x*v2.y - v1.y*v2.x; + return tmp_p; +} + +double DOT(cv::Point3f v1, cv::Point3f v2) +{ + return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z; +} + +cv::Point3f SUB(cv::Point3f v1, cv::Point3f v2) +{ + cv::Point3f tmp_p; + tmp_p.x = v1.x - v2.x; + tmp_p.y = v1.y - v2.y; + tmp_p.z = v1.z - v2.z; + return tmp_p; +} + +/* End functions for Möller–Trumbore intersection algorithm + * */ + +// Function to get the nearest 3D point to the Ray origin +cv::Point3f get_nearest_3D_point(std::vector<cv::Point3f> &points_list, cv::Point3f origin) +{ + cv::Point3f p1 = points_list[0]; + cv::Point3f p2 = points_list[1]; + + double d1 = std::sqrt( std::pow(p1.x-origin.x, 2) + std::pow(p1.y-origin.y, 2) + std::pow(p1.z-origin.z, 2) ); + double d2 = std::sqrt( std::pow(p2.x-origin.x, 2) + std::pow(p2.y-origin.y, 2) + std::pow(p2.z-origin.z, 2) ); + + if(d1 < d2) + { + return p1; + } + else + { + return p2; + } +} + +// Custom constructor given the intrinsic camera parameters + +PnPProblem::PnPProblem(double const params[], double const dists[]) +{ + _A_matrix = cv::Mat::zeros(3, 3, CV_64FC1); // intrinsic camera parameters + _A_matrix.at<double>(0, 0) = params[0]; // [ fx 0 cx ] + _A_matrix.at<double>(1, 1) = params[1]; // [ 0 fy cy ] + _A_matrix.at<double>(0, 2) = params[2]; // [ 0 0 1 ] + _A_matrix.at<double>(1, 2) = params[3]; + _A_matrix.at<double>(2, 2) = 1; + _dist = cv::Mat::zeros(4, 1, CV_64FC1); + _dist.at<double>(0,0) = dists[0]; _dist.at<double>(1,0) = dists[1]; _dist.at<double>(2,0) = dists[2]; _dist.at<double>(3,0) = dists[3]; + + _R_matrix = cv::Mat::zeros(3, 3, CV_64FC1); // rotation matrix + _t_matrix = cv::Mat::zeros(3, 1, CV_64FC1); // translation matrix + _P_matrix = cv::Mat::zeros(3, 4, CV_64FC1); // rotation-translation matrix + +} + +PnPProblem::PnPProblem(cv::Mat intrin, cv::Mat dist) { + intrin.copyTo(_A_matrix); + dist.copyTo(_dist); + + _R_matrix = cv::Mat::zeros(3, 3, CV_64FC1); // rotation matrix + _t_matrix = cv::Mat::zeros(3, 1, CV_64FC1); // translation matrix + _P_matrix = cv::Mat::zeros(3, 4, CV_64FC1); // rotation-translation matrix +} + + +PnPProblem::~PnPProblem() +{ + // TODO Auto-generated destructor stub +} + +void PnPProblem::set_P_matrix( const cv::Mat &R_matrix, const cv::Mat &t_matrix) +{ + // Rotation-Translation Matrix Definition + _P_matrix.at<double>(0,0) = R_matrix.at<double>(0,0); + _P_matrix.at<double>(0,1) = R_matrix.at<double>(0,1); + _P_matrix.at<double>(0,2) = R_matrix.at<double>(0,2); + _P_matrix.at<double>(1,0) = R_matrix.at<double>(1,0); + _P_matrix.at<double>(1,1) = R_matrix.at<double>(1,1); + _P_matrix.at<double>(1,2) = R_matrix.at<double>(1,2); + _P_matrix.at<double>(2,0) = R_matrix.at<double>(2,0); + _P_matrix.at<double>(2,1) = R_matrix.at<double>(2,1); + _P_matrix.at<double>(2,2) = R_matrix.at<double>(2,2); + + _P_matrix.at<double>(0,3) = t_matrix.at<double>(0); + _P_matrix.at<double>(1,3) = t_matrix.at<double>(1); + _P_matrix.at<double>(2,3) = t_matrix.at<double>(2); + +} + + +// Estimate the pose given a list of 2D/3D correspondences and the method to use +bool PnPProblem::estimatePose( const std::vector<cv::Point3f> &list_points3d, + const std::vector<cv::Point2f> &list_points2d, + int flags) +{ + //cv::Mat distCoeffs = cv::Mat::zeros(4, 1, CV_64FC1); + cv::Mat rvec = cv::Mat::zeros(3, 1, CV_64FC1); + cv::Mat tvec = cv::Mat::zeros(3, 1, CV_64FC1); + + bool useExtrinsicGuess = false; + + // Pose estimation + bool correspondence = cv::solvePnP( list_points3d, list_points2d, _A_matrix, _dist, rvec, tvec, + useExtrinsicGuess, flags); + + // Transforms Rotation Vector to Matrix + Rodrigues(rvec,_R_matrix); + _t_matrix = tvec; + + // Set projection matrix + this->set_P_matrix(_R_matrix, _t_matrix); + + return correspondence; +} + +// Estimate the pose given a list of 2D/3D correspondences with RANSAC and the method to use + +void PnPProblem::estimatePoseRANSAC( const std::vector<cv::Point3f> &list_points3d, // list with model 3D coordinates + const std::vector<cv::Point2f> &list_points2d, // list with scene 2D coordinates + int flags, cv::Mat &inliers, int iterationsCount, // PnP method; inliers container + float reprojectionError, double confidence ) // Ransac parameters +{ + //cv::Mat distCoeffs = cv::Mat::zeros(4, 1, CV_64FC1); // vector of distortion coefficients + cv::Mat rvec = cv::Mat::zeros(3, 1, CV_64FC1); // output rotation vector + cv::Mat tvec = cv::Mat::zeros(3, 1, CV_64FC1); // output translation vector + + bool useExtrinsicGuess = false; // if true the function uses the provided rvec and tvec values as + // initial approximations of the rotation and translation vectors + + cv::solvePnPRansac( list_points3d, list_points2d, _A_matrix, _dist, rvec, tvec, + useExtrinsicGuess, iterationsCount, reprojectionError, confidence, + inliers, flags ); + + rvec.copyTo(_R_vect); + Rodrigues(rvec,_R_matrix); // converts Rotation Vector to Matrix + _t_matrix = tvec; // set translation matrix + + this->set_P_matrix(_R_matrix, _t_matrix); // set rotation-translation matrix + +} + +// Given the mesh, backproject the 3D points to 2D to verify the pose estimation +std::vector<cv::Point2f> PnPProblem::verify_points(Mesh *mesh) +{ + std::vector<cv::Point2f> verified_points_2d; + for( int i = 0; i < mesh->getNumVertices(); i++) + { + cv::Point3f point3d = mesh->getVertex(i); + cv::Point2f point2d = this->backproject3DPoint(point3d); + verified_points_2d.push_back(point2d); + } + + return verified_points_2d; +} + + +// Backproject a 3D point to 2D using the estimated pose parameters + +cv::Point2f PnPProblem::backproject3DPoint(const cv::Point3f &point3d) +{ + // 3D point vector [x y z 1]' + cv::Mat point3d_vec = cv::Mat(4, 1, CV_64FC1); + point3d_vec.at<double>(0) = point3d.x; + point3d_vec.at<double>(1) = point3d.y; + point3d_vec.at<double>(2) = point3d.z; + point3d_vec.at<double>(3) = 1; + + // 2D point vector [u v 1]' + cv::Mat point2d_vec = cv::Mat(4, 1, CV_64FC1); + point2d_vec = _A_matrix * _P_matrix * point3d_vec; + + // Normalization of [u v]' + cv::Point2f point2d; + point2d.x = (float)(point2d_vec.at<double>(0) / point2d_vec.at<double>(2)); + point2d.y = (float)(point2d_vec.at<double>(1) / point2d_vec.at<double>(2)); + + + return point2d; +} + +// Back project a 2D point to 3D and returns if it's on the object surface +bool PnPProblem::backproject2DPoint(const Mesh *mesh, const cv::Point2f &point2d, cv::Point3f &point3d) +{ + // Triangles list of the object mesh + std::vector<std::vector<int> > triangles_list = mesh->getTrianglesList(); + + double lambda = 8; + double u = point2d.x; + double v = point2d.y; + + // Point in vector form + cv::Mat point2d_vec = cv::Mat::ones(3, 1, CV_64F); // 3x1 + point2d_vec.at<double>(0) = u * lambda; + point2d_vec.at<double>(1) = v * lambda; + point2d_vec.at<double>(2) = lambda; + + // Point in camera coordinates + cv::Mat X_c = _A_matrix.inv() * point2d_vec ; // 3x1 + + // Point in world coordinates + cv::Mat X_w = _R_matrix.inv() * ( X_c - _t_matrix ); // 3x1 + + // Center of projection + cv::Mat C_op = cv::Mat(_R_matrix.inv()).mul(-1) * _t_matrix; // 3x1 + + // Ray direction vector + cv::Mat ray = X_w - C_op; // 3x1 + ray = ray / cv::norm(ray); // 3x1 + + // Set up Ray + Ray R((cv::Point3f)C_op, (cv::Point3f)ray); + + // A vector to store the intersections found + std::vector<cv::Point3f> intersections_list; + + // Loop for all the triangles and check the intersection + for (unsigned int i = 0; i < triangles_list.size(); i++) + { + cv::Point3f V0 = mesh->getVertex(triangles_list[i][0]); + cv::Point3f V1 = mesh->getVertex(triangles_list[i][1]); + cv::Point3f V2 = mesh->getVertex(triangles_list[i][2]); + + Triangle T(i, V0, V1, V2); + + double out; + if(this->intersect_MollerTrumbore(R, T, &out)) + { + cv::Point3f tmp_pt = R.getP0() + out*R.getP1(); // P = O + t*D + intersections_list.push_back(tmp_pt); + } + } + + // If there are intersection, find the nearest one + if (!intersections_list.empty()) + { + point3d = get_nearest_3D_point(intersections_list, R.getP0()); + return true; + } + else + { + return false; + } +} + +// Möller–Trumbore intersection algorithm +bool PnPProblem::intersect_MollerTrumbore(Ray &Ray, Triangle &Triangle, double *out) +{ + const double EPSILON = 0.000001; + + cv::Point3f e1, e2; + cv::Point3f P, Q, T; + double det, inv_det, u, v; + double t; + + cv::Point3f V1 = Triangle.getV0(); // Triangle vertices + cv::Point3f V2 = Triangle.getV1(); + cv::Point3f V3 = Triangle.getV2(); + + cv::Point3f O = Ray.getP0(); // Ray origin + cv::Point3f D = Ray.getP1(); // Ray direction + + //Find vectors for two edges sharing V1 + e1 = SUB(V2, V1); + e2 = SUB(V3, V1); + + // Begin calculation determinant - also used to calculate U parameter + P = CROSS(D, e2); + + // If determinant is near zero, ray lie in plane of triangle + det = DOT(e1, P); + + //NOT CULLING + if(det > -EPSILON && det < EPSILON) return false; + inv_det = 1.f / det; + + //calculate distance from V1 to ray origin + T = SUB(O, V1); + + //Calculate u parameter and test bound + u = DOT(T, P) * inv_det; + + //The intersection lies outside of the triangle + if(u < 0.f || u > 1.f) return false; + + //Prepare to test v parameter + Q = CROSS(T, e1); + + //Calculate V parameter and test bound + v = DOT(D, Q) * inv_det; + + //The intersection lies outside of the triangle + if(v < 0.f || u + v > 1.f) return false; + + t = DOT(e2, Q) * inv_det; + + if(t > EPSILON) { //ray intersection + *out = t; + return true; + } + + // No hit, no win + return false; +} diff --git a/detectors/src/local2D/simple_ransac_detection/RobustMatcher.cpp b/detectors/src/local2D/simple_ransac_detection/RobustMatcher.cpp new file mode 100644 index 00000000..ca859ee8 --- /dev/null +++ b/detectors/src/local2D/simple_ransac_detection/RobustMatcher.cpp @@ -0,0 +1,328 @@ +/* + * RobustMatcher.cpp + * + * Created on: Jun 4, 2014 + * Author: eriba + */ + +#include "od/detectors/local2D/simple_ransac_detection/RobustMatcher.h" +#include "od/detectors/local2D/simple_ransac_detection/Utils.h" +#include "od/detectors/local2D/simple_ransac_detection/Model.h" +#include <time.h> + +#include <opencv2/features2d/features2d.hpp> +#include <opencv2/features2d.hpp> +#include <opencv2/xfeatures2d.hpp> +#include <opencv2/ml.hpp> + + +void convertToUnsignedSiftGPU(cv::Mat const &cv_des, vector<unsigned char> &siftgpu_des) +{ + int totdes = cv_des.rows * 128; + siftgpu_des.resize(cv_des.rows * 128); + + int desi = 0; + + for (int i = 0; i < cv_des.rows; i++){ + for (int j = 0; j < 128; j++, desi++) + siftgpu_des[desi] = (unsigned char)cv_des.at<float>(i,j); + } +} + +void convertToDmatch(int siftgpu_matches[][2], int num_match, vector<cv::DMatch> &cv_matches) +{ + cv_matches.resize(num_match); + for(int i = 0; i < num_match; ++i) + { + //How to get the feature matches: + //SiftGPU::SiftKeypoint & key1 = keys1[match_buf[i][0]]; + //SiftGPU::SiftKeypoint & key2 = keys2[match_buf[i][1]]; + //key1 in the first image matches with key2 in the second image + cv_matches[i].trainIdx = siftgpu_matches[i][0]; //Assigned first at init + cv_matches[i].queryIdx = siftgpu_matches[i][1]; + } +} + + +void RobustMatcher::instantiateMatcher(Model const &model, bool use_gpu) +{ + + if (use_gpu_ == true) { + + //####GPU + + //1. for SIFT + matcher_gpu_ = cv::cuda::DescriptorMatcher::createBFMatcher(cv::NORM_L2); + cv::cuda::GpuMat train_descriptors(model.get_descriptors()); + matcher_gpu_->add(std::vector<cv::cuda::GpuMat>(1, train_descriptors)); + cout << "GPU matcher instantiated ..." << endl; + + } + else + { + matcher_ = cv::makePtr<cv::FlannBasedMatcher>(); + } +} + +RobustMatcher::RobustMatcher(Model const &model, bool use_gpu, bool use_gpu_match) +{ + + use_gpu_ = use_gpu_match; + + + + // ORB is the default feature + ratio_ =0.8f; + detector_ = cv::ORB::create(); + extractor_ = cv::ORB::create(); + + + + instantiateMatcher(model, use_gpu_match); + +// // BruteFroce matcher with Norm Hamming is the default matcher +// //matcher_ = cv::makePtr<cv::BFMatcher>((int)cv::NORM_HAMMING, false); +// //matcher_ = cv::makePtr<cv::BFMatcher>((int)cv::NORM_L2, false); +// +// +// //####################TEMPORARY CODE BELOW!!!!!!!!!!!!!!!!!!!!!!!! +// +// cv::Ptr<cv::flann::IndexParams> indexParams = cv::makePtr<cv::flann::LshIndexParams>(6, 12, 1); // instantiate LSH index parameters +// cv::Ptr<cv::flann::SearchParams> searchParams = cv::makePtr<cv::flann::SearchParams>(50); // instantiate flann search parameters +// // instantiate FlannBased matcher +// //Ptr<DescriptorMatcher> matcher = makePtr<FlannBasedMatcher>(indexParams, searchParams); +// //matcher_ = cv::makePtr<cv::FlannBasedMatcher>(indexParams, searchParams); +// matcher_ = cv::makePtr<cv::FlannBasedMatcher>(); + +} + +RobustMatcher::~RobustMatcher() +{ + // TODO Auto-generated destructor stub +} + +void RobustMatcher::computeKeyPoints( const cv::Mat& image, std::vector<cv::KeyPoint>& keypoints) +{ + detector_->detect(image, keypoints); +} + +void RobustMatcher::computeDescriptors( const cv::Mat& image, std::vector<cv::KeyPoint>& keypoints, cv::Mat& descriptors) +{ + extractor_->compute(image, keypoints, descriptors); +} + +int RobustMatcher::ratioTest(std::vector<std::vector<cv::DMatch> > &matches) +{ + int removed = 0; + // for all matches + for ( std::vector<std::vector<cv::DMatch> >::iterator + matchIterator= matches.begin(); matchIterator!= matches.end(); ++matchIterator) + { + // if 2 NN has been identified + if (matchIterator->size() > 1) + { + // check distance ratio + if ((*matchIterator)[0].distance / (*matchIterator)[1].distance > ratio_) + { + matchIterator->clear(); // remove match + removed++; + } + } + else + { // does not have 2 neighbours + matchIterator->clear(); // remove match + removed++; + } + } + return removed; +} + +void get_good_matches(const std::vector<std::vector<cv::DMatch> >& matches, std::vector<cv::DMatch>& goodMatches) +{ + for (std::vector<std::vector<cv::DMatch> >::const_iterator + matchIterator1 = matches.begin(); matchIterator1 != matches.end(); ++matchIterator1) + { + // ignore deleted matches + if (matchIterator1->empty() || matchIterator1->size() < 2) + continue; + if ((*matchIterator1)[0].distance < (*matchIterator1)[1].distance) + goodMatches.push_back((*matchIterator1)[0]); + } + +} + +void RobustMatcher::symmetryTest( const std::vector<std::vector<cv::DMatch> >& matches1, + const std::vector<std::vector<cv::DMatch> >& matches2, + std::vector<cv::DMatch>& symMatches ) +{ + + // for all matches image 1 -> image 2 + for (std::vector<std::vector<cv::DMatch> >::const_iterator + matchIterator1 = matches1.begin(); matchIterator1 != matches1.end(); ++matchIterator1) + { + + // ignore deleted matches + if (matchIterator1->empty() || matchIterator1->size() < 2) + continue; + + // for all matches image 2 -> image 1 + for (std::vector<std::vector<cv::DMatch> >::const_iterator + matchIterator2 = matches2.begin(); matchIterator2 != matches2.end(); ++matchIterator2) + { + // ignore deleted matches + if (matchIterator2->empty() || matchIterator2->size() < 2) + continue; + + // Match symmetry test + if ((*matchIterator1)[0].queryIdx == + (*matchIterator2)[0].trainIdx && + (*matchIterator2)[0].queryIdx == + (*matchIterator1)[0].trainIdx) + { + // add symmetrical match + symMatches.push_back( + cv::DMatch((*matchIterator1)[0].queryIdx, + (*matchIterator1)[0].trainIdx, + (*matchIterator1)[0].distance)); + break; // next match in image 1 -> image 2 + } + } + } + +} + +void RobustMatcher::match(const cv::Mat & descriptors_frame, const cv::Mat &descriptors_model, std::vector<cv::DMatch>& good_matches) +{ + std::vector<std::vector<cv::DMatch> > matches; + + if (use_gpu_) + { + matcher_gpu_->knnMatch(cv::cuda::GpuMat(descriptors_frame), matches, 2); // return 2 nearest neighbours + ratioTest(matches); + for ( std::vector<std::vector<cv::DMatch> >::iterator + matchIterator= matches.begin(); matchIterator!= matches.end(); ++matchIterator) + { + if (!matchIterator->empty()) good_matches.push_back((*matchIterator)[0]); + } + } + else + { + matcher_->knnMatch(descriptors_frame, descriptors_model, matches, 2); // return 2 nearest neighbours + ratioTest(matches); + // 4. Fill good matches container + for ( std::vector<std::vector<cv::DMatch> >::iterator + matchIterator= matches.begin(); matchIterator!= matches.end(); ++matchIterator) + { + if (!matchIterator->empty()) good_matches.push_back((*matchIterator)[0]); + } + + } +} + +void RobustMatcher::matchNormalized(cv::Mat & descriptors_frame, cv::Mat &descriptors_model, std::vector<cv::DMatch>& good_matches) +{ + od::normL2(descriptors_frame); od::normL2(descriptors_model); + match(descriptors_frame, descriptors_model, good_matches); +} + +void RobustMatcher::robustMatch( const cv::Mat& frame, std::vector<cv::DMatch>& good_matches, + std::vector<cv::KeyPoint>& keypoints_frame, const cv::Mat& descriptors_model ) +{ + // 1a. Detection of the ORB features + //this->computeKeyPoints(frame, keypoints_frame); + + // 1b. Extraction of the ORB descriptors + cv::Mat descriptors_frame; + //this->computeDescriptors(frame, keypoints_frame, descriptors_frame); + + featureDetector_->computeKeypointsAndDescriptors(frame, descriptors_frame, keypoints_frame); + //cout << "After Restacking: \n" << descriptors_frame << endl; + // 2. Match the two image descriptors + std::vector<std::vector<cv::DMatch> > matches12, matches21; + + // 2a. From image 1 to image 2 + matcher_->knnMatch(descriptors_frame, descriptors_model, matches12, 2); // return 2 nearest neighbours + + // 2b. From image 2 to image 1 + matcher_->knnMatch(descriptors_model, descriptors_frame, matches21, 2); // return 2 nearest neighbours + + + // 3. Remove matches for which NN ratio is > than threshold + // clean image 1 -> image 2 matches + ratioTest(matches12); + // clean image 2 -> image 1 matches + ratioTest(matches21); + + //get_good_matches(matches12, good_matches); + + // 4. Remove non-symmetrical matches + symmetryTest(matches12, matches21, good_matches); + + +} + +void RobustMatcher::findFeatureAndMatch( const cv::Mat& frame, std::vector<cv::DMatch>& good_matches, + std::vector<cv::KeyPoint>& keypoints_frame, + const cv::Mat& descriptors_model ) +{ + good_matches.clear(); + + // 1b. Extraction of the ORB descriptors + cv::Mat descriptors_frame; + featureDetector_->computeKeypointsAndDescriptors(frame, descriptors_frame, keypoints_frame); + + cout << "df " << descriptors_model.rows << endl; + cout << "kf" << descriptors_frame.rows << endl; + + match(descriptors_frame, descriptors_model, good_matches); + +} + + +void RobustMatcher::fastRobustMatch( const cv::Mat& frame, std::vector<cv::DMatch>& good_matches, + std::vector<cv::KeyPoint>& keypoints_frame, + const cv::Mat& descriptors_model ) +{ + good_matches.clear(); + + cv::Mat d_f, d_m; + // 1a. Detection of the ORB features + //this->computeKeyPoints(frame, keypoints_frame); + + // 1b. Extraction of the ORB descriptors + cv::Mat descriptors_frame; + //this->computeDescriptors(frame, keypoints_frame, descriptors_frame); + + featureDetector_->computeKeypointsAndDescriptors(frame, descriptors_frame, keypoints_frame); + + if (descriptors_frame.type() != CV_32F) { + descriptors_frame.convertTo(d_f, CV_32F); + } + else { + d_f = descriptors_frame; + } + if (descriptors_model.type() != CV_32F) { + descriptors_model.convertTo(d_f, CV_32F); + } + else + { + d_m = descriptors_model; + } + + // 2. Match the two image descriptors + std::vector<std::vector<cv::DMatch> > matches; + matcher_->knnMatch(d_f, d_m, matches, 2); + + // 3. Remove matches for which NN ratio is > than threshold + ratioTest(matches); + + // 4. Fill good matches container + for ( std::vector<std::vector<cv::DMatch> >::iterator + matchIterator= matches.begin(); matchIterator!= matches.end(); ++matchIterator) + { + if (!matchIterator->empty()) good_matches.push_back((*matchIterator)[0]); + } + +} + + diff --git a/detectors/src/local2D/simple_ransac_detection/Utils.cpp b/detectors/src/local2D/simple_ransac_detection/Utils.cpp new file mode 100644 index 00000000..ca3277f3 --- /dev/null +++ b/detectors/src/local2D/simple_ransac_detection/Utils.cpp @@ -0,0 +1,341 @@ +/* + * Utils.cpp + * + * Created on: Mar 28, 2014 + * Author: Edgar Riba + */ + +#include <iostream> + +#include "od/detectors/local2D/simple_ransac_detection/PnPProblem.h" +#include "od/detectors/local2D/simple_ransac_detection/ModelRegistration.h" +#include "od/detectors/local2D/simple_ransac_detection/Utils.h" +#include "od/detectors/local2D/simple_ransac_detection/Model.h" + +#include <opencv2/imgproc/imgproc.hpp> +#include <opencv2/calib3d/calib3d.hpp> + +// For text +int fontFace = cv::FONT_ITALIC; +double fontScale = 0.75; +int thickness_font = 2; + +// For circles +int lineType = 16; +int radius = 1; +double thickness_circ = -1; + + +using namespace std; + + + +void viewImage(cv::Mat image, vector<cv::KeyPoint> keypoints) +{ + cv::Mat outimage = image; + cv::drawKeypoints(image, keypoints, outimage, cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS); + + cv::namedWindow( "Keypoints", cv::WINDOW_NORMAL ); // Create a window for display. + cv::resizeWindow("Keypoints", 800, 600); + + cv::imshow("Keypoints", outimage); + cv::waitKey(0); +} + +// Draw a text with the question point +void drawQuestion(cv::Mat image, cv::Point3f point, cv::Scalar color) +{ + std::string x = IntToString((int)point.x); + std::string y = IntToString((int)point.y); + std::string z = IntToString((int)point.z); + + std::string text = " Where is point (" + x + "," + y + "," + z + ") ?"; + cv::putText(image, text, cv::Point(25,50), fontFace, fontScale, color, thickness_font, 8); +} + +// Draw a text with the number of entered points +void drawText(cv::Mat image, std::string text, cv::Scalar color) +{ + cv::putText(image, text, cv::Point(25,50), fontFace, fontScale, color, thickness_font, 8); +} + +// Draw a text with the number of entered points +void drawText2(cv::Mat image, std::string text, cv::Scalar color) +{ + cv::putText(image, text, cv::Point(25,75), fontFace, fontScale, color, thickness_font, 8); +} + +// Draw a text with the frame ratio +void drawFPS(cv::Mat image, double fps, cv::Scalar color) +{ + std::string fps_str = IntToString((int)fps); + std::string text = fps_str + " FPS"; + cv::putText(image, text, cv::Point(500,50), fontFace, fontScale, color, thickness_font, 8); +} + +// Draw a text with the frame ratio +void drawConfidence(cv::Mat image, double confidence, cv::Scalar color) +{ + std::string conf_str = IntToString((int)confidence); + std::string text = conf_str + " %"; + cv::putText(image, text, cv::Point(500,75), fontFace, fontScale, color, thickness_font, 8); +} + +// Draw a text with the number of entered points +void drawCounter(cv::Mat image, int n, int n_max, cv::Scalar color) +{ + std::string n_str = IntToString(n); + std::string n_max_str = IntToString(n_max); + std::string text = n_str + " of " + n_max_str + " points"; + cv::putText(image, text, cv::Point(500,50), fontFace, fontScale, color, thickness_font, 8); +} + +// Draw the points and the coordinates +void drawPoints(cv::Mat image, std::vector<cv::Point2f> &list_points_2d, std::vector<cv::Point3f> &list_points_3d, cv::Scalar color) +{ + for (unsigned int i = 0; i < list_points_2d.size(); ++i) + { + cv::Point2f point_2d = list_points_2d[i]; + cv::Point3f point_3d = list_points_3d[i]; + + // Draw Selected points + cv::circle(image, point_2d, radius, color, -1, lineType ); + + std::string idx = IntToString(i+1); + std::string x = IntToString((int)point_3d.x); + std::string y = IntToString((int)point_3d.y); + std::string z = IntToString((int)point_3d.z); + std::string text = "P" + idx + " (" + x + "," + y + "," + z +")"; + + point_2d.x = point_2d.x + 10; + point_2d.y = point_2d.y - 10; + cv::putText(image, text, point_2d, fontFace, fontScale*0.5, color, thickness_font, 8); + } +} + +// Draw only the 2D points +void draw2DPoints(cv::Mat image, std::vector<cv::Point2f> &list_points, cv::Scalar color) +{ + for( size_t i = 0; i < list_points.size(); i++) + { + cv::Point2f point_2d = list_points[i]; + + // Draw Selected points + cv::circle(image, point_2d, radius, color, -1, lineType ); + } +} + +// Draw an arrow into the image +void drawArrow(cv::Mat image, cv::Point2i p, cv::Point2i q, cv::Scalar color, int arrowMagnitude, int thickness, int line_type, int shift) +{ + //Draw the principle line + cv::line(image, p, q, color, thickness, line_type, shift); + const double PI = CV_PI; + //compute the angle alpha + double angle = atan2((double)p.y-q.y, (double)p.x-q.x); + //compute the coordinates of the first segment + p.x = (int) ( q.x + arrowMagnitude * cos(angle + PI/4)); + p.y = (int) ( q.y + arrowMagnitude * sin(angle + PI/4)); + //Draw the first segment + cv::line(image, p, q, color, thickness, line_type, shift); + //compute the coordinates of the second segment + p.x = (int) ( q.x + arrowMagnitude * cos(angle - PI/4)); + p.y = (int) ( q.y + arrowMagnitude * sin(angle - PI/4)); + //Draw the second segment + cv::line(image, p, q, color, thickness, line_type, shift); +} + +// Draw the 3D coordinate axes +void draw3DCoordinateAxes(cv::Mat image, const std::vector<cv::Point2f> &list_points2d) +{ + cv::Scalar red(0, 0, 255); + cv::Scalar green(0,255,0); + cv::Scalar blue(255,0,0); + cv::Scalar black(0,0,0); + + cv::Point2i origin = list_points2d[0]; + cv::Point2i pointX = list_points2d[1]; + cv::Point2i pointY = list_points2d[2]; + cv::Point2i pointZ = list_points2d[3]; + + drawArrow(image, origin, pointX, red, 9, 2); + drawArrow(image, origin, pointY, blue, 9, 2); + drawArrow(image, origin, pointZ, green, 9, 2); + cv::circle(image, origin, radius/2, black, -1, lineType ); + +} + +// Draw the object mesh +void drawObjectMesh(cv::Mat image, const Mesh *mesh, PnPProblem *pnpProblem, cv::Scalar color) +{ + std::vector<std::vector<int> > list_triangles = mesh->getTrianglesList(); + int skip = 1; +// if (pnpProblem->get_A_matrix().at<float>(0,2) < 300 && list_triangles.size() > 1000) +// skip = 10; + + for( size_t i = 0; i < list_triangles.size(); i+= skip) + { + std::vector<int> tmp_triangle = list_triangles.at(i); + + cv::Point3f point_3d_0 = mesh->getVertex(tmp_triangle[0]); + cv::Point3f point_3d_1 = mesh->getVertex(tmp_triangle[1]); + cv::Point3f point_3d_2 = mesh->getVertex(tmp_triangle[2]); + + //std::cout << point_3d_0 << std::endl << point_3d_1 << std::endl << point_3d_2 << std::endl << std::endl; + + cv::Point2f point_2d_0 = pnpProblem->backproject3DPoint(point_3d_0); + cv::Point2f point_2d_1 = pnpProblem->backproject3DPoint(point_3d_1); + cv::Point2f point_2d_2 = pnpProblem->backproject3DPoint(point_3d_2); + + cv::line(image, point_2d_0, point_2d_1, color, 1); + cv::line(image, point_2d_1, point_2d_2, color, 1); + cv::line(image, point_2d_2, point_2d_0, color, 1); + } +} + +void drawObjectMesh1(cv::Mat image, const Mesh *mesh, const Model *model, PnPProblem *pnpProblem, cv::Scalar color) +{ + std::vector<cv::Point2f> imgpoints; + cv::projectPoints(model->get_points3d(), pnpProblem->get_R_vect(), pnpProblem->get_t_matrix(), pnpProblem->get_A_matrix(), pnpProblem->get_dist_coef(), imgpoints); + draw2DPoints(image, imgpoints, color); + +} + +void drawModel(cv::Mat image, const Model *model, PnPProblem *pnpProblem, cv::Scalar color) +{ + std::vector<cv::Point2f> imgpoints; + cv::projectPoints(model->get_points3d(), pnpProblem->get_R_vect(), pnpProblem->get_t_matrix(), pnpProblem->get_A_matrix(), pnpProblem->get_dist_coef(), imgpoints); + draw2DPoints(image, imgpoints, color); + +} + +void drawModel(cv::Mat image, const Model *model, cv::Mat R_vect, cv::Mat t_mat, cv::Mat A_mat, cv::Mat dist, cv::Scalar color) +{ + std::vector<cv::Point2f> imgpoints; + cv::projectPoints(model->get_points3d(), R_vect, t_mat, A_mat, dist, imgpoints); + draw2DPoints(image, imgpoints, color); + +} + + +// Computes the norm of the translation error +double get_translation_error(const cv::Mat &t_true, const cv::Mat &t) +{ + return cv::norm( t_true - t ); +} + +// Computes the norm of the rotation error +double get_rotation_error(const cv::Mat &R_true, const cv::Mat &R) +{ + cv::Mat error_vec, error_mat; + error_mat = R_true * cv::Mat(R.inv()).mul(-1); + cv::Rodrigues(error_mat, error_vec); + + return cv::norm(error_vec); +} + +// Converts a given Rotation Matrix to Euler angles +cv::Mat rot2euler(const cv::Mat & rotationMatrix) +{ + cv::Mat euler(3,1,CV_64F); + + double m00 = rotationMatrix.at<double>(0,0); + double m02 = rotationMatrix.at<double>(0,2); + double m10 = rotationMatrix.at<double>(1,0); + double m11 = rotationMatrix.at<double>(1,1); + double m12 = rotationMatrix.at<double>(1,2); + double m20 = rotationMatrix.at<double>(2,0); + double m22 = rotationMatrix.at<double>(2,2); + + double x, y, z; + + // Assuming the angles are in radians. + if (m10 > 0.998) { // singularity at north pole + x = 0; + y = CV_PI/2; + z = atan2(m02,m22); + } + else if (m10 < -0.998) { // singularity at south pole + x = 0; + y = -CV_PI/2; + z = atan2(m02,m22); + } + else + { + x = atan2(-m12,m11); + y = asin(m10); + z = atan2(-m20,m00); + } + + euler.at<double>(0) = x; + euler.at<double>(1) = y; + euler.at<double>(2) = z; + + return euler; +} + +// Converts a given Euler angles to Rotation Matrix +cv::Mat euler2rot(const cv::Mat & euler) +{ + cv::Mat rotationMatrix(3,3,CV_64F); + + double x = euler.at<double>(0); + double y = euler.at<double>(1); + double z = euler.at<double>(2); + + // Assuming the angles are in radians. + double ch = cos(z); + double sh = sin(z); + double ca = cos(y); + double sa = sin(y); + double cb = cos(x); + double sb = sin(x); + + double m00, m01, m02, m10, m11, m12, m20, m21, m22; + + m00 = ch * ca; + m01 = sh*sb - ch*sa*cb; + m02 = ch*sa*sb + sh*cb; + m10 = sa; + m11 = ca*cb; + m12 = -ca*sb; + m20 = -sh*ca; + m21 = sh*sa*cb + ch*sb; + m22 = -sh*sa*sb + ch*cb; + + rotationMatrix.at<double>(0,0) = m00; + rotationMatrix.at<double>(0,1) = m01; + rotationMatrix.at<double>(0,2) = m02; + rotationMatrix.at<double>(1,0) = m10; + rotationMatrix.at<double>(1,1) = m11; + rotationMatrix.at<double>(1,2) = m12; + rotationMatrix.at<double>(2,0) = m20; + rotationMatrix.at<double>(2,1) = m21; + rotationMatrix.at<double>(2,2) = m22; + + return rotationMatrix; +} + +// Converts a given string to an integer +int StringToInt ( const std::string &Text ) +{ + std::istringstream ss(Text); + int result; + return ss >> result ? result : 0; +} + +// Converts a given float to a string +std::string FloatToString ( float Number ) +{ + std::ostringstream ss; + ss << Number; + return ss.str(); +} + +// Converts a given integer to a string +std::string IntToString ( int Number ) +{ + std::ostringstream ss; + ss << Number; + return ss.str(); +} From 90291fc5ed4bf9049ef352ebd8def5f9bfcfab4a Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Thu, 26 May 2016 14:16:51 +0200 Subject: [PATCH 11/79] adds correct install and fixes README --- README.md | 1 + cmake/od_macros.cmake | 15 ++------------- cmake/od_targets.cmake | 2 +- common/CMakeLists.txt | 5 ++--- detectors/CMakeLists.txt | 2 ++ detectors/src/global2D/CMakeLists.txt | 3 +-- detectors/src/global3D/CMakeLists.txt | 3 +-- detectors/src/local2D/CMakeLists.txt | 3 +-- detectors/src/misc/CMakeLists.txt | 2 +- 9 files changed, 12 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 9451c452..775b22fe 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ OpenDetection ============= OpenDetection is a standalone open source project for object detection and recognition in images and 3D point clouds. +To clone the dependencies (SiftGPU and pugixml) clone with --recursive flag or pull submodules with ``git submodule init && git submodule update`` . Website - http://opendetection.com or http://krips89.github.io/opendetection_docs diff --git a/cmake/od_macros.cmake b/cmake/od_macros.cmake index 19f59354..c5530df4 100644 --- a/cmake/od_macros.cmake +++ b/cmake/od_macros.cmake @@ -36,12 +36,10 @@ macro(OD_ADD_LIBRARY_ALL _name ) set(options) set(oneValueArgs) - set(multiValueArgs SRCS INCS) + set(multiValueArgs SRCS) cmake_parse_arguments(OD_ADD_LIBRARY_ALL "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - #message(input to od_add_library: ${OD_LIB_TYPE} ${OD_ADD_LIBRARY_ALL_SRCS} ${OD_ADD_LIBRARY_ALL_INCS}) - - add_library(${lib_name} ${OD_LIB_TYPE} ${OD_ADD_LIBRARY_ALL_SRCS} ${OD_ADD_LIBRARY_ALL_INCS}) + add_library(${lib_name} ${OD_LIB_TYPE} ${OD_ADD_LIBRARY_ALL_SRCS}) # allways link libs: target_link_libraries(${lib_name} ${Boost_LIBRARIES}) @@ -52,21 +50,12 @@ macro(OD_ADD_LIBRARY_ALL _name ) SOVERSION ${OD_MAJOR_VERSION}.${OD_MINOR_VERSION} ) - # Install library install(TARGETS ${lib_name} RUNTIME DESTINATION ${OD_INSTALL_RUNTIME_DIR} COMPONENT ${lib_name} LIBRARY DESTINATION ${OD_INSTALL_LIBRARY_DIR} COMPONENT ${lib_name} ARCHIVE DESTINATION ${OD_INSTALL_ARCHIVE_DIR} COMPONENT ${lib_name}) - message(${_name}) - message("includes to od_add_library: " ${OD_ADD_LIBRARY_ALL_INCS}) - message("include dir: " ${OD_INSTALL_INCLUDE_DIR}) - message("include dir: " ${OD_INSTALL_DOXYGEN_DIR}) - #install includes - install(FILES ${_OD_ADD_LIBRARY_ALL_INCS} - DESTINATION ${OD_INSTALL_INCLUDE_DIR}/${_name} - COMPONENT ${lib_name}) endmacro(OD_ADD_LIBRARY_ALL) diff --git a/cmake/od_targets.cmake b/cmake/od_targets.cmake index c9d81e2a..48d51cf7 100644 --- a/cmake/od_targets.cmake +++ b/cmake/od_targets.cmake @@ -13,7 +13,7 @@ if(NOT OD_INSTALL_ARCHIVE_DIR) endif() if(NOT OD_INSTALL_INCLUDE_DIR) - set(OD_INSTALL_INCLUDE_DIR include/od-${OD_VERSION}/od) + set(OD_INSTALL_INCLUDE_DIR include/od-${OD_VERSION}) endif() if(NOT OD_INSTALL_DATA_DIR) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index b483f979..8786d660 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -4,7 +4,6 @@ set(SUBSYS_DESC "The core module of OpenDetection having the pipeline logic") set(SUBSYS_DEPS ${OpenCV_LIBS} ${PCL_LIBRARIES}) set(COMMON_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) - set(COMMON_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include PARENT_SCOPE) if(WITH_GPU) @@ -39,8 +38,8 @@ if(build) include_directories(${COMMON_INCLUDE_DIR}) - OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${SOURCES} INCS ${INCLUDES} ${IMPL_INCLUDES}) - install(FILES ${INCLUDES} DESTINATION ${OD_INSTALL_INCLUDE_DIR}/${SUBSYS_NAME} COMPONENT ${LIB_NAME}) + OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${SOURCES}) + install(DIRECTORY ${COMMON_INCLUDE_DIR}/od DESTINATION ${OD_INSTALL_INCLUDE_DIR} COMPONENT ${LIB_NAME}) if(SUBSYS_DEPS) target_link_libraries("${LIB_NAME}" ${SUBSYS_DEPS}) diff --git a/detectors/CMakeLists.txt b/detectors/CMakeLists.txt index 6e0093f5..92701098 100644 --- a/detectors/CMakeLists.txt +++ b/detectors/CMakeLists.txt @@ -10,3 +10,5 @@ add_subdirectory("src/local2D") add_subdirectory("src/global2D") add_subdirectory("src/global3D") add_subdirectory("src/misc") + +install(DIRECTORY ${DETECTORS_INCLUDE_DIR}/od DESTINATION ${OD_INSTALL_INCLUDE_DIR}) \ No newline at end of file diff --git a/detectors/src/global2D/CMakeLists.txt b/detectors/src/global2D/CMakeLists.txt index d6b85eac..14637d11 100644 --- a/detectors/src/global2D/CMakeLists.txt +++ b/detectors/src/global2D/CMakeLists.txt @@ -41,8 +41,7 @@ if(build) include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) - OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${SOURCES} INCS ${INCLUDES} ${impl_incs}) - install(FILES ${INCLUDES} DESTINATION ${OD_INSTALL_INCLUDE_DIR}/${SUBSYS_NAME} COMPONENT ${LIB_NAME} ) + OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${SOURCES}) if(WITH_SVMLIGHT) if(SUBSYS_DEPS) diff --git a/detectors/src/global3D/CMakeLists.txt b/detectors/src/global3D/CMakeLists.txt index 5367c979..2c8d8fbb 100644 --- a/detectors/src/global3D/CMakeLists.txt +++ b/detectors/src/global3D/CMakeLists.txt @@ -29,8 +29,7 @@ if(build) include_directories(${DETECTORS_IMPL_DIR}) include_directories(${COMMON_INCLUDE_DIR}) - OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${SOURCES} INCS ${INCLUDES} ${IMPL_INCLUDES}) - install(FILES ${INCLUDES} DESTINATION ${OD_INSTALL_INCLUDE_DIR}/${SUBSYS_NAME} COMPONENT ${LIB_NAME}) + OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${SOURCES}) if(SUBSYS_DEPS) target_link_libraries("${LIB_NAME}" ${SUBSYS_DEPS}) diff --git a/detectors/src/local2D/CMakeLists.txt b/detectors/src/local2D/CMakeLists.txt index 17a2ba39..c95d0c7d 100644 --- a/detectors/src/local2D/CMakeLists.txt +++ b/detectors/src/local2D/CMakeLists.txt @@ -49,8 +49,7 @@ if(build) include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) - OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${SOURCES} INCS ${INCLUDES} ${IMPL_INCLUDES}) - install(FILES ${INCLUDES} DESTINATION ${OD_INSTALL_INCLUDE_DIR}/${SUBSYS_NAME} COMPONENT ${LIB_NAME}) + OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${SOURCES}) if(SUBSYS_DEPS) target_link_libraries("${LIB_NAME}" ${SUBSYS_DEPS} pugixml) diff --git a/detectors/src/misc/CMakeLists.txt b/detectors/src/misc/CMakeLists.txt index d46b0f77..6dd69dc1 100644 --- a/detectors/src/misc/CMakeLists.txt +++ b/detectors/src/misc/CMakeLists.txt @@ -24,7 +24,7 @@ if(build) include_directories(${DETECTORS_IMPL_DIR}) include_directories(${SIMPLE_RANSAC_INCLUDE_DIR}) - OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${SOURCES} INCS ${INCLUDES} ${IMPL_INCLUDES}) + OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${SOURCES}) if(SUBSYS_DEPS) target_link_libraries("${LIB_NAME}" ${SUBSYS_DEPS}) From ffdd3096b53bd62b27bd6c0e97148cd768778df1 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Thu, 26 May 2016 14:42:31 +0200 Subject: [PATCH 12/79] removes include files from cmake files --- common/CMakeLists.txt | 13 ------------- detectors/src/global2D/CMakeLists.txt | 12 +----------- detectors/src/global3D/CMakeLists.txt | 8 -------- detectors/src/local2D/CMakeLists.txt | 14 -------------- examples/apps/CMakeLists.txt | 1 - 5 files changed, 1 insertion(+), 47 deletions(-) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 8786d660..85c33f13 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -14,19 +14,6 @@ set(build TRUE) if(build) - set(INCLUDES - "include/od/common/pipeline/ObjectDetector.h" - "include/od/common/pipeline/ODAlgorithmBase.h" - "include/od/common/pipeline/ODScene.h" - "include/od/common/pipeline/ODDetection.h" - "include/od/common/pipeline/ODTrainer.h" - "include/od/common/pipeline/ODDetector.h" - "include/od/common/utils/ODFrameGenerator.h" - "include/od/common/utils/utils.h" - "include/od/common/utils/ODFeatureDetector2D.h" - "include/od/common/bindings/svmlight.h" - ) - set(IMPL_INCLUDES "") set(SOURCES diff --git a/detectors/src/global2D/CMakeLists.txt b/detectors/src/global2D/CMakeLists.txt index 14637d11..8c192591 100644 --- a/detectors/src/global2D/CMakeLists.txt +++ b/detectors/src/global2D/CMakeLists.txt @@ -19,23 +19,13 @@ if(build) "detection/ODCascadeDetector.cpp" "training/ODHOGTrainer.cpp" ) - set(INCLUDES - "${DETECTORS_INCLUDE_DIR}/od/detectors/global2D/ODFaceRecognizer.h" - "${DETECTORS_INCLUDE_DIR}/od/detectors/global2D/detection/ODHOGDetector.h" - "${DETECTORS_INCLUDE_DIR}/od/detectors/global2D/detection/ODCascadeDetector.h" - "${DETECTORS_INCLUDE_DIR}/od/detectors/global2D/training/ODHOGTrainer.h" - ) + else() set(SOURCES "ODFaceRecognizer.cpp" "detection/ODCascadeDetector.cpp" ) - set(INCLUDES - "${DETECTORS_INCLUDE_DIR}/od/detectors/global2D/ODFaceRecognizer.h" - "${DETECTORS_INCLUDE_DIR}/od/detectors/global2D/detection/ODCascadeDetector.h" - ) - endif() include_directories(${DETECTORS_INCLUDE_DIR}) diff --git a/detectors/src/global3D/CMakeLists.txt b/detectors/src/global3D/CMakeLists.txt index 2c8d8fbb..6520d095 100644 --- a/detectors/src/global3D/CMakeLists.txt +++ b/detectors/src/global3D/CMakeLists.txt @@ -4,17 +4,9 @@ set(SUBSYS_DESC "The global detector for point cloouds") set(SUBSYS_DEPS od_common ${PCL_LIBRARIES} ${VTK_LIBRARIES} ${OpenCV_LIBS} siftgpu) set(build TRUE) -#PCL_SUBSYS_OPTION(build "${SUBSYS_NAME}" "${SUBSYS_DESC}" ON) -#PCL_SUBSYS_DEPEND(build "${SUBSYS_NAME}" DEPS ${SUBSYS_DEPS}) if(build) - set(INCLUDES - "${DETECTORS_INCLUDE_DIR}/od/detectors/global3D/ODPointCloudGlobalMatching.h" - "${DETECTORS_INCLUDE_DIR}/od/detectors/global3D/detection/ODCADDetector3DGlobal.h" - "${DETECTORS_INCLUDE_DIR}/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h" - ) - set(IMPL_INCLUDES "${DETECTORS_IMPL_DIR}/od/global3D/detection/ODCADDetector3DGlobal.hpp" ) diff --git a/detectors/src/local2D/CMakeLists.txt b/detectors/src/local2D/CMakeLists.txt index c95d0c7d..6bb957b5 100644 --- a/detectors/src/local2D/CMakeLists.txt +++ b/detectors/src/local2D/CMakeLists.txt @@ -15,20 +15,6 @@ set(build TRUE) if(build) - set(INCLUDES - "${DETECTORS_INCLUDE_DIR}/od/detectors/local2D/ODImageLocalMatching.h" - "${DETECTORS_INCLUDE_DIR}/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h" - "${DETECTORS_INCLUDE_DIR}/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h" - "${DETECTORS_INCLUDE_DIR}/od/detectors/local2D/simple_ransac_detection/CsvReader.h" - "${DETECTORS_INCLUDE_DIR}/od/detectors/local2D/simple_ransac_detection/CsvWriter.h" - "${DETECTORS_INCLUDE_DIR}/od/detectors/local2D/simple_ransac_detection/Mesh.h" - "${DETECTORS_INCLUDE_DIR}/od/detectors/local2D/simple_ransac_detection/Model.h" - "${DETECTORS_INCLUDE_DIR}/od/detectors/local2D/simple_ransac_detection/ModelRegistration.h" - "${DETECTORS_INCLUDE_DIR}/od/detectors/local2D/simple_ransac_detection/PnPProblem.h" - "${DETECTORS_INCLUDE_DIR}/od/detectors/local2D/simple_ransac_detection/RobustMatcher.h" - "${DETECTORS_INCLUDE_DIR}/od/detectors/local2D/simple_ransac_detection/Utils.h" - ) - set(IMPL_INCLUDES "") diff --git a/examples/apps/CMakeLists.txt b/examples/apps/CMakeLists.txt index 5d1b45fa..ce070136 100644 --- a/examples/apps/CMakeLists.txt +++ b/examples/apps/CMakeLists.txt @@ -1,6 +1,5 @@ include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) -include_directories(${SIMPLE_RANSAC_INCLUDE_DIR}) OD_ADD_EXAMPLE(odcadrecog_test_single_model FILES cadrecog2D/od_test_single_db_single_model.cpp LINK_WITH od_common od_local_image_detector ) From ee5575c11940a0c4814291aac5e0cda76f3b0d0c Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Mon, 30 May 2016 15:02:49 +0200 Subject: [PATCH 13/79] starting to refactor code in common --- .gitignore | 1 - 3rdparty/CMakeLists.txt | 2 +- 3rdparty/pugixml_build/CMakeLists.txt | 1 - CMakeLists.txt | 6 +- .../{FindGLEW.cmake => FindGlew.cmake} | 0 cmake/modules/FindSphinx.cmake | 26 -- cmake/od_targets.cmake | 1 - cmake/od_version.cmake | 3 +- .../od/common/pipeline/ODAlgorithmBase.h | 11 +- .../include/od/common/pipeline/ODDetection.h | 10 +- .../include/od/common/pipeline/ODDetector.h | 32 +- common/include/od/common/pipeline/ODScene.h | 47 +- common/include/od/common/pipeline/ODTrainer.h | 14 +- .../od/common/pipeline/ObjectDetector.h | 32 +- .../od/common/utils/ODFeatureDetector2D.h | 59 +-- .../od/common/utils/ODFrameGenerator.h | 77 ++-- common/include/od/common/utils/utils.h | 333 +------------- common/src/utils/ODFeatureDetector2D.cpp | 100 ++-- common/src/utils/utils.cpp | 430 ++++++++++++------ .../od/detectors/global2D/ODFaceRecognizer.h | 2 +- .../global2D/detection/ODCascadeDetector.h | 11 +- .../detection/ODCADRecognizer2DLocal.h | 5 +- detectors/src/global2D/ODFaceRecognizer.cpp | 6 +- detectors/src/global3D/CMakeLists.txt | 1 - .../detection/ODCADRecognizer2DLocal.cpp | 8 +- .../ODCADRecogTrainerSnapshotBased.cpp | 24 +- .../gsoc2016_blog_giacomo.md | 20 +- examples/objectdetector/od_pc_global.cpp | 2 +- 28 files changed, 521 insertions(+), 743 deletions(-) rename cmake/modules/{FindGLEW.cmake => FindGlew.cmake} (100%) delete mode 100644 cmake/modules/FindSphinx.cmake diff --git a/.gitignore b/.gitignore index c390e7f9..016afb65 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ .idea/ build/ debug/ -3rdparty/svmlight/ *~ diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 1b07260c..678f5bed 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -1,4 +1,4 @@ -#add mandatory 3rd party builds +#add mandatory 3rdparty libraries add_subdirectory(pugixml_build) #if(WITH_SVMLIGHT) diff --git a/3rdparty/pugixml_build/CMakeLists.txt b/3rdparty/pugixml_build/CMakeLists.txt index 6dcf26e4..72973bfb 100644 --- a/3rdparty/pugixml_build/CMakeLists.txt +++ b/3rdparty/pugixml_build/CMakeLists.txt @@ -1,5 +1,4 @@ cmake_minimum_required(VERSION 2.8) Project(pugixml CXX) -set(CMAKE_BUILD_TYPE Release) add_library(pugixml SHARED ${CMAKE_SOURCE_DIR}/3rdparty/pugixml/src/pugixml.cpp) diff --git a/CMakeLists.txt b/CMakeLists.txt index a152213e..cd9d9b10 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 2.8) project(OpenDetection) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") # Initialize variables set(OD_SOURCE_DIR ${OpenDetection_SOURCE_DIR}) @@ -13,7 +13,7 @@ set(CMAKE_MODULE_PATH ${OD_CMAKE_DIR}/modules) include(${OD_CMAKE_DIR}/od_version.cmake) set(OD_VERSION "${OD_MAJOR_VERSION}.${OD_MINOR_VERSION}") set(OD_VERSION_DETAILED "${OD_MAJOR_VERSION}.${OD_MINOR_VERSION}.${OD_BUILD_VERSION}") -configure_file ("${OD_CMAKE_DIR}/od_config.h.in" "${OD_BINARY_DIR}/generated/od_config.h") +configure_file("${OD_CMAKE_DIR}/od_config.h.in" "${OD_BINARY_DIR}/generated/od_config.h") # Set up macros and configurations include(${OD_CMAKE_DIR}/od_targets.cmake) @@ -27,7 +27,7 @@ option(WITH_SVMLIGHT "Build SVM Light" OFF) option(WITH_EXAMPLES "Build examples" OFF) # Set modules -set(OD_MODULES_NAMES 3rdparty common doc detectors examples) +set(OD_MODULES_NAMES 3rdparty common detectors doc examples) set(OD_MODULES_DIRS ${OD_MODULES_NAMES}) # Add modules diff --git a/cmake/modules/FindGLEW.cmake b/cmake/modules/FindGlew.cmake similarity index 100% rename from cmake/modules/FindGLEW.cmake rename to cmake/modules/FindGlew.cmake diff --git a/cmake/modules/FindSphinx.cmake b/cmake/modules/FindSphinx.cmake deleted file mode 100644 index 33bd40b1..00000000 --- a/cmake/modules/FindSphinx.cmake +++ /dev/null @@ -1,26 +0,0 @@ -############################################################################### -# Find Sphinx -# -# This sets the following variables: -# SPHINX_FOUND - True if Sphinx was found. -# SPHINX_EXECUTABLE - Sphinx-build executable - -find_package(PkgConfig QUIET) -pkg_check_modules(PC_SPHINX sphinx-build) - -find_package(PythonInterp) - -if(PYTHONINTERP_FOUND) - get_filename_component(PYTHON_DIR "${PYTHON_EXECUTABLE}" PATH) -endif(PYTHONINTERP_FOUND) - -find_program(SPHINX_EXECUTABLE NAMES sphinx-build - HINTS ${PC_SPHINX_EXECUTABLE} $ENV{SPHINX_DIR} ${PYTHON_DIR}/Scripts - PATH_SUFFIXES bin - DOC "Sphinx documentation generator" -) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Sphinx DEFAULT_MSG SPHINX_EXECUTABLE) - -mark_as_advanced(SPHINX_EXECUTABLE) diff --git a/cmake/od_targets.cmake b/cmake/od_targets.cmake index 48d51cf7..7e950181 100644 --- a/cmake/od_targets.cmake +++ b/cmake/od_targets.cmake @@ -1,4 +1,3 @@ - # installation structure if(NOT OD_INSTALL_RUNTIME_DIR) set(OD_INSTALL_RUNTIME_DIR bin) diff --git a/cmake/od_version.cmake b/cmake/od_version.cmake index 243635fb..818060e4 100644 --- a/cmake/od_version.cmake +++ b/cmake/od_version.cmake @@ -1,4 +1,5 @@ # OD version number components. set(OD_MAJOR_VERSION 1) set(OD_MINOR_VERSION 0) -set(OD_BUILD_VERSION 0) \ No newline at end of file +set(OD_BUILD_VERSION 0) + diff --git a/common/include/od/common/pipeline/ODAlgorithmBase.h b/common/include/od/common/pipeline/ODAlgorithmBase.h index 9363b43f..ada40f86 100644 --- a/common/include/od/common/pipeline/ODAlgorithmBase.h +++ b/common/include/od/common/pipeline/ODAlgorithmBase.h @@ -27,13 +27,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Created by sarkar on 12.06.15. // - -#ifndef OPENDETECTION_ODALGORITHMBASE_H -#define OPENDETECTION_ODALGORITHMBASE_H -#include <iostream> +#pragma once #include <boost/filesystem.hpp> -#include <boost/algorithm/string.hpp> - namespace od { @@ -47,12 +42,10 @@ namespace od { public: - virtual void parseParameterString(std::string parameter_string) + virtual void parseParameterString(const std::string & parameter_string) { } virtual void init() { } }; } - -#endif //OPENDETECTION_ODALGORITHMBASE_H diff --git a/common/include/od/common/pipeline/ODDetection.h b/common/include/od/common/pipeline/ODDetection.h index 3f7c19e7..d6a6d460 100644 --- a/common/include/od/common/pipeline/ODDetection.h +++ b/common/include/od/common/pipeline/ODDetection.h @@ -27,19 +27,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Created by sarkar on 12.06.15. // +#pragma once -#ifndef OPENDETECTION_ODDETECTION_H -#define OPENDETECTION_ODDETECTION_H - -#include <iostream> #include "od/common/utils/utils.h" #include "od/common/pipeline/ODScene.h" #include <Eigen/Core> #include <opencv2/core/eigen.hpp> - - -#include <pcl/point_cloud.h> -#include <pcl/point_types.h> #include <opencv2/imgproc.hpp> namespace od @@ -416,4 +409,3 @@ namespace od }; } -#endif //OPENDETECTION_ODDETECTION_H diff --git a/common/include/od/common/pipeline/ODDetector.h b/common/include/od/common/pipeline/ODDetector.h index b9ea3266..12033e19 100644 --- a/common/include/od/common/pipeline/ODDetector.h +++ b/common/include/od/common/pipeline/ODDetector.h @@ -27,16 +27,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Created by sarkar on 08.06.15. // - -#ifndef OPENDETECTION_ODDETECTOR_H -#define OPENDETECTION_ODDETECTOR_H +#pragma once #include "od/common/pipeline/ObjectDetector.h" #include "od/common/pipeline/ODScene.h" #include "od/common/pipeline/ODDetection.h" #include "od/common/pipeline/ObjectDetector.h" #include "od/common/pipeline/ODAlgorithmBase.h" -#include <iostream> namespace od { @@ -55,8 +52,8 @@ namespace od ODDetector(std::string const &training_data_location_) : ODDetectorCommon(training_data_location_) { } - virtual ODDetections* detect(ODScene *scene){} - virtual ODDetections* detectOmni(ODScene *scene){} + virtual ODDetections * detect(ODScene *scene){} + virtual ODDetections * detectOmni(ODScene *scene){} bool metainfo_; @@ -72,10 +69,10 @@ namespace od class ODDetector2D: public ODDetector { public: - ODDetector2D(std::string const &trained_data_location_) : ODDetector(trained_data_location_) + ODDetector2D(const std::string & trained_data_location_) : ODDetector(trained_data_location_) { } - ODDetections* detect(ODScene *scene) + ODDetections * detect(ODScene *scene) { return detect(dynamic_cast<ODSceneImage *>(scene)); } @@ -85,14 +82,14 @@ namespace od * \param[in] scene An instance of 2D scene * \return [out] detections A number of detections as an ODDetections instance. */ - virtual ODDetections* detect(ODSceneImage *scene) = 0; + virtual ODDetections * detect(ODSceneImage * scene) = 0; /** \brief Function for performing detection on an entire scene. * The purpose of this function is to detect an object in an entire scene. Thus, other than the type of detection we also have information about the location of the detection w.r.t. the scene. * \param[in] scene An instance of 2D scene * \return [out] detections A number of detections as an ODDetections2D instance containing information about the detection and its 2D location. */ - virtual ODDetections2D* detectOmni(ODSceneImage *scene) = 0; + virtual ODDetections2D * detectOmni(ODSceneImage * scene) = 0; }; /** \brief The detector of 3D scene. @@ -102,11 +99,11 @@ namespace od * \author Kripasindhu Sarkar * */ - template<typename PointT = pcl::PointXYZRGBA> + template<typename PointT> class ODDetector3D: public ODDetector { public: - ODDetector3D(std::string const &trained_data_location_) : ODDetector(trained_data_location_) + ODDetector3D(const std::string & trained_data_location_) : ODDetector(trained_data_location_) { } /** \brief Function for performing detection on a segmented scene. @@ -114,14 +111,14 @@ namespace od * \param[in] scene An instance of 3D scene * \return A number of detections as an ODDetections instance. */ - virtual ODDetections* detect(ODScenePointCloud<PointT> *scene) = 0; + virtual ODDetections * detect(ODScenePointCloud<PointT> * scene) = 0; /** \brief Function for performing detection on an entire scene. * The purpose of this function is to detect an object in an entire scene. Thus, other than the type of detection we also have information about the location of the detection w.r.t. the scene. * \param[in] scene An instance of 3D scene * \return A number of detections as an ODDetections3D instance containing information about the detection and its 3D pose. */ - virtual ODDetections3D* detectOmni(ODScenePointCloud<PointT> *scene) = 0; + virtual ODDetections3D * detectOmni(ODScenePointCloud<PointT> * scene) = 0; }; /** \brief The detector of 2D scene performing a 'complete detection'. @@ -135,7 +132,7 @@ namespace od class ODDetector2DComplete: public ODDetector { public: - ODDetector2DComplete(std::string const &trained_data_location_) : ODDetector(trained_data_location_) + ODDetector2DComplete(const std::string & trained_data_location_) : ODDetector(trained_data_location_) { } /** \brief Function for performing detection on a segmented scene. @@ -143,15 +140,14 @@ namespace od * \param[in] scene An instance of 2D scene * \return A number of detections as an ODDetections instance. */ - virtual ODDetections* detect(ODSceneImage *scene) = 0; + virtual ODDetections * detect(ODSceneImage * scene) = 0; /** \brief Function for performing detection on an entire scene. * The purpose of this function is to detect an object in an entire scene. Thus, other than the type of detection we also have information about the location of the detection w.r.t. the scene. * \param[in] scene An instance of 2D scene * \return A number of detections as an ODDetections3D instance containing information about the detection and its pose in 3D. */ - virtual ODDetections3D* detectOmni(ODSceneImage *scene) = 0; + virtual ODDetections3D * detectOmni(ODSceneImage * scene) = 0; }; } -#endif //OPENDETECTION_ODDETECTOR_H diff --git a/common/include/od/common/pipeline/ODScene.h b/common/include/od/common/pipeline/ODScene.h index 547e7f12..c9b3b3b3 100644 --- a/common/include/od/common/pipeline/ODScene.h +++ b/common/include/od/common/pipeline/ODScene.h @@ -28,16 +28,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 10.06.15. // -#ifndef OPENDETECTION_SCENE_H -#define OPENDETECTION_SCENE_H - -//#include <opencv2/core/core.hpp> -#include <opencv2/core/types.hpp> -#include <iostream> -#include <opencv2/imgcodecs.hpp> +#pragma once +#include <opencv2/core/core.hpp> +#include <opencv2/highgui/highgui.hpp> #include <pcl/point_cloud.h> #include <pcl/io/pcd_io.h> -#include <boost/smart_ptr/make_shared_object.hpp> namespace od { @@ -50,9 +45,9 @@ namespace od { public: - virtual void *getData() = 0; + virtual void * getData() = 0; - std::string const &getPath() const + std::string const & getPath() const { return path_; } @@ -70,33 +65,33 @@ namespace od { public: - std::vector<cv::KeyPoint> const &getKeypoints() const + const std::vector<cv::KeyPoint> & getKeypoints() const { return keypoints_; } - void setKeypoints(std::vector<cv::KeyPoint> const &keypoints_) + void setKeypoints(const std::vector<cv::KeyPoint> & keypoints_) { ODSceneImage::keypoints_ = keypoints_; } - cv::Mat const &getDescriptors() const + const cv::Mat & getDescriptors() const { return descriptors_; } - void setDescriptors(cv::Mat const &descriptors_) + void setDescriptors(const cv::Mat & descriptors_) { ODSceneImage::descriptors_ = descriptors_; is_trained_ = true; } - ODSceneImage(cv::Mat const &cvimage):is_trained_(false) + ODSceneImage(const cv::Mat & cvimage): is_trained_(false) { this->cvimage_ = cvimage.clone(); } - ODSceneImage(std::string const &path):is_trained_(false) + ODSceneImage(const std::string & path): is_trained_(false) { this->cvimage_ = cv::imread(path); this->path_ = path; @@ -114,7 +109,6 @@ namespace od protected: cv::Mat cvimage_; - std::vector<cv::KeyPoint> keypoints_; cv::Mat descriptors_; bool is_trained_; @@ -127,23 +121,23 @@ namespace od * \author Kripasindhu Sarkar * */ - template <typename PointType = pcl::PointXYZRGBA> + template <typename PointType> class ODScenePointCloud : public ODScene { public: typedef typename pcl::PointCloud<PointType>::Ptr PointCloudPtr; - ODScenePointCloud(PointCloudPtr const &point_cloud) + ODScenePointCloud(const PointCloudPtr & point_cloud) { point_cloud_ = point_cloud; } - ODScenePointCloud(std::string point_cloud_file): point_cloud_(new pcl::PointCloud<PointType>()) + ODScenePointCloud(const std::string & point_cloud_file): point_cloud_(new pcl::PointCloud<PointType>()) { if (pcl::io::loadPCDFile<PointType> (point_cloud_file, *point_cloud_ ) == -1) { - std::cout<<"ERROR: Couldn't read the file "<< point_cloud_file <<std::endl; + std::cout << "ERROR: Couldn't read the file "<< point_cloud_file << std::endl; } this->path_ = point_cloud_file; } @@ -151,25 +145,26 @@ namespace od ODScenePointCloud(): point_cloud_(new pcl::PointCloud<PointType>()) {} - PointCloudPtr const &getPointCloud() const + const PointCloudPtr & getPointCloud() const { return point_cloud_; } - PointCloudPtr &getPointCloudRef() const + PointCloudPtr & getPointCloudRef() const { return point_cloud_; } - void setPointCloud(PointCloudPtr const &point_cloud_) + void setPointCloud(const PointCloudPtr & point_cloud_) { ODScenePointCloud::point_cloud_ = point_cloud_; } - void * getData() { return (void *)point_cloud_.get(); } + void * getData() + { return (void *)point_cloud_.get(); } + protected: PointCloudPtr point_cloud_; }; } -#endif //OPENDETECTION_SCENE_H diff --git a/common/include/od/common/pipeline/ODTrainer.h b/common/include/od/common/pipeline/ODTrainer.h index c1bbcb2c..10f05813 100644 --- a/common/include/od/common/pipeline/ODTrainer.h +++ b/common/include/od/common/pipeline/ODTrainer.h @@ -27,20 +27,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Created by sarkar on 08.06.15. // - -#ifndef OPENDETECTION_TRAINER_H -#define OPENDETECTION_TRAINER_H - -#include <iostream> +#pragma once #include <boost/filesystem.hpp> -#include <boost/algorithm/string.hpp> - #include "od/common/pipeline/ODAlgorithmBase.h" #include "od/common/pipeline/ObjectDetector.h" -namespace bf = boost::filesystem; - namespace od { /** \brief The base class for all trainers. All trainers derives from this and implement the function train(). @@ -52,7 +44,8 @@ namespace od { public: - ODTrainer(std::string const &training_input_location ="", std::string const &training_data_location="") : ODDetectorCommon(training_data_location) + ODTrainer(const std::string & training_input_location = "", const std::string & training_data_location = "") : + ODDetectorCommon(training_data_location) { training_input_location_ = training_data_location; } @@ -62,4 +55,3 @@ namespace od }; } -#endif //OPENDETECTION_TRAINER_H diff --git a/common/include/od/common/pipeline/ObjectDetector.h b/common/include/od/common/pipeline/ObjectDetector.h index b802d2ce..4860f9f7 100644 --- a/common/include/od/common/pipeline/ObjectDetector.h +++ b/common/include/od/common/pipeline/ObjectDetector.h @@ -27,15 +27,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Created by sarkar on 03.06.15. // - -#ifndef OPENDETECTION_OBJECTDETECTOR_H -#define OPENDETECTION_OBJECTDETECTOR_H - +#pragma once #include <string> #include <vector> #include "od/common/pipeline/ODDetection.h" - namespace od { @@ -61,7 +57,7 @@ namespace od { public: - ODDetectorCommon( std::string const &trained_data_location_="") : trained_data_location_(trained_data_location_) + ODDetectorCommon(const std::string & trained_data_location_= "") : trained_data_location_(trained_data_location_) { std::string clasname = typeid(this).name(); TRAINED_DATA_ID_ = clasname; @@ -81,7 +77,7 @@ namespace od /** \brief Gets/Sets the directory containing the data for training. The trainer uses the data from directory for training. Detectors can use this location to get additional information in its detection algirhtms as well. */ - void setTrainingInputLocation(std::string training_input_location_) + void setTrainingInputLocation(const std::string & training_input_location_) { this->training_input_location_ = training_input_location_; } @@ -98,7 +94,7 @@ namespace od /** \brief The base directory for trained data. This should be same for all Trainers and Detectors and can be considered as the 'database' of trained data. Trainers uses one of its * subdirectories based on its type to store algo specific trained data. The corresponding Detector would use the same directory to fetch the trained data for online detection. */ - virtual void setTrainedDataLocation(std::string trained_data_location_) + virtual void setTrainedDataLocation(const std::string & trained_data_location_) { this->trained_data_location_ = trained_data_location_; } @@ -116,12 +112,12 @@ namespace od return getSpecificTrainingDataLocation() + "/" + TRAINED_DATA_ID_; } - std::string const &getTrainedDataID() const + const std::string & getTrainedDataID() const { return TRAINED_DATA_ID_; } - void setTrainedDataID(std::string const &trainedDataID) + void setTrainedDataID(const std::string & trainedDataID) { ODDetectorCommon::TRAINED_DATA_ID_ = trainedDataID; } @@ -144,12 +140,12 @@ namespace od ObjectDetector() { } - DetectionMethod const &getMethod() const + const DetectionMethod & getMethod() const { return method_; } - void setDetectionMethod(DetectionMethod const &detection_method_) + void setDetectionMethod(const DetectionMethod & detection_method_) { this->method_ = detection_method_; } @@ -169,7 +165,7 @@ namespace od return training_input_location_; } - void setTrainingInputLocation(std::string training_input_location_) + void setTrainingInputLocation(const std::string & training_input_location_) { this->training_input_location_ = training_input_location_; } @@ -179,7 +175,7 @@ namespace od return training_data_location_; } - void setTrainingDataLocation(std::string training_data_location_) + void setTrainingDataLocation(const std::string & training_data_location_) { this->training_data_location_ = training_data_location_; } @@ -197,10 +193,10 @@ namespace od virtual int train() = 0; - virtual int detect(ODScene *scene, std::vector<ODDetection *> detections) {} + virtual int detect(ODScene * scene, const std::vector<ODDetection *> & detections) {} - virtual ODDetection* detect(ODScene *scene) {} - virtual ODDetections* detectOmni(ODScene *scene) {} + virtual ODDetection* detect(ODScene * scene) {} + virtual ODDetections* detectOmni(ODScene * scene) {} protected: DetectionMethod method_; @@ -212,5 +208,3 @@ namespace od }; } - -#endif //OPENDETECTION_OBJECTDETECTOR_H diff --git a/common/include/od/common/utils/ODFeatureDetector2D.h b/common/include/od/common/utils/ODFeatureDetector2D.h index cfeda570..0aa051b1 100644 --- a/common/include/od/common/utils/ODFeatureDetector2D.h +++ b/common/include/od/common/utils/ODFeatureDetector2D.h @@ -1,10 +1,7 @@ // // Created by sarkar on 20.04.15. // - -#ifndef OBJECT_DETECTION_FEATUREDETECTOR_H -#define OBJECT_DETECTION_FEATUREDETECTOR_H - +#pragma once #include <iostream> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> @@ -18,61 +15,35 @@ namespace od { + enum FeatureType + { + SIFT, SURF, ORB, SIFT_GPU, ORB_GPU, + }; + class ODFeatureDetector2D { public: - ODFeatureDetector2D(std::string feature_type, bool use_gpu); - - enum FeatureType - { - SIFT, SURF, ORB, SIFT_GPU, ORB_GPU, - }; - - int mode_; - - ODFeatureDetector2D(FeatureType type) - { + ODFeatureDetector2D(const std::string & feature_type, bool use_gpu); - mode_ = type; + ODFeatureDetector2D(FeatureType type); - if(type == SIFT) { - feature_detector_ = cv::xfeatures2d::SIFT::create(); - } else if(type == ORB) { - feature_detector_ = cv::ORB::create(); - } else if(type == SURF) { - feature_detector_ = cv::xfeatures2d::SURF::create(); - } else if(type == ORB_GPU) { - feature_detector_ = cv::cuda::ORB::create(); - } else if(type == SIFT_GPU) { - sift_gpu_ = new SiftGPU; - //char * argv[] = {(char *)"-fo", (char *)"-1", (char *)"-v", (char *)"1"}; - char *argv[] = {(char *) "-fo", (char *) "-1", (char *) "-v", (char *) "3", (char *) "-cuda"}; - int argc = sizeof(argv) / sizeof(char *); - sift_gpu_->ParseParam(argc, argv); - if(sift_gpu_->CreateContextGL() != SiftGPU::SIFTGPU_FULL_SUPPORTED) - std::cout << "FATAL ERROR cannot create SIFTGPU context"; - } - } + void computeKeypointsAndDescriptors(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints); - //always return Opencv type keypoints - void computeKeypointsAndDescriptors(cv::Mat const &image, cv::Mat &descriptors, std::vector<cv::KeyPoint> &keypoints); + void findSiftGPUDescriptors(const char * image_name, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints); - void findSiftGPUDescriptors(char const *image_name, cv::Mat &descriptors, std::vector<cv::KeyPoint> &keypoints); + void findSiftGPUDescriptors(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints); - void findSiftGPUDescriptors(cv::Mat const &image, cv::Mat &descriptors, std::vector<cv::KeyPoint> &keypoints); - - void computeAndSave(cv::Mat const &image, std::string const path); + void computeAndSave(const cv::Mat & image, const std::string & path); private: + void findSiftGPUDescriptors_(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints); + cv::Ptr<cv::FeatureDetector> feature_detector_; cv::Ptr<SiftGPU> sift_gpu_; - - - void findSiftGPUDescriptors1(cv::Mat const &image, cv::Mat &descriptors, std::vector<cv::KeyPoint> &keypoints); + int mode_; }; } -#endif //OBJECT_DETECTION_FEATUREDETECTOR_H diff --git a/common/include/od/common/utils/ODFrameGenerator.h b/common/include/od/common/utils/ODFrameGenerator.h index 66804d1c..18661357 100644 --- a/common/include/od/common/utils/ODFrameGenerator.h +++ b/common/include/od/common/utils/ODFrameGenerator.h @@ -1,15 +1,13 @@ // // Created by sarkar on 19.06.15. // - -#ifndef OPENDETECTION_ODFRAMEGENERATOR_H -#define OPENDETECTION_ODFRAMEGENERATOR_H - +#pragma once #include "od/common/pipeline/ODDetection.h" #include "od/common/pipeline/ODScene.h" #include <iostream> #include <opencv2/videoio.hpp> #include <pcl/apps/3d_rec_framework/tools/openni_frame_source.h> +#include <boost/make_shared.hpp> namespace od { @@ -19,7 +17,6 @@ namespace od }; - /** \brief The FrameGenerator class for capturing and reading Scenes conveniently. * Templated with two parameters - SceneType identifying a Scene class, and TYPE identifying the type of input. After the instantiation with a correct TYPE, use the function * getNextFrame() to get an instance of next scene of SceneType. getNextFrame() returns valid scenes until all scenes matched are exhausted - the time when 'isValid()' is false. @@ -27,15 +24,15 @@ namespace od * \tparam SceneT One of the Scene classes - ODSceneImage or ODScenePointCloud * \tparam TYPE TYPE can be GENERATOR_TYPE_FILE_LIST which means you provide the list of scene files to be returned by the FrameGenerator in the constructor (for eg. \/home/username/pics/\*.jpg) * Or it can be GENERATOR_TYPE_DEVICE which picks up the webcam or the kinect based on the SceneType. - * \author Kripasindhu Sarkar - * - */ + * \author Kripasindhu Sarkar + * + */ template<typename SceneT, GeneratorType TYPE> class ODFrameGenerator { public: - ODFrameGenerator(std::string input = ""); + ODFrameGenerator(const std::string & input = ""); ODFrameGenerator(int input = 0){} SceneT * getNextFrame(); @@ -46,7 +43,7 @@ namespace od int cameraID_; std::vector<std::string> file_list_; std::string video_read_path_; - int curr_image_; + unsigned int curr_image_; cv::VideoCapture inputCapture_; bool exhausted_; }; @@ -56,7 +53,7 @@ namespace od class ODFrameGenerator<SceneT, GENERATOR_TYPE_FILE_LIST> { public: - ODFrameGenerator(std::string const input = "") + ODFrameGenerator(const std::string & input = "") { file_list_ = myglob(input); curr_image_ = -1; @@ -65,11 +62,12 @@ namespace od SceneT * getNextFrame() { - if (exhausted_) + if(exhausted_) { cout << "Files Exhausted!"; - return NULL; + return nullptr; } + curr_image_++; if(curr_image_ == file_list_.size() - 1) exhausted_ = true; @@ -77,11 +75,15 @@ namespace od cout << "Frame: " << file_list_[curr_image_] << endl; return new SceneT(file_list_[curr_image_]); } - bool isValid() {return !exhausted_;} + bool isValid() + {return !exhausted_;} + std::string currentFile() { return file_list_[curr_image_]; } + + private: std::vector<std::string> file_list_; bool exhausted_; int curr_image_; @@ -91,24 +93,29 @@ namespace od class ODFrameGenerator<ODSceneImage, GENERATOR_TYPE_DEVICE> { public: - ODFrameGenerator(std::string input = "") + ODFrameGenerator(const std::string & input = "") { input_capture_.open(input); - if (!input_capture_.isOpened()) { cout << "FATAL: Cannot open video capture!";} + if(!input_capture_.isOpened()) + {std::cout << "FATAL: Cannot open video capture!" << std::endl;} } ODFrameGenerator(int input = 0) { input_capture_.open(input); - if (!input_capture_.isOpened()) { cout << "FATAL: Cannot open video capture!";} + if(!input_capture_.isOpened()) + {std::cout << "FATAL: Cannot open video capture!" << std::endl;} } ODSceneImage * getNextFrame() { - cv::Mat frame; input_capture_.read(frame); + cv::Mat frame; + input_capture_.read(frame); return new ODSceneImage(frame); } - bool isValid() {return input_capture_.isOpened();} + + bool isValid() + {return input_capture_.isOpened();} cv::VideoCapture input_capture_; }; @@ -147,38 +154,37 @@ namespace od */ - template<> - class ODFrameGenerator<ODScenePointCloud<pcl::PointXYZRGBA> , GENERATOR_TYPE_DEVICE> + template<typename PointT> + class ODFrameGenerator<ODScenePointCloud<PointT>, GENERATOR_TYPE_DEVICE> { public: - typedef pcl::PointXYZRGBA PointT; typedef pcl::PointCloud<PointT> PointCloud; - typedef pcl::PointCloud<PointT>::Ptr PointCloudPtr; - typedef pcl::PointCloud<PointT>::ConstPtr PointCloudConstPtr; + typedef typename pcl::PointCloud<PointT>::Ptr PointCloudPtr; + typedef typename pcl::PointCloud<PointT>::ConstPtr PointCloudConstPtr; /* A simple class for capturing data from an OpenNI camera */ - ODFrameGenerator(std::string input = "") : grabber_(input), most_recent_frame_(), frame_counter_(0), active_(true) + ODFrameGenerator(const std::string & input = "") : grabber_(input), most_recent_frame_(), frame_counter_(0), active_(true) { - boost::function<void(const PointCloudConstPtr&)> frame_cb = boost::bind (&ODFrameGenerator<ODScenePointCloud<pcl::PointXYZRGBA> , GENERATOR_TYPE_DEVICE>::onNewFrame, this, _1); - grabber_.registerCallback (frame_cb); + boost::function<void(const PointCloudConstPtr&)> frame_cb = boost::bind(&ODFrameGenerator<ODScenePointCloud<PointT> , GENERATOR_TYPE_DEVICE>::onNewFrame, this, _1); + grabber_.registerCallback(frame_cb); grabber_.start (); - boost::this_thread::sleep (boost::posix_time::seconds(5)); + boost::this_thread::sleep(boost::posix_time::seconds(5)); } ~ODFrameGenerator() { - // Stop the grabber when shutting down grabber_.stop (); } - ODScenePointCloud<pcl::PointXYZRGBA> * getNextFrame() + ODScenePointCloud<PointT> * getNextFrame() { OpenNIFrameSource::PointCloudPtr frame = snap(); - return new ODScenePointCloud<pcl::PointXYZRGBA>(frame); + return new ODScenePointCloud<PointT>(frame); } - bool isValid() {return isActive();} + bool isValid() + {return isActive();} const PointCloudPtr snap () { @@ -205,17 +211,14 @@ namespace od { mutex_.lock (); ++frame_counter_; - most_recent_frame_ = boost::make_shared < PointCloud > (*cloud); // Make a copy of the frame + most_recent_frame_ = boost::make_shared<PointCloud>(*cloud); // Make a copy of the frame mutex_.unlock (); } pcl::OpenNIGrabber grabber_; PointCloudPtr most_recent_frame_; - int frame_counter_; + unsigned int frame_counter_; boost::mutex mutex_; bool active_; }; - } - -#endif //OPENDETECTION_ODFRAMEGENERATOR_H diff --git a/common/include/od/common/utils/utils.h b/common/include/od/common/utils/utils.h index de0f7765..48d0588f 100644 --- a/common/include/od/common/utils/utils.h +++ b/common/include/od/common/utils/utils.h @@ -2,30 +2,16 @@ // Created by sarkar on 09.06.15. // -#ifndef OPENDETECTION_UTILS_H -#define OPENDETECTION_UTILS_H - -//TODO remove time from lib and use chrono -#include <sys/time.h> +#pragma once #include <boost/preprocessor.hpp> #include <boost/filesystem.hpp> #include <boost/algorithm/string.hpp> #include <opencv2/core/core.hpp> #include <fstream> -#include <sstream> #include <iostream> #include <glob.h> -namespace bf = boost::filesystem; - -/** \brief misclenious helping functions; will be refactored later - * - * \author Kripasindhu Sarkar - * - */ - - #define X_DEFINE_ENUM_WITH_STRING_CONVERSIONS_TOSTRING_CASE(r, data, elem) \ case elem : return BOOST_PP_STRINGIZE(elem); @@ -51,19 +37,11 @@ namespace bf = boost::filesystem; namespace od { - template<typename T> - std::string toString(T Number) - { - std::ostringstream ss; - ss << Number; - return ss.str(); - } - - std::vector<std::string> myglob(const std::string &pat); + //TODO REMOVE AND SUBSTITUTE USING BOOST FILESYSTEM + std::vector<std::string> myglob(const std::string & pat); void normL2(cv::Mat &descriptors); - /** * @brief Makes composite image from the given images. Equal number of rows and columns are preferred. Therefore, number of rows = sqrt(number of input images) * @@ -72,303 +50,26 @@ namespace od * @param messages Messages to be put on the top left of each image in `imgs`. Note `message.size()` should be equal to `imgs.size()`. * @return new composite image. */ - cv::Mat makeCanvasMultiImages(std::vector<cv::Mat>& imgs, cv::Size cellSize, std::vector<std::string> messages); + cv::Mat makeCanvasMultiImages(const std::vector<cv::Mat> & imgs, const cv::Size & cellSize, const std::vector<std::string> & messages); - cv::Scalar getHashedColor(std::string name, int constadd); + cv::Scalar getHashedColor(const std::string & name, int offset); - static std::string getTexfileinObj(std::string objfilename) - { + std::string getTexfileinObj(const std::string & objfilename); - boost::filesystem::path p(objfilename); - std::string input_dir = boost::filesystem::path(objfilename).parent_path().c_str(); + namespace fileutils { - std::ifstream input(objfilename.c_str()); - std::string line; - while(getline(input, line)) - { - std::istringstream iss(line); - std::string tok1; - iss >> tok1; - if(tok1 == "mtllib") - { - std::string tok2; + std::string getFirstFile(const std::string & base_path, const std::string & extension); - iss >> tok2; - std::string linemtl; + void getFilesInDirectoryRec(const std::string & base_path, const std::string & extension, std::vector<std::string> & files); + void getFilesInDirectoryRec(const std::string & base_path, const std::vector<std::string> & extensions, std::vector<std::string> & files); - std::ifstream inputmtl((input_dir + "/" + tok2).c_str()); - while(getline(inputmtl, linemtl)) - { - std::istringstream issmtl(linemtl); - issmtl >> tok1; - if(tok1 == "map_Kd") - { - issmtl >> tok2; - return input_dir + "/" + tok2; - } - } - } - } - return ""; - } - -/** \brief An utility class for Timer related operations. - * - * \author Kripasindhu Sarkar - * - */ - class Timer - { - private: - - timeval startTime; - - public: - - double duration_; - - void start() - { - gettimeofday(&startTime, NULL); - } + void getFilesInDirectoryInternal(const boost::filesystem::path & dir, const std::string & rel_path_so_far, std::vector<std::string> & relative_paths, const std::vector<std::string> & exts); + void getFilesInDirectoryInternal(const boost::filesystem::path & dir, const std::string & rel_path_so_far, std::vector<std::string> & relative_paths, const std::string & ext); + + void getFilesInDirectory(const boost::filesystem::path & dir, const std::string & rel_path_so_far, std::vector<std::string> & relative_paths, const std::string & ext); + void createTrainingDir(const std::string & training_dir); - double stop() - { - timeval endTime; - long seconds, useconds; - double duration; - - gettimeofday(&endTime, NULL); - - seconds = endTime.tv_sec - startTime.tv_sec; - useconds = endTime.tv_usec - startTime.tv_usec; - - duration = seconds + useconds / 1000000.0; - duration_ = duration; - return duration; - } - - - double getDuration() - { return duration_; } - - static void printTime(double duration) - { - printf("%5.6f seconds\n", duration); - } - }; - - - template<typename T, typename Ptype> - void printListIn(std::vector<T> list, int n = 0) - { - int num; - if(n == 0) num = list.size(); else num = n < list.size() ? n : list.size(); - - for(int i = 0; i < num; i++) - std::cout << (Ptype) list[i] << " "; - std::cout << std::endl; + //TODO REMOVE + void getArgvArgc(std::string const & commandline, char ***argv, int & argc); } - - template<typename T> - void printList(std::vector<T> list) - { - for(int i = 0; i < list.size(); i++) - std::cout << list[i] << " "; - std::cout << std::endl; - } - - - /** \brief Utility class for File and directory handling. - * - * \author Kripasindhu Sarkar - * - */ - class FileUtils - { - public: - - static std::string getFirstFile(std::string base_path, std::string extension) - { - std::vector<std::string> files; - std::string start = ""; - std::string ext = extension; - bf::path dir = base_path; - FileUtils::getFilesInDirectoryInternal(dir, start, files, ext); - if (files.size() == 0) - { - std::cout << "No file with extension " << extension << " present!\nReturning NULL"; - return ""; - } - else return files[0]; - } - - static void getFilesInDirectoryRec(std::string base_path, std::string extension, std::vector<std::string> &files) - { - std::string start = ""; - std::string ext = extension; - bf::path dir = base_path; - FileUtils::getFilesInDirectoryInternal(dir, start, files, ext); - } - - static void getFilesInDirectoryRec(std::string base_path, std::vector<std::string> extensions, std::vector<std::string> &files) - { - std::string start = ""; - std::vector<std::string> exts = extensions; - bf::path dir = base_path; - FileUtils::getFilesInDirectoryInternal(dir, start, files, exts); - } - - static void getFilesInDirectoryInternal(bf::path &dir, std::string &rel_path_so_far, std::vector<std::string> &relative_paths, std::vector<std::string> exts) - { - bf::directory_iterator end_itr; - for(bf::directory_iterator itr(dir); itr != end_itr; ++itr) { - //check if its a directory, then get models in it - if(bf::is_directory(*itr)) { -#if BOOST_FILESYSTEM_VERSION == 3 - std::string so_far = rel_path_so_far + (itr->path().filename()).string() + "/"; -#else - std::string so_far = rel_path_so_far + (itr->path ()).filename () + "/"; -#endif - - bf::path curr_path = itr->path(); - getFilesInDirectoryInternal(curr_path, so_far, relative_paths, exts); - } else { - //check that it is a ply file and then add, otherwise ignore.. - std::vector<std::string> strs; -#if BOOST_FILESYSTEM_VERSION == 3 - std::string file = (itr->path().filename()).string(); -#else - std::string file = (itr->path ()).filename (); -#endif - - boost::split(strs, file, boost::is_any_of(".")); - std::string extension = strs[strs.size() - 1]; - - bool flagfound = false; - for (int exti = 0; exti < exts.size(); exti ++) - if(file.rfind(exts[exti]) != std::string::npos) - { flagfound = true; break; } - - if( flagfound == true ) - { -#if BOOST_FILESYSTEM_VERSION == 3 - std::string path = rel_path_so_far + (itr->path().filename()).string(); -#else - std::string path = rel_path_so_far + (itr->path ()).filename (); -#endif - std::string fullpath = rel_path_so_far + itr->path().string(); - relative_paths.push_back(fullpath); - } - } - } - } - - static void getFilesInDirectoryInternal(bf::path &dir, std::string &rel_path_so_far, std::vector<std::string> &relative_paths, std::string const &ext) - { - bf::directory_iterator end_itr; - for(bf::directory_iterator itr(dir); itr != end_itr; ++itr) { - //check if its a directory, then get models in it - if(bf::is_directory(*itr)) { -#if BOOST_FILESYSTEM_VERSION == 3 - std::string so_far = rel_path_so_far + (itr->path().filename()).string() + "/"; -#else - std::string so_far = rel_path_so_far + (itr->path ()).filename () + "/"; -#endif - - bf::path curr_path = itr->path(); - getFilesInDirectoryInternal(curr_path, so_far, relative_paths, ext); - } else { - //check that it is a ply file and then add, otherwise ignore.. - std::vector<std::string> strs; -#if BOOST_FILESYSTEM_VERSION == 3 - std::string file = (itr->path().filename()).string(); -#else - std::string file = (itr->path ()).filename (); -#endif - - boost::split(strs, file, boost::is_any_of(".")); - std::string extension = strs[strs.size() - 1]; - - if( file.rfind(ext) != std::string::npos ) - { -#if BOOST_FILESYSTEM_VERSION == 3 - std::string path = rel_path_so_far + (itr->path().filename()).string(); -#else - std::string path = rel_path_so_far + (itr->path ()).filename (); -#endif - std::string fullpath = rel_path_so_far + itr->path().string(); - relative_paths.push_back(fullpath); - } - } - } - } - - static void getFilesInDirectory(bf::path &dir, std::string &rel_path_so_far, std::vector<std::string> &relative_paths, std::string const &ext) - { - bf::directory_iterator end_itr; - for(bf::directory_iterator itr(dir); itr != end_itr; ++itr) { - //check if its a directory, then get models in it - if(bf::is_directory(*itr)) { -#if BOOST_FILESYSTEM_VERSION == 3 - std::string so_far = rel_path_so_far + (itr->path().filename()).string() + "/"; -#else - std::string so_far = rel_path_so_far + (itr->path ()).filename () + "/"; -#endif - - bf::path curr_path = itr->path(); - getFilesInDirectoryInternal(curr_path, so_far, relative_paths, ext); - } else { - //check that it is a ply file and then add, otherwise ignore.. - std::vector<std::string> strs; -#if BOOST_FILESYSTEM_VERSION == 3 - std::string file = (itr->path().filename()).string(); -#else - std::string file = (itr->path ()).filename (); -#endif - - boost::split(strs, file, boost::is_any_of(".")); - std::string extension = strs[strs.size() - 1]; - - if( file.rfind(ext) != std::string::npos ) - { -#if BOOST_FILESYSTEM_VERSION == 3 - std::string path = rel_path_so_far + (itr->path().filename()).string(); -#else - std::string path = rel_path_so_far + (itr->path ()).filename (); -#endif - std::string fullpath = rel_path_so_far + itr->path().string(); - relative_paths.push_back(path); - } - } - } - } - - static void createTrainingDir(std::string training_dir) - { - bf::path trained_dir = training_dir; - if(!bf::exists(trained_dir)) - bf::create_directory(trained_dir); - } - - static void getArgvArgc(std::string const &commandline, char ***argv, int &argc) - { - enum - { - kMaxArgs = 64 - }; - - argc = 0; - *argv = new char *[kMaxArgs]; - (*argv)[argc++] = (char *) "program"; - - char *p; - p = strtok((char *) commandline.c_str(), " "); - while(p && argc < kMaxArgs) { - (*argv)[argc++] = p; - p = strtok(0, " "); - } - } - }; } -#endif //OPENDETECTION_UTILS_H diff --git a/common/src/utils/ODFeatureDetector2D.cpp b/common/src/utils/ODFeatureDetector2D.cpp index 851c32dd..66500e6e 100644 --- a/common/src/utils/ODFeatureDetector2D.cpp +++ b/common/src/utils/ODFeatureDetector2D.cpp @@ -5,83 +5,86 @@ #include <GL/gl.h> #include "od/common/utils/ODFeatureDetector2D.h" -using namespace std; namespace od { - ODFeatureDetector2D::ODFeatureDetector2D(string type, bool use_gpu) + ODFeatureDetector2D::ODFeatureDetector2D(const std::string & feature_type, bool use_gpu) { - mode_ = SIFT; //by default it is SIFT + mode_ = SIFT; if(use_gpu) { - - //##########GPU VERSION - - - if(type == "ORB") { + if(feature_type == "ORB") { mode_ = ORB_GPU; feature_detector_ = cv::cuda::ORB::create(); - } else if(type == "SIFT") { + }else if(feature_type == "SIFT") { mode_ = SIFT_GPU; - sift_gpu_ = new SiftGPU; - //char * argv[] = {(char *)"-fo", (char *)"-1", (char *)"-v", (char *)"1", (char *)"-cuda", (char *)"0"}; + sift_gpu_ = new SiftGPU(); char *argv[] = {(char *) "-fo", (char *) "-1", (char *) "-v", (char *) "1"}; int argc = sizeof(argv) / sizeof(char *); sift_gpu_->ParseParam(argc, argv); if(sift_gpu_->CreateContextGL() != SiftGPU::SIFTGPU_FULL_SUPPORTED) - cout << "FATAL ERROR cannot create SIFTGPU context"; + std::cout << "FATAL ERROR cannot create SIFTGPU context" << std::endl; } } else { - //########CPU VERSIONS - - - if(type == "SIFT") { + if(feature_type == "SIFT") { mode_ = SIFT; feature_detector_ = cv::xfeatures2d::SIFT::create(); - } else if(type == "ORB") { + } else if(feature_type == "ORB") { mode_ = ORB; feature_detector_ = cv::ORB::create(); - } else if(type == "SURF") { + } else if(feature_type == "SURF") { mode_ = SURF; feature_detector_ = cv::xfeatures2d::SURF::create(); } } } - void ODFeatureDetector2D::computeKeypointsAndDescriptors(cv::Mat const &image, cv::Mat &descriptors, vector<cv::KeyPoint> &keypoints) + ODFeatureDetector2D::ODFeatureDetector2D(FeatureType type) + { + + mode_ = type; + + if(type == SIFT) { + feature_detector_ = cv::xfeatures2d::SIFT::create(); + } else if(type == ORB) { + feature_detector_ = cv::ORB::create(); + } else if(type == SURF) { + feature_detector_ = cv::xfeatures2d::SURF::create(); + } else if(type == ORB_GPU) { + feature_detector_ = cv::cuda::ORB::create(); + } else if(type == SIFT_GPU) { + sift_gpu_ = new SiftGPU; + //char * argv[] = {(char *)"-fo", (char *)"-1", (char *)"-v", (char *)"1"}; + char *argv[] = {(char *) "-fo", (char *) "-1", (char *) "-v", (char *) "3", (char *) "-cuda"}; + int argc = sizeof(argv) / sizeof(char *); + sift_gpu_->ParseParam(argc, argv); + if(sift_gpu_->CreateContextGL() != SiftGPU::SIFTGPU_FULL_SUPPORTED) + std::cout << "FATAL ERROR cannot create SIFTGPU context"; + } + } + + void ODFeatureDetector2D::computeKeypointsAndDescriptors(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) { if(mode_ == SIFT_GPU) { - findSiftGPUDescriptors1(image, descriptors, keypoints); + findSiftGPUDescriptors_(image, descriptors, keypoints); } else { feature_detector_->detect(image, keypoints); feature_detector_->compute(image, keypoints, descriptors); } - //viewImage(image, keypoints); } - void CVMatToSiftGPU(const cv::Mat &image, unsigned char *siftImage, cv::Mat &grey) + void CVMatToSiftGPU(const cv::Mat & image, unsigned char * siftImage, cv::Mat & grey) { siftImage = (unsigned char *) malloc(image.rows * image.cols); cv::Mat tmp; cv::cvtColor(image, grey, cv::COLOR_BGR2GRAY); memcpy(siftImage, grey.data, image.rows * image.cols); - - -// tmp2.convertTo(tmp, CV_8U); -// //viewImage(tmp); -// -// for (int y = 0; y < tmp.rows; ++y) { -// for (int x = 0; x < tmp.cols; ++x) { -// siftImage[y * tmp.cols + x] = tmp.at<unsigned char> (y, x); -// } -// } - return; } - void ODFeatureDetector2D::findSiftGPUDescriptors1(cv::Mat const &image, cv::Mat &descriptors, vector<cv::KeyPoint> &keypoints) + void ODFeatureDetector2D::findSiftGPUDescriptors_(cv::Mat const &image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) { unsigned char *data = image.data; cv::Mat greyimage; @@ -93,10 +96,10 @@ namespace od int nFeat = sift_gpu_->GetFeatureNum();//get feature count //allocate memory for readback - vector<SiftGPU::SiftKeypoint> keys(nFeat); + std::vector<SiftGPU::SiftKeypoint> keys(nFeat); //read back keypoints and normalized descritpros //specify NULL if you don’t need keypoints or descriptors - vector<float> imageDescriptors(128 * nFeat); + std::vector<float> imageDescriptors(128 * nFeat); sift_gpu_->GetFeatureVector(&keys[0], &imageDescriptors[0]); sift_gpu_->SaveSIFT("2.sift"); @@ -114,13 +117,11 @@ namespace od descriptors.push_back(descriptor); } - - //viewImage(image, keypoints); } - void ODFeatureDetector2D::findSiftGPUDescriptors(cv::Mat const &image, cv::Mat &descriptors, vector<cv::KeyPoint> &keypoints) + void ODFeatureDetector2D::findSiftGPUDescriptors(cv::Mat const &image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) { unsigned char *data = image.data; cv::Mat greyimage; @@ -133,10 +134,10 @@ namespace od int nFeat = sift_gpu_->GetFeatureNum();//get feature count //allocate memory for readback - vector<SiftGPU::SiftKeypoint> keys(nFeat); + std::vector<SiftGPU::SiftKeypoint> keys(nFeat); //read back keypoints and normalized descritpros //specify NULL if you don’t need keypoints or descriptors - vector<float> imageDescriptors(128 * nFeat); + std::vector<float> imageDescriptors(128 * nFeat); sift_gpu_->GetFeatureVector(&keys[0], &imageDescriptors[0]); sift_gpu_->SaveSIFT("2.sift"); @@ -156,16 +157,16 @@ namespace od } - void ODFeatureDetector2D::findSiftGPUDescriptors(char const *image_name, cv::Mat &descriptors, vector<cv::KeyPoint> &keypoints) + void ODFeatureDetector2D::findSiftGPUDescriptors(const char * image_name, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) { sift_gpu_->RunSIFT(image_name); int nFeat = sift_gpu_->GetFeatureNum();//get feature count //allocate memory for readback - vector<SiftGPU::SiftKeypoint> keys(nFeat); + std::vector<SiftGPU::SiftKeypoint> keys(nFeat); //read back keypoints and normalized descritpros //specify NULL if you don’t need keypoints or descriptors - vector<float> imageDescriptors(128 * nFeat); + std::vector<float> imageDescriptors(128 * nFeat); sift_gpu_->GetFeatureVector(&keys[0], &imageDescriptors[0]); sift_gpu_->SaveSIFT("1.sift"); @@ -173,22 +174,23 @@ namespace od //to opencv format keypoints.clear(); descriptors.create(0, 128, CV_32FC1); - for(int i = 0; i < nFeat; ++i) { + for(size_t i = 0; i < nFeat; ++i) { cv::KeyPoint key(keys[i].x, keys[i].y, keys[i].s, keys[i].o); keypoints.push_back(key); cv::Mat descriptor(1, 128, CV_32FC1); - for(int x = 0; x < 128; x++) descriptor.at<float>(x) = floor(0.5 + 512.0f * imageDescriptors[(i << 7) + x]); + for(size_t x = 0; x < 128; x++) + descriptor.at<float>(x) = floor(0.5 + 512.0f * imageDescriptors[(i << 7) + x]); descriptors.push_back(descriptor); } cv::Mat image = cv::imread(image_name); } - void ODFeatureDetector2D::computeAndSave(cv::Mat const &image, std::string const path) + void ODFeatureDetector2D::computeAndSave(const cv::Mat & image, const std::string &path) { cv::Mat descriptors; - vector<cv::KeyPoint> keypoints; + std::vector<cv::KeyPoint> keypoints; if(mode_ == SIFT_GPU) { - findSiftGPUDescriptors1(image, descriptors, keypoints); + findSiftGPUDescriptors_(image, descriptors, keypoints); sift_gpu_->SaveSIFT(path.c_str()); } else { //DO NOTHING! IMPLEMENT LATER diff --git a/common/src/utils/utils.cpp b/common/src/utils/utils.cpp index 09521b1d..b2293974 100644 --- a/common/src/utils/utils.cpp +++ b/common/src/utils/utils.cpp @@ -5,197 +5,359 @@ #include <opencv2/imgproc/imgproc.hpp> #include <boost/functional/hash.hpp> -using namespace std; -using namespace cv; - namespace od { - - std::vector<std::string> myglob(const std::string &pat) + std::vector<std::string> myglob(const std::string & pat) { - using namespace std; glob_t glob_result; - glob(pat.c_str(), GLOB_TILDE, NULL, &glob_result); - vector<string> ret; - for(unsigned int i = 0; i < glob_result.gl_pathc; ++i) { - ret.push_back(string(glob_result.gl_pathv[i])); + glob(pat.c_str(), GLOB_TILDE, nullptr, &glob_result); + std::vector<std::string> ret; + for(size_t i = 0; i < glob_result.gl_pathc; ++i) { + ret.push_back(std::string(glob_result.gl_pathv[i])); } globfree(&glob_result); return ret; } - void normL2(cv::Mat &descriptors) + void normL2(cv::Mat & descriptors) { for (int r = 0; r < descriptors.rows; r++) { float norm=0; - for (int c = 0; c < descriptors.cols; c++) norm+=(descriptors.at<float>(r, c)*descriptors.at<float>(r, c)); + for (size_t c = 0; c < descriptors.cols; c++) + norm += (descriptors.at<float>(r, c)*descriptors.at<float>(r, c)); + norm = 1.0/sqrt(norm); - for (int c = 0; c < descriptors.cols; c++) descriptors.at<float>(r, c)*=norm; - } - } - cv::Mat makeCanvasMultiImage(std::vector<cv::Mat>& vecMat, int windowHeight, int nRows) - { - int N = vecMat.size(); - nRows = nRows > N ? N : nRows; - int edgeThickness = 10; - int imagesPerRow = ceil(double(N) / nRows); - int resizeHeight = floor(2.0 * ((floor(double(windowHeight - edgeThickness) / nRows)) / 2.0)) - edgeThickness; - int maxRowLength = 0; - - std::vector<int> resizeWidth; - for (int i = 0; i < N;) { - int thisRowLen = 0; - for (int k = 0; k < imagesPerRow; k++) { - double aspectRatio = double(vecMat[i].cols) / vecMat[i].rows; - int temp = int( ceil(resizeHeight * aspectRatio)); - resizeWidth.push_back(temp); - thisRowLen += temp; - if (++i == N) break; - } - if ((thisRowLen + edgeThickness * (imagesPerRow + 1)) > maxRowLength) { - maxRowLength = thisRowLen + edgeThickness * (imagesPerRow + 1); - } + for (size_t c = 0; c < descriptors.cols; c++) + descriptors.at<float>(r, c) *= norm; } - int windowWidth = maxRowLength; - cv::Mat canvasImage(windowHeight, windowWidth, CV_8UC3, cv::Scalar(0, 0, 0)); - - for (int k = 0, i = 0; i < nRows; i++) { - int y = i * resizeHeight + (i + 1) * edgeThickness; - int x_end = edgeThickness; - for (int j = 0; j < imagesPerRow && k < N; k++, j++) { - int x = x_end; - cv::Rect roi(x, y, resizeWidth[k], resizeHeight); - cv::Size s = canvasImage(roi).size(); - // change the number of channels to three - cv::Mat target_ROI(s, CV_8UC3); - if (vecMat[k].channels() != canvasImage.channels()) { - if (vecMat[k].channels() == 1) { - cv::cvtColor(vecMat[k], target_ROI, CV_GRAY2BGR); - } - } - cv::resize(target_ROI, target_ROI, s); - if (target_ROI.type() != canvasImage.type()) { - target_ROI.convertTo(target_ROI, canvasImage.type()); - } - target_ROI.copyTo(canvasImage(roi)); - x_end += resizeWidth[k] + edgeThickness; - } - } - return canvasImage; } - int fontFace = cv::FONT_HERSHEY_SIMPLEX; - double fontScale = 0.75; - int thickness_font = 2; - Scalar red(0, 0, 255); - Scalar green(0,255,0); - Scalar blue(255,0,0); - Scalar yellow(0,255,255); - - void drawTextTopLeft(cv::Mat image, std::string text, cv::Scalar color) + void drawTextTopLeft(cv::Mat & image, const std::string & text, const cv::Scalar & color) { - cv::putText(image, text, cv::Point(25,50), fontFace, fontScale, color, thickness_font, 16); + cv::putText(image, text, cv::Point(25,50), cv::FONT_HERSHEY_SIMPLEX, 0.75, color, 2, 16); } - cv::Mat makeCanvasMultiImages(std::vector<cv::Mat>& imgs, cv::Size cellSize, std::vector<std::string> messages) + cv::Mat makeCanvasMultiImages(const std::vector<cv::Mat> & imgs, const cv::Size & cell_size, const std::vector<std::string> & messages) { bool printmssg = false; - if (messages.size() == imgs.size()) printmssg = true; + if(messages.size() == imgs.size()) + printmssg = true; + + const float n_imgs = imgs.size(); + const int imgs_in_row = ceil(sqrt(n_imgs)); + const int imgs_in_col = ceil(n_imgs/imgs_in_row); - float nImgs=imgs.size(); - int imgsInRow=ceil(sqrt(nImgs)); // You can set this explicitly - int imgsInCol=ceil(nImgs/imgsInRow); // You can set this explicitly + const unsigned int cell_width = cell_size.width; + const unsigned int cell_height = cell_size.height; - int resultImgW=cellSize.width*imgsInRow; - int resultImgH=cellSize.height*imgsInCol; + const int resultImgW = cell_width * imgs_in_row; + const int resultImgH = cell_height * imgs_in_col; - Mat resultImg=Mat::zeros(resultImgH,resultImgW,CV_8UC3); - int ind=0; - Mat tmp; - for(int i=0;i<imgsInCol;i++) + + + cv::Mat result_img = cv::Mat::zeros(resultImgH, resultImgW, CV_8UC3); + unsigned int ind = 0; + cv::Mat tmp; + for(size_t i = 0; i < imgs_in_col; i++) { - for(int j=0;j<imgsInRow;j++) + for(size_t j = 0; j < imgs_in_row; j++) { - if(ind<imgs.size()) + if(ind < imgs.size()) { - int cell_row=i*cellSize.height; - int cell_col=j*cellSize.width; + const int cell_row = i*cell_height; + const int cell_col = j*cell_width; - resize(imgs[ind], tmp, cellSize); + resize(imgs[ind], tmp, cell_size); - if (printmssg) drawTextTopLeft(tmp, messages[ind], yellow); + if(printmssg) + drawTextTopLeft(tmp, messages[ind], cv::Scalar(0, 255, 255)); - tmp.copyTo(resultImg(Range(cell_row,cell_row+cellSize.height),Range(cell_col,cell_col+cellSize.width))); + tmp.copyTo(result_img(cv::Range(cell_row, cell_row+cell_height), cv::Range(cell_col, cell_col+cell_width))); } ind++; } } - return resultImg; + return result_img; } + std::string getTexfileinObj(const std::string & objfilename) + { + + std::string input_dir(boost::filesystem::path(objfilename).parent_path().string()); + + std::ifstream input(objfilename); + std::string line; + while(getline(input, line)) + { + std::istringstream iss(line); + std::string tok1; + iss >> tok1; + if(tok1 == "mtllib") + { + std::string tok2; + + iss >> tok2; + std::string linemtl; - cv::Scalar getHashedColor(std::string name, int constadd = 100) + std::ifstream inputmtl((input_dir + "/" + tok2)); + while(getline(inputmtl, linemtl)) + { + std::istringstream issmtl(linemtl); + issmtl >> tok1; + if(tok1 == "map_Kd") + { + issmtl >> tok2; + return input_dir + "/" + tok2; + } + } + } + } + return ""; + } + + cv::Scalar getHashedColor(const std::string & name, int offset = 100) { boost::hash<std::string> string_hash; - int hashed = string_hash(name); + const int hashed = string_hash(name); - return CV_RGB((hashed + constadd) % 255, (hashed + 2*constadd) % 255, (hashed + 3*constadd) % 255); + return CV_RGB((hashed + offset) % 255, (hashed + 2*offset) % 255, (hashed + 3*offset) % 255); } - cv::Mat makeCanvasMultiImage1(std::vector<cv::Mat>& vecMat, int windowHeight, int nRows) + cv::Mat makeCanvasMultiImage1(std::vector<cv::Mat> & input, int window_height, int rows) { - int N = vecMat.size(); - nRows = nRows > N ? N : nRows; - int edgeThickness = 10; - int imagesPerRow = ceil(double(N) / nRows); - int resizeHeight = floor(2.0 * ((floor(double(windowHeight - edgeThickness) / nRows)) / 2.0)) - edgeThickness; - int maxRowLength = 0; - - std::vector<int> resizeWidth; - for (int i = 0; i < N;) { - int thisRowLen = 0; - for (int k = 0; k < imagesPerRow; k++) { - double aspectRatio = double(vecMat[i].cols) / vecMat[i].rows; - int temp = int( ceil(resizeHeight * aspectRatio)); - resizeWidth.push_back(temp); - thisRowLen += temp; - if (++i == N) break; + const unsigned int input_size = input.size(); + const unsigned int n_rows = rows > input_size ? input_size : rows; + const unsigned int edge_thickness = 10; + const unsigned int images_per_row = ceil(static_cast<float>(input_size) / static_cast<float>(n_rows)); + const unsigned int resize_height = floor(2.0 * ((floor(static_cast<float>(window_height - edge_thickness) / static_cast<float>(n_rows))) / 2.0)) - static_cast<float>(edge_thickness); + unsigned int max_row_length = 0; + + std::vector<int> resize_width; + + for(size_t i = 0; i < input_size;){ + int this_row_len = 0; + for(size_t k = 0; k < images_per_row; k++){ + float aspect_ratio = float(input[i].cols) / input[i].rows; + const int temp = static_cast<int>(ceil(resize_height * aspect_ratio)); + resize_width.push_back(temp); + this_row_len += temp; + if(++i == input_size) + break; } - if ((thisRowLen + edgeThickness * (imagesPerRow + 1)) > maxRowLength) { - maxRowLength = thisRowLen + edgeThickness * (imagesPerRow + 1); + const int tmp = this_row_len + edge_thickness * (images_per_row + 1); + if(tmp > max_row_length){ + max_row_length = tmp; } } - int windowWidth = maxRowLength; - cv::Mat canvasImage(windowHeight, windowWidth, CV_8UC3, cv::Scalar(0, 0, 0)); - - for (int k = 0, i = 0; i < nRows; i++) { - int y = i * resizeHeight + (i + 1) * edgeThickness; - int x_end = edgeThickness; - for (int j = 0; j < imagesPerRow && k < N; k++, j++) { - int x = x_end; - cv::Rect roi(x, y, resizeWidth[k], resizeHeight); - cv::Size s = canvasImage(roi).size(); + + cv::Mat canvas_image(window_height, max_row_length, CV_8UC3, cv::Scalar(0, 0, 0)); + + for(size_t k = 0, i = 0; i < n_rows; i++){ + int y = i * resize_height + (i + 1) * edge_thickness; + int x_end = edge_thickness; + cv::Size s; + for(size_t j = 0; j < images_per_row && k < input_size; k++, j++){ + cv::Rect roi(x_end, y, resize_width[k], resize_height); + s = canvas_image(roi).size(); // change the number of channels to three - cv::Mat target_ROI(s, CV_8UC3); - if (vecMat[k].channels() != canvasImage.channels()) { - if (vecMat[k].channels() == 1) { - cv::cvtColor(vecMat[k], target_ROI, CV_GRAY2BGR); + cv::Mat target_roi(s, CV_8UC3); + if(input[k].channels() != canvas_image.channels()){ + if(input[k].channels() == 1){ + cv::cvtColor(input[k], target_roi, CV_GRAY2BGR); } } - cv::resize(target_ROI, target_ROI, s); - if (target_ROI.type() != canvasImage.type()) { - target_ROI.convertTo(target_ROI, canvasImage.type()); + cv::resize(target_roi, target_roi, s); + if(target_roi.type() != canvas_image.type()){ + target_roi.convertTo(target_roi, canvas_image.type()); } - target_ROI.copyTo(canvasImage(roi)); - x_end += resizeWidth[k] + edgeThickness; + target_roi.copyTo(canvas_image(roi)); + x_end += resize_width[k] + edge_thickness; + } + } + return canvas_image; + } + + namespace fileutils { + + std::string getFirstFile(const std::string & base_path, const std::string & extension) + { + std::vector<std::string> files; + fileutils::getFilesInDirectoryInternal(base_path, "", files, extension); + if(files.size() == 0) + { + std::cout << "No file with extension " << extension << " present!" << std::endl; + std::cout << "Returning empty string" << std::endl; + return ""; + } + return files[0]; + } + + void getFilesInDirectoryRec(const std::string & base_path, const std::string & extension, std::vector<std::string> & files) + { + fileutils::getFilesInDirectoryInternal(base_path, "", files, extension); + } + + void getFilesInDirectoryRec(const std::string & base_path, const std::vector<std::string> & extensions, std::vector<std::string> & files) + { + fileutils::getFilesInDirectoryInternal(base_path, "", files, extensions); + } + + void getFilesInDirectoryInternal(const boost::filesystem::path & dir, const std::string & rel_path_so_far, std::vector<std::string> & relative_paths, const std::vector<std::string> & exts) + { + boost::filesystem::directory_iterator end_itr; + for(boost::filesystem::directory_iterator itr(dir); itr != end_itr; ++itr) { + //check if its a directory, then get models in it + if(boost::filesystem::is_directory(*itr)) { +#if BOOST_FILESYSTEM_VERSION == 3 + std::string so_far = rel_path_so_far + (itr->path().filename()).string() + "/"; +#else + std::string so_far = rel_path_so_far + (itr->path ()).filename () + "/"; +#endif + + boost::filesystem::path curr_path = itr->path(); + getFilesInDirectoryInternal(curr_path, so_far, relative_paths, exts); + } else { + //check that it is a ply file and then add, otherwise ignore.. + std::vector<std::string> strs; +#if BOOST_FILESYSTEM_VERSION == 3 + std::string file = (itr->path().filename()).string(); +#else + std::string file = (itr->path ()).filename (); +#endif + + boost::split(strs, file, boost::is_any_of(".")); + std::string extension = strs[strs.size() - 1]; + + bool flagfound = false; + for (int exti = 0; exti < exts.size(); exti ++) + if(file.rfind(exts[exti]) != std::string::npos) + { flagfound = true; break; } + + if( flagfound == true ) + { +#if BOOST_FILESYSTEM_VERSION == 3 + std::string path = rel_path_so_far + (itr->path().filename()).string(); +#else + std::string path = rel_path_so_far + (itr->path ()).filename (); +#endif + std::string fullpath = rel_path_so_far + itr->path().string(); + relative_paths.push_back(fullpath); + } + } + } + } + + void getFilesInDirectoryInternal(const boost::filesystem::path & dir, const std::string & rel_path_so_far, std::vector<std::string> & relative_paths, const std::string & ext) + { + boost::filesystem::directory_iterator end_itr; + for(boost::filesystem::directory_iterator itr(dir); itr != end_itr; ++itr) { + //check if its a directory, then get models in it + if(boost::filesystem::is_directory(*itr)) { +#if BOOST_FILESYSTEM_VERSION == 3 + std::string so_far = rel_path_so_far + (itr->path().filename()).string() + "/"; +#else + std::string so_far = rel_path_so_far + (itr->path ()).filename () + "/"; +#endif + + boost::filesystem::path curr_path = itr->path(); + getFilesInDirectoryInternal(curr_path, so_far, relative_paths, ext); + } else { + //check that it is a ply file and then add, otherwise ignore.. + std::vector<std::string> strs; +#if BOOST_FILESYSTEM_VERSION == 3 + std::string file = (itr->path().filename()).string(); +#else + std::string file = (itr->path ()).filename (); +#endif + + boost::split(strs, file, boost::is_any_of(".")); + std::string extension = strs[strs.size() - 1]; + + if( file.rfind(ext) != std::string::npos ) + { +#if BOOST_FILESYSTEM_VERSION == 3 + std::string path = rel_path_so_far + (itr->path().filename()).string(); +#else + std::string path = rel_path_so_far + (itr->path ()).filename (); +#endif + std::string fullpath = rel_path_so_far + itr->path().string(); + relative_paths.push_back(fullpath); + } + } + } + } + + void getFilesInDirectory(const boost::filesystem::path & dir, const std::string & rel_path_so_far, std::vector<std::string> & relative_paths, const std::string & ext) + { + boost::filesystem::directory_iterator end_itr; + for(boost::filesystem::directory_iterator itr(dir); itr != end_itr; ++itr) { + //check if its a directory, then get models in it + if(boost::filesystem::is_directory(*itr)) { +#if BOOST_FILESYSTEM_VERSION == 3 + std::string so_far = rel_path_so_far + (itr->path().filename()).string() + "/"; +#else + std::string so_far = rel_path_so_far + (itr->path ()).filename () + "/"; +#endif + + boost::filesystem::path curr_path = itr->path(); + getFilesInDirectoryInternal(curr_path, so_far, relative_paths, ext); + } else { + //check that it is a ply file and then add, otherwise ignore.. + std::vector<std::string> strs; +#if BOOST_FILESYSTEM_VERSION == 3 + std::string file = (itr->path().filename()).string(); +#else + std::string file = (itr->path ()).filename (); +#endif + + boost::split(strs, file, boost::is_any_of(".")); + std::string extension = strs[strs.size() - 1]; + + if( file.rfind(ext) != std::string::npos ) + { +#if BOOST_FILESYSTEM_VERSION == 3 + std::string path = rel_path_so_far + (itr->path().filename()).string(); +#else + std::string path = rel_path_so_far + (itr->path ()).filename (); +#endif + std::string fullpath = rel_path_so_far + itr->path().string(); + relative_paths.push_back(path); + } + } + } + } + + void createTrainingDir(const std::string & training_dir) + { + if(!boost::filesystem::exists(training_dir)) + boost::filesystem::create_directory(training_dir); + } + + void getArgvArgc(const std::string & commandline, char *** argv, int & argc) + { + enum + { + kMaxArgs = 64 + }; + + argc = 0; + *argv = new char *[kMaxArgs]; + (*argv)[argc++] = (char *) "program"; + + char *p; + p = strtok((char *) commandline.c_str(), " "); + while(p && argc < kMaxArgs) { + (*argv)[argc++] = p; + p = strtok(0, " "); } } - return canvasImage; + // fileutil namespace } + //od namespace } diff --git a/detectors/include/od/detectors/global2D/ODFaceRecognizer.h b/detectors/include/od/detectors/global2D/ODFaceRecognizer.h index 45c1783e..96bc6619 100644 --- a/detectors/include/od/detectors/global2D/ODFaceRecognizer.h +++ b/detectors/include/od/detectors/global2D/ODFaceRecognizer.h @@ -40,7 +40,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <opencv2/imgproc.hpp> #include <opencv2/objdetect.hpp> - +#include <string> namespace od { diff --git a/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h b/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h index 34cf4786..2f07b3c7 100644 --- a/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h +++ b/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h @@ -26,10 +26,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */// // Created by sarkar on 17.07.15. // - -#ifndef OPENDETECTION_ODCASCADEDETECTOR_H -#define OPENDETECTION_ODCASCADEDETECTOR_H - +#pragma once #include "od/common/pipeline/ODDetector.h" #include "od/common/pipeline/ODScene.h" #include "od/common/utils/utils.h" @@ -65,7 +62,7 @@ namespace od void init() { - haar_cascade_ = boost::make_shared<cv::CascadeClassifier>(FileUtils::getFirstFile(getSpecificTrainingDataLocation(), + haar_cascade_ = std::make_shared<cv::CascadeClassifier>(fileutils::getFirstFile(getSpecificTrainingDataLocation(), TRAINED_DATA_ID_)); } @@ -73,7 +70,7 @@ namespace od ODDetections* detect(ODSceneImage *scene); private: - boost::shared_ptr<cv::CascadeClassifier> haar_cascade_; + std::shared_ptr<cv::CascadeClassifier> haar_cascade_; double scaleFactor_; int minNeighbors_; @@ -87,5 +84,3 @@ namespace od } } - -#endif //OPENDETECTION_ODCASCADEDETECTOR_H diff --git a/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h b/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h index 104783d0..d4054b94 100644 --- a/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h +++ b/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h @@ -39,7 +39,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/common/utils/ODFeatureDetector2D.h" #include <iostream> -#include <time.h> // OpenCV #include <opencv2/core/core.hpp> #include <opencv2/core/utility.hpp> @@ -216,7 +215,7 @@ namespace od pnpMethod = cv::SOLVEPNP_EPNP; f_type_default = "SIFT"; - featureDetector = boost::make_shared<ODFeatureDetector2D>(f_type_default, use_gpu); + featureDetector = std::make_shared<ODFeatureDetector2D>(f_type_default, use_gpu); } void parseParameterString(string parameter_string); @@ -259,7 +258,7 @@ namespace od vector<Model> models; PnPProblem pnp_detection; std::string f_type_default; - boost::shared_ptr<ODFeatureDetector2D> featureDetector; + std::shared_ptr<ODFeatureDetector2D> featureDetector; bool detectSingleModel(ODSceneImage *scene, Model const &model, ODDetection3D *&pD, cv::Mat &frame_viz); }; diff --git a/detectors/src/global2D/ODFaceRecognizer.cpp b/detectors/src/global2D/ODFaceRecognizer.cpp index 7461530f..5a7d8d3b 100644 --- a/detectors/src/global2D/ODFaceRecognizer.cpp +++ b/detectors/src/global2D/ODFaceRecognizer.cpp @@ -64,7 +64,7 @@ namespace od //get models in the directory std::vector<std::string> files; - FileUtils::getFilesInDirectoryRec(getSpecificTrainingDataLocation(), TRAINED_DATA_EXT_, files); + fileutils::getFilesInDirectoryRec(getSpecificTrainingDataLocation(), TRAINED_DATA_EXT_, files); if (files.size() == 0) { @@ -90,7 +90,7 @@ namespace od exit(1); } cvrecognizer_->train(images, labels); - FileUtils::createTrainingDir(getSpecificTrainingDataLocation()); + fileutils::createTrainingDir(getSpecificTrainingDataLocation()); cvrecognizer_->save(getSpecificTrainingDataLocation() + "/trained." + TRAINED_DATA_EXT_); trained_ = true; @@ -115,7 +115,7 @@ namespace od cvrecognizer_->predict(face_edited, label, confidence); //fill in the detection - ODDetection2D *detection = new ODDetection2D(ODDetection::OD_DETECTION_CLASS, toString(label), confidence); + ODDetection2D *detection = new ODDetection2D(ODDetection::OD_DETECTION_CLASS, std::to_string(label), confidence); ODDetections2D *detections = new ODDetections2D; detections->push_back(detection); return detections; diff --git a/detectors/src/global3D/CMakeLists.txt b/detectors/src/global3D/CMakeLists.txt index 6520d095..70d684b0 100644 --- a/detectors/src/global3D/CMakeLists.txt +++ b/detectors/src/global3D/CMakeLists.txt @@ -29,5 +29,4 @@ if(build) #PCL_MAKE_PKGCONFIG("${LIB_NAME}" "${SUBSYS_NAME}" "${SUBSYS_DESC}" "${SUBSYS_DEPS}" "" "" "" "") - endif(build) \ No newline at end of file diff --git a/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp b/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp index 37056126..aff4666b 100644 --- a/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp +++ b/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp @@ -59,7 +59,7 @@ namespace od void ODCADRecognizer2DLocal::parseParameterString(string parameter_string) { - const String keys = "{help h | | print this message }" + const std::string keys = "{help h | | print this message }" "{video v | | path to recorded video }" "{test_images img | | image for detection }" "{cam_id | | pass true if you want the input from the camera }" @@ -78,7 +78,7 @@ namespace od char **argv; int argc; - FileUtils::getArgvArgc(parameter_string, &argv, argc); + fileutils::getArgvArgc(parameter_string, &argv, argc); CommandLineParser parser(argc, argv, keys); if(parser.has("help")) @@ -115,7 +115,7 @@ namespace od pnp_detection = PnPProblem(cam_man, dist_coeff); // get all trained models - FileUtils::getFilesInDirectoryRec(getSpecificTrainingDataLocation(), TRAINED_DATA_ID_, model_names); + fileutils::getFilesInDirectoryRec(getSpecificTrainingDataLocation(), TRAINED_DATA_ID_, model_names); for(int i = 0; i < model_names.size(); i++) { @@ -126,7 +126,7 @@ namespace od if(models.size() > 0) f_type_default = models[0].f_type; - featureDetector = boost::make_shared<ODFeatureDetector2D>(f_type_default, use_gpu); + featureDetector = std::make_shared<ODFeatureDetector2D>(f_type_default, use_gpu); } diff --git a/detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp b/detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp index 8b81b4db..d724990a 100644 --- a/detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp +++ b/detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp @@ -25,9 +25,6 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h" -//simplecube/newcube.obj simplecube/flower.jpeg -//Lion/Final.obj Lion/Texture.png -//BigDaddy/Param.obj BigDaddy/Parameterization.png #include <string> #include <stdlib.h> @@ -87,9 +84,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <opencv2/xfeatures2d.hpp> #include <vtkJPEGWriter.h> - -//extra -//xml #include "pugixml.hpp" @@ -132,7 +126,7 @@ namespace od cout << "Processing finished... Writing the final descriptors" << endl; string filename = boost::filesystem::path(input_file).filename().replace_extension(feature_type + "." + output_extension).c_str(); - FileUtils::createTrainingDir(output_dir); + fileutils::createTrainingDir(output_dir); write_pairs(pairs_3d_2d, common_descriptors, output_dir + "/" + filename); write_pairs_xml(pairs_3d_2d, common_descriptors, output_dir + "/" + filename); @@ -182,21 +176,21 @@ namespace od string input_file, input_dir, output_dir, output_extension; - vtkActor *actor; - vtkRenderer *renderer; + vtkActor * actor; + vtkRenderer * renderer; bool snap_mode; }; int ODCADRecogTrainerSnapshotBased::train() { - FileUtils::createTrainingDir(trained_data_location_); + fileutils::createTrainingDir(trained_data_location_); //get models in the directory std::vector<std::string> files; std::string start = ""; std::string ext = std::string("obj"); - bf::path dir = training_input_location_; - FileUtils::getFilesInDirectory(dir, start, files, ext); + boost::filesystem::path dir = training_input_location_; + fileutils::getFilesInDirectory(dir, start, files, ext); //for each models in the train_input_directory, train them and put them on the training_directory @@ -300,7 +294,7 @@ namespace od vtkSmartPointer<vtkJPEGWriter> writer = vtkSmartPointer<vtkJPEGWriter>::New(); std::string filename; - filename = input_dir + "/" + string("snapshot") + toString(snap_no) + ".jpg"; + filename = input_dir + "/" + string("snapshot") + std::to_string(snap_no) + ".jpg"; writer->SetFileName(filename.c_str()); writer->SetInputConnection(windowToImageFilter->GetOutputPort()); @@ -424,8 +418,8 @@ namespace od common_descriptors.push_back(descriptors_local_good); //writing a local set - write_pairs(pairs_local, descriptors_local_good, input_dir + "/" + "local" + toString(ino) + ".pairs"); - write_pairs_xml(pairs_local, descriptors_local_good, input_dir + "/" + "local" + toString(ino) + ".xml"); + write_pairs(pairs_local, descriptors_local_good, input_dir + "/" + "local" + std::to_string(ino) + ".pairs"); + write_pairs_xml(pairs_local, descriptors_local_good, input_dir + "/" + "local" + std::to_string(ino) + ".xml"); cout << "Processed view " << ino << endl; } diff --git a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md index 99f944bc..b9cc2da2 100644 --- a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md +++ b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md @@ -114,4 +114,22 @@ and include the upper level folder. To use the first solution we would need to i - detection The new structure compiles fines except for three examples which have a linker bug (undefined reference to `vtable for od::g3d::ODCADDetectTrainer3DGlobal' -) which I am trying to resolve. I fixed a bit the source code of all the examples removing unnecessary includes, fixing namespaces, maintaining a common interface and avoiding dynamic memory allocation where possible. The next step after fixing the linker bug will be to fix the install paths for includes and libs. \ No newline at end of file +) which I am trying to resolve. I fixed a bit the source code of all the examples removing unnecessary includes, fixing namespaces, maintaining a common interface and avoiding dynamic memory allocation where possible. The next step after fixing the linker bug will be to fix the install paths for includes and libs. + +##Include structure 2 and install target## + +I fixed the linking error; it came from a wrong variable name in a cmake file which specified some source files which were not compiled and so the linking error. I split simple_ransac_detection in src and include and reinserted it in local2D detectors since it is used only there. The source files are just added to the local2D library and compiled together. + +Next I moved to fix the install target in the cmake. In our case, since we have already a clean structure of the include folders which we want to maintain we can directly copy the include folders of each module (detectors and common) into the include folder specified by *CMAKE_INSTALL_PREFIX* . We just need to create an upper folder called od-$VERSION to avoid conflicts with different library versions on the same machine. The clean include structure allows also to remove the explicit single include files from the install targets and allows to use directly the cmake command + +@code +install(DIRECTORY ${DETECTORS_INCLUDE_DIR}/od DESTINATION ${OD_INSTALL_INCLUDE_DIR}) +@endcode + +with the DIRECTORY keyword to copy the whole folder. Also I removed the include files from the + +@code +OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${SOURCES}) +@endcode + +since you don't have to compile headers files.It is enough to specify the include folders to find the includes at compile time. It could be possible to add header precompilation, but I believe this would be an advanced step which could be implemented at the end. I still need to check if this way of installing includes using the *DIRECTORY* keyword without specifying the single files is the clearest way. \ No newline at end of file diff --git a/examples/objectdetector/od_pc_global.cpp b/examples/objectdetector/od_pc_global.cpp index 43cc7ef0..ff1c351c 100644 --- a/examples/objectdetector/od_pc_global.cpp +++ b/examples/objectdetector/od_pc_global.cpp @@ -59,7 +59,7 @@ int main(int argc, char *argv[]) detector.init(); //Get a scene - od::ODScenePointCloud<> scene(pointcloud_file); + od::ODScenePointCloud<pcl::PointXYZRGBA> scene(pointcloud_file); od::ODDetections3D * detections = detector.detectOmni(&scene); From 94bf1907c64928875760c9ec6efe58fdc1d8bab5 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Mon, 6 Jun 2016 12:22:13 +0200 Subject: [PATCH 14/79] continues to fix files, mmove -h of detection in global3d to the hpp file in imp --- common/src/utils/ODFeatureDetector2D.cpp | 46 ++- .../detection/ODCADDetector3DGlobal.hpp | 95 +++-- .../od/detectors/global2D/ODFaceRecognizer.h | 37 +- .../global2D/detection/ODCascadeDetector.h | 28 +- .../global2D/detection/ODHOGDetector.h | 147 +++----- .../global2D/training/ODHOGTrainer.h | 183 ++++------ .../global3D/ODPointCloudGlobalMatching.h | 2 +- .../detection/ODCADDetector3DGlobal.h | 114 ------ detectors/src/global2D/ODFaceRecognizer.cpp | 55 +-- .../global2D/detection/ODCascadeDetector.cpp | 61 ++-- .../src/global2D/detection/ODHOGDetector.cpp | 150 ++++++-- .../src/global2D/training/ODHOGTrainer.cpp | 343 ++++++++++-------- .../detection/ODCADDetector3DGlobal.cpp | 2 +- .../misc/detection/ODDetectorMultiAlgo.cpp | 2 +- .../gsoc2016_blog_giacomo.md | 28 +- examples/objectdetector/od_pc_global.cpp | 2 +- .../objectdetector/od_pc_global_files.cpp | 2 +- 17 files changed, 630 insertions(+), 667 deletions(-) rename detectors/impl/od/{ => detectors}/global3D/detection/ODCADDetector3DGlobal.hpp (74%) delete mode 100644 detectors/include/od/detectors/global3D/detection/ODCADDetector3DGlobal.h diff --git a/common/src/utils/ODFeatureDetector2D.cpp b/common/src/utils/ODFeatureDetector2D.cpp index 66500e6e..346bb28a 100644 --- a/common/src/utils/ODFeatureDetector2D.cpp +++ b/common/src/utils/ODFeatureDetector2D.cpp @@ -13,7 +13,6 @@ namespace od mode_ = SIFT; if(use_gpu) { - if(feature_type == "ORB") { mode_ = ORB_GPU; feature_detector_ = cv::cuda::ORB::create(); @@ -27,7 +26,6 @@ namespace od std::cout << "FATAL ERROR cannot create SIFTGPU context" << std::endl; } } else { - if(feature_type == "SIFT") { mode_ = SIFT; feature_detector_ = cv::xfeatures2d::SIFT::create(); @@ -41,27 +39,39 @@ namespace od } } + + ODFeatureDetector2D::ODFeatureDetector2D(FeatureType type) { mode_ = type; - if(type == SIFT) { - feature_detector_ = cv::xfeatures2d::SIFT::create(); - } else if(type == ORB) { - feature_detector_ = cv::ORB::create(); - } else if(type == SURF) { - feature_detector_ = cv::xfeatures2d::SURF::create(); - } else if(type == ORB_GPU) { - feature_detector_ = cv::cuda::ORB::create(); - } else if(type == SIFT_GPU) { - sift_gpu_ = new SiftGPU; - //char * argv[] = {(char *)"-fo", (char *)"-1", (char *)"-v", (char *)"1"}; - char *argv[] = {(char *) "-fo", (char *) "-1", (char *) "-v", (char *) "3", (char *) "-cuda"}; - int argc = sizeof(argv) / sizeof(char *); - sift_gpu_->ParseParam(argc, argv); - if(sift_gpu_->CreateContextGL() != SiftGPU::SIFTGPU_FULL_SUPPORTED) - std::cout << "FATAL ERROR cannot create SIFTGPU context"; + switch(type) + { + case(SIFT) : + feature_detector_ = cv::xfeatures2d::SIFT::create(); + break; + + case(ORB) : + feature_detector_ = cv::ORB::create(); + break; + + case(SURF) : + feature_detector_ = cv::xfeatures2d::SURF::create(); + break; + + case(ORB_GPU) : + feature_detector_ = cv::cuda::ORB::create(); + break; + default : + sift_gpu_ = new SiftGPU(); + char *argv[] = {(char *) "-fo", (char *) "-1", (char *) "-v", (char *) "3", (char *) "-cuda"}; + int argc = sizeof(argv) / sizeof(char *); + sift_gpu_->ParseParam(argc, argv); + if(sift_gpu_->CreateContextGL() != SiftGPU::SIFTGPU_FULL_SUPPORTED) + std::cout << "FATAL ERROR cannot create SIFTGPU context"; + break; + } } diff --git a/detectors/impl/od/global3D/detection/ODCADDetector3DGlobal.hpp b/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp similarity index 74% rename from detectors/impl/od/global3D/detection/ODCADDetector3DGlobal.hpp rename to detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp index 1d5b3c64..5ae9d093 100644 --- a/detectors/impl/od/global3D/detection/ODCADDetector3DGlobal.hpp +++ b/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp @@ -1,23 +1,77 @@ // // Created by sarkar on 10.08.15. // - -#ifndef OPENDETECTION_ODCADDETECTOR3DGLOBAL_HPP -#define OPENDETECTION_ODCADDETECTOR3DGLOBAL_HPP - - +#pragma once +#include "od/common/pipeline/ODDetector.h" + +#include <pcl/pcl_macros.h> +#include <pcl/apps/3d_rec_framework/pipeline/global_nn_classifier.h> +#include <pcl/apps/3d_rec_framework/pc_source/mesh_source.h> +#include <pcl/apps/3d_rec_framework/feature_wrapper/global/vfh_estimator.h> +#include <pcl/apps/3d_rec_framework/feature_wrapper/global/esf_estimator.h> +#include <pcl/apps/3d_rec_framework/feature_wrapper/global/cvfh_estimator.h> +//#include <pcl/apps/3d_rec_framework/tools/openni_frame_source.h> +#include <pcl/apps/3d_rec_framework/utils/metrics.h> +#include <pcl/visualization/pcl_visualizer.h> +#include <pcl/apps/dominant_plane_segmentation.h> +#include <pcl/console/parse.h> namespace od { namespace g3d { + template<typename PointT> + class ODCADDetector3DGlobal : public ODDetector3D<PointT> + { + + public: + ODCADDetector3DGlobal(const std::string & training_data_location = "", const std::string & training_input_location = "") : + ODDetector3D<PointT>(training_data_location), NN(2), desc_name_("esf") + { + this->TRAINED_LOCATION_DENTIFIER_ = "GLOBAL3DVFH"; + this->training_input_location_ = training_input_location; + } + + void init(); + + ODDetections * detect(ODScenePointCloud<PointT> * scene); + + ODDetections3D * detectOmni(ODScenePointCloud<PointT> * scene); + + int getNN() const + { + return NN; + } + + void setNN(int NN) + { + ODCADDetector3DGlobal::NN = NN; + } + + const std::string & getDescName() const + { + return desc_name_; + } + + void setDescName(const std::string & desc_name) + { + desc_name_ = desc_name; + } + + protected: + int NN; + std::string desc_name_; + boost::shared_ptr<pcl::rec_3d_framework::GlobalClassifier<pcl::PointXYZ> > global_; + + }; + template<typename PointT> void ODCADDetector3DGlobal<PointT>::init() { boost::shared_ptr<pcl::rec_3d_framework::MeshSource<pcl::PointXYZ> > mesh_source(new pcl::rec_3d_framework::MeshSource<pcl::PointXYZ>); mesh_source->setPath(this->training_input_location_); - std::string training_dir_specific = this->getSpecificTrainingDataLocation(); + std::string training_dir_specific = this->getSpecificTrainingDataLocation(); mesh_source->setModelScale (1.f); mesh_source->generate(training_dir_specific); @@ -31,7 +85,7 @@ namespace od normal_estimator->setRemoveOutliers(true); normal_estimator->setFactorsForCMR(3, 7); - if(desc_name.compare("vfh") == 0) + if(desc_name_.compare("vfh") == 0) { boost::shared_ptr<pcl::rec_3d_framework::VFHEstimation<pcl::PointXYZ, pcl::VFHSignature308> > vfh_estimator; vfh_estimator.reset(new pcl::rec_3d_framework::VFHEstimation<pcl::PointXYZ, pcl::VFHSignature308>); @@ -45,13 +99,13 @@ namespace od new pcl::rec_3d_framework::GlobalNNPipeline<flann::L1, pcl::PointXYZ, pcl::VFHSignature308>()); global->setDataSource(cast_source); global->setTrainingDir(training_dir_specific); - global->setDescriptorName(desc_name); + global->setDescriptorName(desc_name_); global->setNN(NN); global->setFeatureEstimator(cast_estimator); global->initialize(false); - this->global_ = global; + global_ = global; - } else if(desc_name.compare("cvfh") == 0) + } else if(desc_name_.compare("cvfh") == 0) { boost::shared_ptr<pcl::rec_3d_framework::CVFHEstimation<pcl::PointXYZ, pcl::VFHSignature308> > vfh_estimator; vfh_estimator.reset(new pcl::rec_3d_framework::CVFHEstimation<pcl::PointXYZ, pcl::VFHSignature308>); @@ -62,15 +116,15 @@ namespace od vfh_estimator); boost::shared_ptr<pcl::rec_3d_framework::GlobalNNPipeline<Metrics::HistIntersectionUnionDistance, pcl::PointXYZ, pcl::VFHSignature308> > global( - new pcl::rec_3d_framework::GlobalNNPipeline<Metrics::HistIntersectionUnionDistance, pcl::PointXYZ, pcl::VFHSignature308>()); + new pcl::rec_3d_framework::GlobalNNPipeline<Metrics::HistIntersectionUnionDistance, pcl::PointXYZ, pcl::VFHSignature308>()); global->setDataSource(cast_source); global->setTrainingDir(training_dir_specific); - global->setDescriptorName(desc_name); + global->setDescriptorName(desc_name_); global->setFeatureEstimator(cast_estimator); global->setNN(NN); global->initialize(false); - this->global_ = global; - } else if(desc_name.compare("esf") == 0) + global_ = global; + } else if(desc_name_.compare("esf") == 0) { boost::shared_ptr<pcl::rec_3d_framework::ESFEstimation<pcl::PointXYZ, pcl::ESFSignature640> > estimator; estimator.reset(new pcl::rec_3d_framework::ESFEstimation<pcl::PointXYZ, pcl::ESFSignature640>); @@ -83,11 +137,11 @@ namespace od new pcl::rec_3d_framework::GlobalNNPipeline<flann::L1, pcl::PointXYZ, pcl::ESFSignature640>()); global->setDataSource(cast_source); global->setTrainingDir(training_dir_specific); - global->setDescriptorName(desc_name); + global->setDescriptorName(desc_name_); global->setFeatureEstimator(cast_estimator); global->setNN(NN); global->initialize(false); - this->global_ = global; + global_ = global; } else { std::cout << "FATAL: descriptor type not available."; @@ -129,9 +183,7 @@ namespace od dps.getTableCoefficients(table_plane_); - float dist_ = 0.03f; - - for(size_t i = 0; i < clusters.size(); i++) + for(size_t i = 0; i < clusters.size(); ++i) { global_->setInputCloud(xyz_points); @@ -193,8 +245,7 @@ namespace od return detections; } + + } } - - -#endif //OPENDETECTION_ODCADDETECTOR3DGLOBAL_HPP diff --git a/detectors/include/od/detectors/global2D/ODFaceRecognizer.h b/detectors/include/od/detectors/global2D/ODFaceRecognizer.h index 96bc6619..dcea7e59 100644 --- a/detectors/include/od/detectors/global2D/ODFaceRecognizer.h +++ b/detectors/include/od/detectors/global2D/ODFaceRecognizer.h @@ -26,10 +26,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */// // Created by sarkar on 16.06.15. // - -#ifndef OPENDETECTION_ODPOINTCLOUDGLOBALMATCHING_H -#define OPENDETECTION_ODPOINTCLOUDGLOBALMATCHING_H - +#pragma once #include "od/common/pipeline/ODDetector.h" #include "od/common/pipeline/ODTrainer.h" #include "od/common/utils/utils.h" @@ -64,12 +61,7 @@ namespace od OD_DEFINE_ENUM_WITH_STRING_CONVERSIONS(FaceRecogType, (OD_FACE_FISCHER)(OD_FACE_EIGEN)) - ODFaceRecognizer(FaceRecogType recogtype = OD_FACE_EIGEN, int num_components = 0, double threshold = DBL_MAX) - : recogtype_(recogtype), num_components_(num_components), threshold_(threshold), im_height_(0), im_width_(0) - { - TRAINED_DATA_IDENTIFIER_ = "FACERECOG"; - TRAINED_DATA_EXT_ = "facerec.xml"; - } + ODFaceRecognizer(FaceRecogType recog_type = OD_FACE_EIGEN, int num_components = 0, double threshold = DBL_MAX); void init(); @@ -79,17 +71,17 @@ namespace od int train(); - ODDetections *detect(ODSceneImage *scene); + ODDetections * detect(ODSceneImage * scene); - FaceRecogType const &getRecogtype() const + const FaceRecogType & getRecogtype() const { - return recogtype_; + return recog_type_; } - void setRecogtype(FaceRecogType const &recogtype_) + void setRecogtype(const FaceRecogType & recog_type) { - ODFaceRecognizer::recogtype_ = recogtype_; + recog_type_ = recog_type; } int getThreshold() const @@ -97,9 +89,9 @@ namespace od return threshold_; } - void setThreshold(int threshold_) + void setThreshold(int threshold) { - ODFaceRecognizer::threshold_ = threshold_; + threshold_ = threshold; } int getNumComponents() const @@ -107,14 +99,14 @@ namespace od return num_components_; } - void setNumComponents(int num_components_) + void setNumComponents(int num_components) { - ODFaceRecognizer::num_components_ = num_components_; + num_components_ = num_components; } protected: - cv::Ptr<cv::face::FaceRecognizer> cvrecognizer_; - FaceRecogType recogtype_; + cv::Ptr<cv::face::FaceRecognizer> cv_recognizer_; + FaceRecogType recog_type_; int im_width_; int im_height_; @@ -123,11 +115,10 @@ namespace od private: - static void read_csv(const std::string &filename, std::vector<cv::Mat> &images, std::vector<int> &labels, char separator = ';'); + void read_csv(const std::string & file_name, std::vector<cv::Mat> & images, std::vector<int> & labels, char separator = ';'); }; /** \example objectdetector/od_image_facerecog.cpp */ } } -#endif //OPENDETECTION_ODPOINTCLOUDGLOBALMATCHING_H diff --git a/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h b/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h index 2f07b3c7..e3e6b892 100644 --- a/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h +++ b/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h @@ -32,7 +32,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/common/utils/utils.h" #include "od/common/utils/ODFeatureDetector2D.h" -#include <iostream> #include <opencv2/opencv.hpp> namespace od @@ -52,30 +51,21 @@ namespace od { public: - ODCascadeDetector(std::string const &trained_data_location = "", double scaleFactor = 1.1, int minNeighbors = 3, int flags = 0, cv::Size minSize = cv::Size(), cv::Size maxSize = cv::Size()) - : ODDetector2D(trained_data_location), scaleFactor_(scaleFactor), minNeighbors_(minNeighbors), minSize_(minSize), maxSize_(maxSize) - { - TRAINED_LOCATION_DENTIFIER_ = "CASCADE"; - TRAINED_DATA_ID_ = "cascade.xml"; - metainfo_ = true; - } + ODCascadeDetector(const std::string & trained_data_location = "", double scale_factor = 1.1, int min_neighbors = 3, + int flags = 0, const cv::Size & min_size = cv::Size(), const cv::Size & max_size = cv::Size()); - void init() - { - haar_cascade_ = std::make_shared<cv::CascadeClassifier>(fileutils::getFirstFile(getSpecificTrainingDataLocation(), - TRAINED_DATA_ID_)); - } + void init(); - ODDetections2D *detectOmni(ODSceneImage *scene); - ODDetections* detect(ODSceneImage *scene); + ODDetections2D * detectOmni(ODSceneImage *scene); + ODDetections * detect(ODSceneImage *scene); private: std::shared_ptr<cv::CascadeClassifier> haar_cascade_; - double scaleFactor_; - int minNeighbors_; - cv::Size minSize_; - cv::Size maxSize_; + double scale_factor_; + int min_neighbors_; + cv::Size min_size_; + cv::Size max_size_; }; /** \examples objectdetector/od_image_cascade.cpp diff --git a/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h b/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h index e3df86f0..93f1cdbd 100644 --- a/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h +++ b/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h @@ -26,10 +26,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */// // Created by sarkar on 15.07.15. // - -#ifndef OPENDETECTION_ODHOGMULTISCALEDETECTOR_H -#define OPENDETECTION_ODHOGMULTISCALEDETECTOR_H - +#pragma once #include "od/common/pipeline/ODDetector.h" #include "od/common/pipeline/ODScene.h" #include "od/common/utils/utils.h" @@ -64,114 +61,56 @@ namespace od OD_DEFINE_ENUM_WITH_STRING_CONVERSIONS(SVMType, (OD_CUSTOM)(OD_DEFAULT_PEOPLE)(OD_DAIMLER_PEOPLE)(OD_FILE)) - ODHOGDetector(std::string const &trained_data_location_ = "", cv::Size winsize = cv::Size(64,128), cv::Size blocksize = cv::Size(16,16), cv::Size blockstride = cv::Size(8,8), cv::Size cellsize = cv::Size(8,8), float hitshreshold = 0.0): - ODDetector2D(trained_data_location_), winSize(winsize), blockSize(blocksize), blockStride(blockstride), - cellSize(cellsize), hitThreshold(hitshreshold), hog_(winSize, blockSize, blockStride, cellSize, 9, 1, -1, - cv::HOGDescriptor::L2Hys, 0.2, false, cv::HOGDescriptor::DEFAULT_NLEVELS) - { - TRAINED_LOCATION_DENTIFIER_ = "HOG"; - TRAINED_DATA_ID_ = "hog.xml"; - metainfo_ = true; - svmtype_ = OD_DEFAULT_PEOPLE; - - if (trained_data_location_ != "") - svmtype_ = OD_FILE; - } + ODHOGDetector(const std::string & trained_data_location_ = "", const cv::Size & win_size = cv::Size(64,128), + const cv::Size & block_size = cv::Size(16,16), const cv::Size & block_stride = cv::Size(8,8), const cv::Size & cell_size = cv::Size(8,8), + float hit_threshold = 0.0): + ODDetector2D(trained_data_location_), win_size_(win_size), block_size_(block_size), block_stride_(block_stride), + cell_size_(cell_size), hit_threshold_(hit_threshold), hog_(win_size_, block_size, block_stride, cell_size, 9, 1, -1, + cv::HOGDescriptor::L2Hys, 0.2, false, cv::HOGDescriptor::DEFAULT_NLEVELS){} void init(); - void load(std::string filename); - - void setSVMFromFile(std::string fileName); - - void setSVMDetector(std::vector<float> svmdetector) - { - hog_.setSVMDetector(svmdetector); - } - - ODDetections2D *detectOmni(ODSceneImage *scene); - ODDetections *detect(ODSceneImage *scene); - - int detect(ODScene *scene, std::vector<ODDetection *> &detections) - { } - - - void setTrainedDataLocation(std::string trained_data_location_) - { - this->trained_data_location_ = trained_data_location_; - this->svmtype_ = OD_FILE; - } - - SVMType const &getSvmtype() const - { - return svmtype_; - } - - void setSvmtype(SVMType const &svmtype_) - { - ODHOGDetector::svmtype_ = svmtype_; - } - - cv::Size const &getWinSize() const - { - return winSize; - } - - void setWinSize(cv::Size const &winSize) - { - ODHOGDetector::winSize = winSize; - } - - cv::Size const &getBlockSize() const - { - return blockSize; - } - - void setBlockSize(cv::Size const &blockSize) - { - ODHOGDetector::blockSize = blockSize; - } - - cv::Size const &getBlockStride() const - { - return blockStride; - } - - void setBlockStride(cv::Size const &blockStride) - { - ODHOGDetector::blockStride = blockStride; - } - - cv::Size const &getCellSize() const - { - return cellSize; - } - - void setCellSize(cv::Size const &cellSize) - { - ODHOGDetector::cellSize = cellSize; - } - - float getHitThreshold() const - { - return hitThreshold; - } - - void setHitThreshold(float hitThreshold) - { - ODHOGDetector::hitThreshold = hitThreshold; - } + void load(const std::string & file_name); + + void setSVMFromFile(const std::string & file_name); + + void setSVMDetector(std::vector<float> svm_detector); + + ODDetections2D * detectOmni(ODSceneImage * scene); + ODDetections * detect(ODSceneImage * scene); + + int detect(ODScene * scene, std::vector<ODDetection *> & detections); + + void setTrainedDataLocation(const std::string & trained_data_location); + + const SVMType & getSvmtype() const; + void setSvmtype(const SVMType & svm_type_); + + const cv::Size & getWinSize() const; + void setWinSize(const cv::Size & win_size); + + const cv::Size & getBlockSize() const; + void setBlockSize(const cv::Size & block_size); + + const cv::Size & getBlockStride() const; + void setBlockStride(const cv::Size & block_stride); + + const cv::Size & getCellSize() const; + void setCellSize(const cv::Size & cell_size); + + float getHitThreshold() const; + void setHitThreshold(float hit_threshold); void printParameters(); protected: //properteis - cv::Size winSize; - cv::Size blockSize; - cv::Size blockStride; - cv::Size cellSize; + cv::Size win_size_; + cv::Size block_size_; + cv::Size block_stride_; + cv::Size cell_size_; - float hitThreshold; + float hit_threshold_; cv::HOGDescriptor hog_; SVMType svmtype_; @@ -188,5 +127,3 @@ namespace od } } - -#endif //OPENDETECTION_ODHOGMULTISCALEDETECTOR_H \ No newline at end of file diff --git a/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h b/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h index 32772631..f92e26d4 100644 --- a/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h +++ b/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h @@ -26,17 +26,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */// // Created by sarkar on 13.08.15. // - -#ifndef OPENDETECTION_ODHOGTRAINER_H -#define OPENDETECTION_ODHOGTRAINER_H - +#pragma once #include <opencv2/objdetect.hpp> #include "od/common/pipeline/ODTrainer.h" #include "od/common/utils/utils.h" - - - namespace od { namespace g2d @@ -54,131 +48,102 @@ namespace od { public: - ODHOGTrainer(std::string const &training_input_location_ = "", std::string const &trained_data_location_ = "", cv::Size winsize = cv::Size(64,128), cv::Size blocksize = cv::Size(16,16), cv::Size blockstride = cv::Size(8,8), cv::Size cellsize = cv::Size(8,8), float hitshreshold = 0.0): - ODTrainer(training_input_location_, trained_data_location_), winSize(winsize), blockSize(blocksize), blockStride(blockstride), - cellSize(cellsize), hog_(winSize, blockSize, blockStride, cellSize, 9) - { - - TRAINED_LOCATION_DENTIFIER_ = "HOG"; - TRAINED_DATA_ID_ = "hog.xml"; - - //algo parameter init - trainingPadding = cv::Size(0, 0); - start_hog_pos = cv::Point(15, 15); - nofeatures_neg = 10; - winStride = cv::Size(); - train_hard_negetive_ = false; - - if(trained_data_location_ != "") - { - posSamplesDir = training_input_location_ + "/pos"; - negSamplesDir = training_input_location_ + "/neg"; - } - - - //internal data - FileUtils::createTrainingDir(getSpecificTrainingDataLocation()); - featuresFile = getSpecificTrainingDataLocation() + "/features.dat"; - svmModelFile = getSpecificTrainingDataLocation() + "/svmlightmodel.dat"; - svmModelHard = getSpecificTrainingDataLocation() + "/svmlightmodelhard.dat"; - descriptorVectorFile = getSpecificTrainingDataLocation() + "/descriptorvector.dat"; - descriptorVectorFile = getSpecificTrainingDataLocation() + "/descriptorvectorHard.dat"; - - - } + ODHOGTrainer(const std::string & training_input_location_ = "", const std::string & trained_data_location_ = "", + const cv::Size & win_size = cv::Size(64,128), const cv::Size & block_size = cv::Size(16,16), + const cv::Size & block_stride = cv::Size(8,8), const cv::Size & cell_size = cv::Size(8,8), float hits_threshold = 0.0); int train(); - void init() {} + void init(){} - std::string const &getPosSamplesDir() const + const std::string & getPosSamplesDir() const { - return posSamplesDir; + return pos_samples_dir_; } - void setPosSamplesDir(std::string const &posSamplesDir) + void setPosSamplesDir(const std::string & pos_samples_dir) { - ODHOGTrainer::posSamplesDir = posSamplesDir; + pos_samples_dir_ = pos_samples_dir; } - std::string const &getNegSamplesDir() const + const std::string & getNegSamplesDir() const { - return negSamplesDir; + return neg_samples_dir_; } - void setNegSamplesDir(std::string const &negSamplesDir) + void setNegSamplesDir(const std::string & neg_samples_dir) { - ODHOGTrainer::negSamplesDir = negSamplesDir; + neg_samples_dir_ = neg_samples_dir; } int getNOFeaturesNeg() const { - return nofeatures_neg; + return nofeatures_neg_; } void setNOFeaturesNeg(int featno) { - ODHOGTrainer::nofeatures_neg = featno; + no_features_neg_ = featno; } - cv::Point const &getStartHogPos() const + const cv::Point & getStartHogPos() const { - return start_hog_pos; + return start_hog_pos_; } - void setStartHogPos(cv::Point const &start_hog_pos) + void setStartHogPos(const cv::Point & start_hog_pos) { - ODHOGTrainer::start_hog_pos = start_hog_pos; + start_hog_pos_ = start_hog_pos; } - cv::Size const &getWinSize() const + const cv::Size & getWinSize() const { - return winSize; + return win_size_; } - void setWinSize(cv::Size const &winSize) + void setWinSize(const cv::Size & win_size) { - ODHOGTrainer::winSize = winSize; + win_size_ = win_size; } - cv::Size const &getBlockSize() const + const cv::Size & getBlockSize() const { - return blockSize; + return block_size_; } - void setBlockSize(cv::Size const &blockSize) + void setBlockSize(const cv::Size & block_size) { - ODHOGTrainer::blockSize = blockSize; + block_size_ = block_size; } - cv::Size const &getBlockStride() const + const cv::Size & getBlockStride() const { - return blockStride; + return block_stride_; } - void setBlockStride(cv::Size const &blockStride) + void setBlockStride(const cv::Size & block_stride) { - ODHOGTrainer::blockStride = blockStride; + block_stride_ = block_stride; } - cv::Size const &getCellSize() const + const cv::Size & getCellSize() const { - return cellSize; + return cell_size_; } - void setCellSize(cv::Size const &cellSize) + void setCellSize(const cv::Size & cell_size) { - ODHOGTrainer::cellSize = cellSize; + cell_size_ = cell_size; } - cv::Size const &getTrainingPadding() const + const cv::Size & getTrainingPadding() const { - return trainingPadding; + return training_padding_; } - void setTrainingPadding(cv::Size const &trainingPadding) + void setTrainingPadding(const cv::Size & training_padding) { - ODHOGTrainer::trainingPadding = trainingPadding; + training_padding_ = training_padding; } bool isTrainHardNegetive() const @@ -188,80 +153,74 @@ namespace od void setTrainHardNegetive(bool train_hard_negetive) { - ODHOGTrainer::train_hard_negetive_ = train_hard_negetive; + train_hard_negetive_ = train_hard_negetive; } double getHitThreshold() const { - return hitThreshold; + return hit_threshold_; } protected: //hog specific - cv::Size winSize; - cv::Size blockSize; - cv::Size blockStride; - cv::Size cellSize; + cv::Size win_size_; + cv::Size block_size_; + cv::Size block_stride_; + cv::Size cell_size_; cv::HOGDescriptor hog_; //algo specific - cv::Size trainingPadding; - cv::Point start_hog_pos; - int nofeatures_neg; - cv::Size winStride; + cv::Size training_padding_; + cv::Point start_hog_pos_; + int no_features_neg__; + cv::Size win_stride_; bool train_hard_negetive_; //directories - std::string posSamplesDir; - std::string negSamplesDir; + std::string pos_samples_dir_; + std::string neg_samples_dir__; //properties retained - double hitThreshold; + double hit_threshold_; private: - //internal training data - std::string featuresFile; - std::string svmModelFile; - std::string svmModelHard; - std::string descriptorVectorFile; - std::string descriptorVectorHard; + void readDescriptorsFromFile(const std::string & file_name, std::vector<float> & descriptor_vector); - void readDescriptorsFromFile(std::string fileName, std::vector<float> &descriptor_vector); + void save(const std::string & filename); - void save(std::string filename); + void createHardTrainingData(const cv::HOGDescriptor & hog, double hit_threshold, + const std::vector<std::string> & neg_file_names); - void createHardTrainingData(cv::HOGDescriptor const &hog, double const hitThreshold, - std::vector<std::string> const &negFileNames); + void calculateFeaturesFromImageLoc(const cv::Mat & image_data, std::vector<float> & feature_vector, + const cv::HOGDescriptor & hog, const cv::Point & start_pos); - void calculateFeaturesFromImageLoc(cv::Mat const &imageData, std::vector<float> &featureVector, - cv::HOGDescriptor const &hog, cv::Point startpos); + void detectTrainingSetTest(const cv::HOGDescriptor & hog, double hit_threshold, + const std::vector<std::string> & pos_file_names, const std::vector<std::string> & neg_file_names); - void detectTrainingSetTest(cv::HOGDescriptor const &hog, double const hitThreshold, - std::vector<std::string> const &posFileNames, std::vector<std::string> const &negFileNames); + void calculateFeaturesFromInput(const std::string & image_filename, std::vector<float> & featureVector, + cv::HOGDescriptor & hog); - void calculateFeaturesFromInput(std::string const &imageFilename, std::vector<float> &featureVector, - cv::HOGDescriptor &hog); + void saveDescriptorVectorToFile(std::vector<float> & descriptor_vector, std::vector<unsigned int> & vector_indices, + std::string file_name); - void saveDescriptorVectorToFile(std::vector<float> &descriptorVector, std::vector<unsigned int> &_vectorIndices, - std::string fileName); + void handleNegetivefile(const std::string & image_filename, cv::HOGDescriptor & hog, std::fstream & file); - void handleNegetivefile(std::string const imageFilename, cv::HOGDescriptor &hog, std::fstream &file); + double trainWithSVMLight(const std::string & svm_model_file, const std::string & svm_descriptor_file, std::vector<float> & descriptor_vector); - double trainWithSVMLight(std::string svmModelFile, std::string svmDescriptorFile, std::vector<float> &descriptorVector); + std::string features_file_; + std::string svm_model_file_; + std::string svm_model_hard_; + std::string descriptor_vector_file_; + std::string descriptor_vector_hard_; }; /** \example objectdetector/od_hog_train.cpp */ - } } - - - -#endif //OPENDETECTION_ODHOGTRAINER_H diff --git a/detectors/include/od/detectors/global3D/ODPointCloudGlobalMatching.h b/detectors/include/od/detectors/global3D/ODPointCloudGlobalMatching.h index b41c990b..6230ad4a 100644 --- a/detectors/include/od/detectors/global3D/ODPointCloudGlobalMatching.h +++ b/detectors/include/od/detectors/global3D/ODPointCloudGlobalMatching.h @@ -33,7 +33,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/common/pipeline/ODDetector.h" #include "od/common/pipeline/ODTrainer.h" #include "od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h" -#include "od/detectors/global3D/detection/ODCADDetector3DGlobal.h" +#include "od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp" namespace od { diff --git a/detectors/include/od/detectors/global3D/detection/ODCADDetector3DGlobal.h b/detectors/include/od/detectors/global3D/detection/ODCADDetector3DGlobal.h deleted file mode 100644 index 4bc1984e..00000000 --- a/detectors/include/od/detectors/global3D/detection/ODCADDetector3DGlobal.h +++ /dev/null @@ -1,114 +0,0 @@ -/* -Copyright (c) 2015, Kripasindhu Sarkar -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions 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. - * Neither the name of the copyright holder(s) nor the - names of its contributors may 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 COPYRIGHT HOLDER 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. -*/// -// Created by sarkar on 16.06.15. -// - -#ifndef OPENDETECTION_ODPOINTCLOUDGLOBALMATCHINGDETECTOR_H -#define OPENDETECTION_ODPOINTCLOUDGLOBALMATCHINGDETECTOR_H - -#include "od/common/pipeline/ODDetector.h" - -#include <pcl/pcl_macros.h> -#include <pcl/apps/3d_rec_framework/pipeline/global_nn_classifier.h> -#include <pcl/apps/3d_rec_framework/pc_source/mesh_source.h> -#include <pcl/apps/3d_rec_framework/feature_wrapper/global/vfh_estimator.h> -#include <pcl/apps/3d_rec_framework/feature_wrapper/global/esf_estimator.h> -#include <pcl/apps/3d_rec_framework/feature_wrapper/global/cvfh_estimator.h> -//#include <pcl/apps/3d_rec_framework/tools/openni_frame_source.h> -#include <pcl/apps/3d_rec_framework/utils/metrics.h> -#include <pcl/visualization/pcl_visualizer.h> -#include <pcl/apps/dominant_plane_segmentation.h> -#include <pcl/console/parse.h> - -namespace od -{ - namespace g3d - { - /** \brief Detector based on 3D global features like VFH, ESF, CVFH etc. - * - * This class uses PCL 3d_recognition_framework in the background for the detection. First train your data using ODCADDetectTrainer3DGlobal and use this class for the detection. - * This detection will assume the presence of a plane (like a table top) in the pointcloud. It segments the point cloud assuming the presence of a plane and using a simple Euclidian segmentation. - * After that it finds the global features of each segmented scene match them with the trained data thereby performing a clssification. Read the documentation of ODCADDetectTrainer3DGlobal to know how - * the training data should be arranged and trained to get meaningful detection. - * - * This class provides a detection for each segmented scenes which matches them the best. So the number of positive detection is same as the number of possible segmented scene in the point cloud. - * - * \author Kripasindhu Sarkar - * - */ - template<typename PointT = pcl::PointXYZRGBA> - class ODCADDetector3DGlobal : public ODDetector3D<PointT> - { - - public: - ODCADDetector3DGlobal(std::string const &training_data_location = "", std::string const &training_input_location = "") : ODDetector3D<PointT>(training_data_location), - NN(2), desc_name("esf") - { - this->TRAINED_LOCATION_DENTIFIER_ = "GLOBAL3DVFH"; - this->training_input_location_ = training_input_location; - } - - void init(); - - ODDetections *detect(ODScenePointCloud<PointT> *scene); - - ODDetections3D *detectOmni(ODScenePointCloud<PointT> *scene); - - int getNN() const - { - return NN; - } - - void setNN(int NN) - { - ODCADDetector3DGlobal::NN = NN; - } - - std::string const &getDescName() const - { - return desc_name; - } - - void setDescName(std::string const &desc_name) - { - ODCADDetector3DGlobal::desc_name = desc_name; - } - - protected: - int NN; - std::string desc_name; - boost::shared_ptr<pcl::rec_3d_framework::GlobalClassifier<pcl::PointXYZ> > global_; - - }; - /** \example objectdetector/od_pc_global_real_time.cpp - * \example objectdetector/od_pc_global_files.cpp - */ - } -} - -#include "od/global3D/detection/ODCADDetector3DGlobal.hpp" -#endif //OPENDETECTION_ODPOINTCLOUDGLOBALMATCHINGDETECTOR_H diff --git a/detectors/src/global2D/ODFaceRecognizer.cpp b/detectors/src/global2D/ODFaceRecognizer.cpp index 5a7d8d3b..a5cec425 100644 --- a/detectors/src/global2D/ODFaceRecognizer.cpp +++ b/detectors/src/global2D/ODFaceRecognizer.cpp @@ -29,22 +29,27 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/global2D/ODFaceRecognizer.h" -using namespace cv; -using namespace std; - namespace od { namespace g2d { + + ODFaceRecognizer::ODFaceRecognizer(FaceRecogType recog_type, int num_components, double threshold) + : recog_type_(recog_type), num_components_(num_components), threshold_(threshold), im_height_(0), im_width_(0) + { + TRAINED_DATA_IDENTIFIER_ = "FACERECOG"; + TRAINED_DATA_EXT_ = "facerec.xml"; + } + void ODFaceRecognizer::init() { - switch(recogtype_) + switch(recog_type_) { case OD_FACE_FISCHER: - cvrecognizer_ = cv::face::createFisherFaceRecognizer(num_components_, threshold_); + cv_recognizer_ = cv::face::createFisherFaceRecognizer(num_components_, threshold_); break; case OD_FACE_EIGEN: - cvrecognizer_ = cv::face::createEigenFaceRecognizer(num_components_, threshold_); + cv_recognizer_ = cv::face::createEigenFaceRecognizer(num_components_, threshold_); break; default: std::cout << "FATAL: FACETYPE NOT FOUND!"; @@ -66,33 +71,33 @@ namespace od std::vector<std::string> files; fileutils::getFilesInDirectoryRec(getSpecificTrainingDataLocation(), TRAINED_DATA_EXT_, files); - if (files.size() == 0) + if(files.size() == 0) { - std::cout << "FATAL: Trained data not found" << endl; + std::cout << "FATAL: Trained data not found" << std::endl; exit(1); } //choose the first - cvrecognizer_->load(files[0]); + cv_recognizer_->load(files[0]); } } int ODFaceRecognizer::train() { - vector<cv::Mat> images; - vector<int> labels; + std::vector<cv::Mat> images; + std::vector<int> labels; try { read_csv(training_input_location_, images, labels); - } catch(cv::Exception &e) + } catch(cv::Exception & e) { - cerr << "Error opening file \"" << training_input_location_ << "\". Reason: " << e.msg << endl; + std::cerr << "Error opening file \"" << training_input_location_ << "\". Reason: " << e.msg << std::endl; exit(1); } - cvrecognizer_->train(images, labels); + cv_recognizer_->train(images, labels); fileutils::createTrainingDir(getSpecificTrainingDataLocation()); - cvrecognizer_->save(getSpecificTrainingDataLocation() + "/trained." + TRAINED_DATA_EXT_); + cv_recognizer_->save(getSpecificTrainingDataLocation() + "/trained." + TRAINED_DATA_EXT_); trained_ = true; //the training set has atleast one image @@ -100,10 +105,10 @@ namespace od im_height_ = images[0].rows; } - ODDetections *ODFaceRecognizer::detect(ODSceneImage *scene) + ODDetections * ODFaceRecognizer::detect(ODSceneImage * scene) { cv::Mat face_edited; - cvtColor(scene->getCVImage(), face_edited, CV_BGR2GRAY); + cv::cvtColor(scene->getCVImage(), face_edited, CV_BGR2GRAY); if(trained_) { @@ -112,28 +117,28 @@ namespace od int label = 100; double confidence; - cvrecognizer_->predict(face_edited, label, confidence); + cv_recognizer_->predict(face_edited, label, confidence); //fill in the detection - ODDetection2D *detection = new ODDetection2D(ODDetection::OD_DETECTION_CLASS, std::to_string(label), confidence); - ODDetections2D *detections = new ODDetections2D; + ODDetection2D * detection = new ODDetection2D(ODDetection::OD_DETECTION_CLASS, std::to_string(label), confidence); + ODDetections2D * detections = new ODDetections2D; detections->push_back(detection); return detections; } - void ODFaceRecognizer::read_csv(const string &filename, vector<cv::Mat> &images, vector<int> &labels, char separator) + void ODFaceRecognizer::read_csv(const std::string & filename, std::vector<cv::Mat> & images, std::vector<int> & labels, char separator) { - std::ifstream file(filename.c_str(), ifstream::in); + std::ifstream file(filename.c_str(), std::ifstream::in); if(!file) { - string error_message = "No valid input file was given, please check the given filename."; + std::string error_message("No valid input file was given, please check the given filename."); CV_Error(CV_StsBadArg, error_message); } - string line, path, classlabel; + std::string line, path, classlabel; while(getline(file, line)) { - stringstream liness(line); + std::stringstream liness(line); getline(liness, path, separator); getline(liness, classlabel); if(!path.empty() && !classlabel.empty()) diff --git a/detectors/src/global2D/detection/ODCascadeDetector.cpp b/detectors/src/global2D/detection/ODCascadeDetector.cpp index d544b024..8e02cf06 100644 --- a/detectors/src/global2D/detection/ODCascadeDetector.cpp +++ b/detectors/src/global2D/detection/ODCascadeDetector.cpp @@ -29,38 +29,52 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/global2D/detection/ODCascadeDetector.h" -using namespace cv; -using namespace std; - namespace od { namespace g2d { - ODDetections2D *ODCascadeDetector::detectOmni(ODSceneImage *scene) + + ODCascadeDetector::ODCascadeDetector(const std::string & trained_data_location, double scale_factor, int min_neighbors, int flags, + const cv::Size & min_size, const cv::Size & max_size) + : ODDetector2D(trained_data_location), scale_factor_(scale_factor), min_neighbors_(min_neighbors), + min_size_(min_size), max_size_(max_size) + { + TRAINED_LOCATION_DENTIFIER_ = "CASCADE"; + TRAINED_DATA_ID_ = "cascade.xml"; + metainfo_ = true; + } + + void ODCascadeDetector::init() { - Mat gray; - cvtColor(scene->getCVImage(), gray, CV_BGR2GRAY); + haar_cascade_ = std::make_shared<cv::CascadeClassifier>(fileutils::getFirstFile(getSpecificTrainingDataLocation(), + TRAINED_DATA_ID_)); + } + + ODDetections2D * ODCascadeDetector::detectOmni(ODSceneImage * scene) + { + cv::Mat gray; + cv::cvtColor(scene->getCVImage(), gray, CV_BGR2GRAY); // Find the faces in the frame: - vector<Rect_<int> > faces; - haar_cascade_->detectMultiScale(gray, faces, scaleFactor_, minNeighbors_, 0, minSize_, maxSize_); + std::vector<cv::Rect_<int> > faces; + haar_cascade_->detectMultiScale(gray, faces, scale_factor_, min_neighbors_, 0, min_size_, max_size_); //always create detections - ODDetections2D *detections = new ODDetections2D; + ODDetections2D * detections = new ODDetections2D; cv::Mat viz = scene->getCVImage().clone(); - - for(int i = 0; i < faces.size(); i++) + cv::Rect face_i; + for(size_t i = 0; i < faces.size(); ++i) { // Process face by face: - Rect face_i = faces[i]; + face_i = faces[i]; - ODDetection2D *detection2D = new ODDetection2D(ODDetection::OD_DETECTION_CLASS, "FACE", 1); + ODDetection2D * detection2D = new ODDetection2D(ODDetection::OD_DETECTION_CLASS, "FACE", 1); detection2D->setBoundingBox(face_i); detections->push_back(detection2D); if(metainfo_) { - rectangle(viz, face_i, CV_RGB(0, 255, 0), 1); + cv::rectangle(viz, face_i, CV_RGB(0, 255, 0), 1); } } detections->setMetainfoImage(viz); @@ -68,26 +82,23 @@ namespace od return detections; } - ODDetections* ODCascadeDetector::detect(ODSceneImage *scene) + ODDetections * ODCascadeDetector::detect(ODSceneImage * scene) { //always create detections - ODDetections *detections = new ODDetections; + ODDetections * detections = new ODDetections; - Mat gray; - cvtColor(scene->getCVImage(), gray, CV_BGR2GRAY); + cv::Mat gray; + cv::cvtColor(scene->getCVImage(), gray, CV_BGR2GRAY); // Find the faces in the frame: - vector<Rect_<int> > faces; - + std::vector<cv::Rect_<int> > faces; - cv::Size imsize = gray.size(); //hack for single detection, //note: maxsize = minsize = size of input image for single window detection //todo: implement in some other way of fast single detection; currently this will work, but maynot be fast - haar_cascade_->detectMultiScale(gray, faces, 5, minNeighbors_, 0, gray.size(), gray.size()); - if (faces.size() > 0) + haar_cascade_->detectMultiScale(gray, faces, 5, min_neighbors_, 0, gray.size(), gray.size()); + if(faces.size() > 0) { - ODDetection *detection = new ODDetection(ODDetection::OD_DETECTION_CLASS, "FACE", 1); - detections->push_back(detection); + detections->push_back(new ODDetection(ODDetection::OD_DETECTION_CLASS, "FACE", 1)); } return detections; } diff --git a/detectors/src/global2D/detection/ODHOGDetector.cpp b/detectors/src/global2D/detection/ODHOGDetector.cpp index c455e44a..8c276afe 100644 --- a/detectors/src/global2D/detection/ODHOGDetector.cpp +++ b/detectors/src/global2D/detection/ODHOGDetector.cpp @@ -36,10 +36,23 @@ namespace od namespace g2d { + ODHOGDetector::ODHOGDetector(const std::string & trained_data_location_ = "", const cv::Size & win_size = cv::Size(64,128), + const cv::Size & block_size = cv::Size(16,16), const cv::Size & block_stride = cv::Size(8,8), const cv::Size & cell_size = cv::Size(8,8), + float hit_threshold = 0.0) + { + TRAINED_LOCATION_DENTIFIER_ = "HOG"; + TRAINED_DATA_ID_ = "hog.xml"; + metainfo_ = true; + svm_type_ = OD_DEFAULT_PEOPLE; + + if (trained_data_location_ != "") + svm_type_ = OD_FILE; + } + void ODHOGDetector::init() { - switch(svmtype_) + switch(svm_type_) { case OD_DEFAULT_PEOPLE: hog_.setSVMDetector(cv::HOGDescriptor::getDefaultPeopleDetector()); @@ -47,8 +60,8 @@ namespace od //hog_.save(getSpecificTrainingDataLocation() + "/defaultpeople." + TRAINED_DATA_EXT_); break; case OD_DAIMLER_PEOPLE: - hog_.winSize = cv::Size(48, 96); - hitThreshold =1.2; + hog_.win_size = cv::Size(48, 96); + hit_threshold = 1.2; hog_.setSVMDetector(cv::HOGDescriptor::getDaimlerPeopleDetector()); cout << "HOG TYPE: OpenCV Daimler People" << endl; //hog_.save(getSpecificTrainingDataLocation() + "/daimlerpeople." + TRAINED_DATA_EXT_); @@ -64,34 +77,36 @@ namespace od printParameters(); } - void ODHOGDetector::load(std::string filename) + void ODHOGDetector::load(const std::string & file_name) { - cv::FileStorage fs(filename, cv::FileStorage::READ); - fs["hitThreshold"] >> hitThreshold; - cv::FileNode fn = fs[cv::FileStorage::getDefaultObjectName(filename)]; + cv::FileStorage fs(file_name, cv::FileStorage::READ); + fs["hitThreshold"] >> hit_threshold_; + cv::FileNode fn = fs[cv::FileStorage::getDefaultObjectName(file_name)]; hog_.read(fn); } - ODDetections2D *ODHOGDetector::detectOmni(ODSceneImage *scene) + void ODHOGDetector::setSVMDetector(std::vector<float> svm_detector) { - //always create a detection - ODDetections2D *detections = new ODDetections2D; + hog_.setSVMDetector(svm_detector); + } + ODDetections2D * ODHOGDetector::detectOmni(ODSceneImage * scene) + { + //always create a detection + ODDetections2D * detections = new ODDetections2D; vector<cv::Rect> found, found_filtered; - hog_.detectMultiScale(scene->getCVImage(), found, hitThreshold, cv::Size(8, 8), cv::Size(32, 32), 1.05, 2); - + hog_.detectMultiScale(scene->getCVImage(), found, hit_threshold, cv::Size(8, 8), cv::Size(32, 32), 1.05, 2); cv::Mat viz = scene->getCVImage().clone(); for(int i = 0; i < found.size(); i++) { - ODDetection2D *detection2D = new ODDetection2D; + ODDetection2D * detection2D = new ODDetection2D; detection2D->setBoundingBox(found[i]); detection2D->setId("PEOPLE"); detection2D->setType(ODDetection::OD_DETECTION_CLASS); detections->push_back(detection2D); - if(metainfo_) { cv::Rect r = found[i]; @@ -107,20 +122,20 @@ namespace od return detections; } - ODDetections *ODHOGDetector::detect(ODSceneImage *scene) + ODDetections * ODHOGDetector::detect(ODSceneImage * scene) { //always create a detection - ODDetections *detections = new ODDetections; + ODDetections * detections = new ODDetections; - cv::Mat scaledwindow; - cv::resize(scene->getCVImage(), scaledwindow, hog_.winSize); + cv::Mat scaled_window; + cv::resize(scene->getCVImage(), scaled_window, hog_.win_size); - std::vector<cv::Point> foundLocations; + std::vector<cv::Point> found_locations; - hog_.detect(scene->getCVImage(), foundLocations, hitThreshold); - if (!foundLocations.empty()) + hog_.detect(scene->getCVImage(), found_locations, hitThreshold); + if (!found_locations.empty()) { - ODDetection2D *detection2D = new ODDetection2D; + ODDetection2D * detection2D = new ODDetection2D; detection2D->setId("PEOPLE"); detection2D->setType(ODDetection::OD_DETECTION_CLASS); detections->push_back(detection2D); @@ -129,24 +144,89 @@ namespace od return detections; } - void ODHOGDetector::setSVMFromFile(std::string fileName) + void ODHOGDetector::setTrainedDataLocation(const std::string & trained_data_location) + { + trained_data_location_ = trained_data_location; + svmtype_ = OD_FILE; + } + + const SVMType & ODHOGDetector::getSvmtype() const + { + return svm_type_; + } + + void ODHOGDetector::setSvmtype(const SVMType & svm_type) + { + svm_type_ = svm_type; + } + + const cv::Size & ODHOGDetector::getWinSize() const + { + return win_size; + } + + void ODHOGDetector::setWinSize(const cv::Size & win_size) + { + win_size_ = win_size; + } + + const cv::Size & ODHOGDetector::getBlockSize() const + { + return block_size; + } + + void ODHOGDetector::setBlockSize(const cv::Size & block_size) + { + block_size_ = block_size; + } + + const cv::Size & getBlockStride() const + { + return block_stride; + } + + void ODHOGDetector::setBlockStride(const cv::Size & block_stride) + { + ODHOGDetector::blockStride = blockStride; + } + + const cv::Size & ODHOGDetector::getCellSize() const + { + return cell_size; + } + + void ODHOGDetector::setCellSize(const cv::Size & cell_size) + { + ODHOGDetector::cellSize = cellSize; + } + + float ODHOGDetector::getHitThreshold() const + { + return hit_threshold; + } + + void ODHOGDetector::setHitThreshold(float hit_threshold) + { + hit_threshold_ = hit_threshold; + } + + void ODHOGDetector::setSVMFromFile(const std::string & file_name) { vector<float> descriptor_vector; - printf("Reading descriptor vector from file '%s'\n", fileName.c_str()); - string separator = " "; // Use blank as default separator between single features + printf("Reading descriptor vector from file '%s'\n", file_name.c_str()); - ifstream File; + ifstream file; float percent; - File.open(fileName.c_str(), ios::in); - if (File.good() && File.is_open()) { + file.open(file_name.c_str(), ios::in); + if (file.good() && file.is_open()) { double d; - while(File >> d) + while(file >> d) { //cout << d << " "; descriptor_vector.push_back(d); } - File.close(); + file.close(); } hog_.setSVMDetector(descriptor_vector); @@ -154,11 +234,11 @@ namespace od void ODHOGDetector::printParameters() { - cout << "winSize: " << winSize << endl; - cout << "blockSize: " << blockSize << endl; - cout << "blockStride: " << blockStride << endl; - cout << "cellSize: " << cellSize << endl; - cout << "hitThreshold: " << hitThreshold << endl; + cout << "winSize: " << win_size_ << endl; + cout << "blockSize: " << block_size_ << endl; + cout << "blockStride: " << block_stride_ << endl; + cout << "cellSize: " << cell_size_ << endl; + cout << "hitThreshold: " << hit_threshold_ << endl; } } } diff --git a/detectors/src/global2D/training/ODHOGTrainer.cpp b/detectors/src/global2D/training/ODHOGTrainer.cpp index 49a44e58..5f101859 100644 --- a/detectors/src/global2D/training/ODHOGTrainer.cpp +++ b/detectors/src/global2D/training/ODHOGTrainer.cpp @@ -44,9 +44,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define TRAINHOG_SVM_TO_TRAIN SVMlight -using namespace std; -using namespace cv; - namespace od { namespace g2d @@ -62,123 +59,159 @@ namespace od printf("\033[u"); } - void ODHOGTrainer::saveDescriptorVectorToFile(vector<float> &descriptorVector, vector<unsigned int> &_vectorIndices, string fileName) + ODHOGTrainer::ODHOGTrainer(const std::string & training_input_location_, const std::string & trained_data_location_, const cv::Size & win_size, + const cv::Size & block_size, const cv::Size & block_stride, const cv::Size & cell_size, float hit_threshold): + ODTrainer(training_input_location_, trained_data_location_), win_size_(win_size), block_size_(block_size), + block_stride_(block_stride), cell_size_(cell_size), hog_(win_size, block_size, block_stride, cell_size, 9) + { + + TRAINED_LOCATION_DENTIFIER_ = "HOG"; + TRAINED_DATA_ID_ = "hog.xml"; + + //algo parameter init + training_padding_ = cv::Size(0, 0); + start_hog_pos_ = cv::Point(15, 15); + nofeatures_neg_ = 10; + win_stride_ = cv::Size(); + train_hard_negetive_ = false; + + if(trained_data_location_ != "") + { + pos_samples_dir = training_input_location_ + "/pos"; + neg_samples_dir = training_input_location_ + "/neg"; + } + + + //internal data + FileUtils::createTrainingDir(getSpecificTrainingDataLocation()); + features_file_ = getSpecificTrainingDataLocation() + "/features.dat"; + svm_model_file_ = getSpecificTrainingDataLocation() + "/svmlightmodel.dat"; + svm_model_hard_ = getSpecificTrainingDataLocation() + "/svmlightmodelhard.dat"; + descriptor_vector_file_ = getSpecificTrainingDataLocation() + "/descriptorvector.dat"; + descriptor_vector_file_ = getSpecificTrainingDataLocation() + "/descriptorvectorHard.dat"; + + + } + + void ODHOGTrainer::saveDescriptorVectorToFile(std::vector<float> & descriptor_vector, std::vector<unsigned int> & vector_indices, + const std::string & file_name) { - printf("Saving descriptor vector to file '%s'\n", fileName.c_str()); - string separator = " "; // Use blank as default separator between single features - fstream File; + std::cout << "Saving descriptor vector to file " << file_name << std::endl; + std::string separator = " "; // Use blank as default separator between single features + fstream file; float percent; - File.open(fileName.c_str(), ios::out); - if(File.good() && File.is_open()) + file.open(file_name.c_str(), ios::out); + const unsigned int descriptors_num = descriptor_vector.size(); + + if(file.good() && file.is_open()) { - printf("Saving %lu descriptor vector features:\t", descriptorVector.size()); + std::cout << "Saving " << descriptors_num << " descriptor vector features:\t" << std::endl; storeCursor(); - for(int feature = 0; feature < descriptorVector.size(); ++feature) + for(size_t feature = 0; feature < descriptors_num; ++feature) { - if((feature % 10 == 0) || (feature == (descriptorVector.size() - 1))) + if((feature % 10 == 0) || (feature == (descriptors_num - 1))) { - percent = ((1 + feature) * 100 / descriptorVector.size()); + percent = ((1 + feature) * 100 / descriptors_num); printf("%4u (%3.0f%%)", feature, percent); fflush(stdout); resetCursor(); } - File << descriptorVector.at(feature) << separator; + file << descriptor_vector[feature] << separator; } printf("\n"); - File << endl; - File.flush(); - File.close(); + file << std::endl; + file.flush(); + file.close(); } } - void ODHOGTrainer::calculateFeaturesFromInput(const string &imageFilename, vector<float> &featureVector, HOGDescriptor &hog) + void ODHOGTrainer::calculateFeaturesFromInput(const std::string & image_file_name, std::vector<float> & feature_vector, HOGDescriptor & hog) { - Mat imageDataorig, imageData; - imageDataorig = imread(imageFilename, 0); + cv::Mat image_data_orig, image_data; + image_data_orig = imread(image_file_name, 0); - if(imageDataorig.empty()) + if(image_data_orig.empty()) { featureVector.clear(); - printf("Error: HOG image '%s' is empty, features calculation skipped!\n", imageFilename.c_str()); + std::cout << "Error: HOG image " << image_file_name << " is empty, features calculation skipped!" << std::endl; return; } - vector<Point> locations; - locations.push_back(start_hog_pos); - hog.compute(imageDataorig, featureVector, winStride, trainingPadding, locations); + std::vector<cv::Point> locations; + locations.push_back(start_hog_pos_); + hog.compute(image_data_orig, feature_vector, win_stride_, training_padding_, locations); //cout << "Desc size :" << featureVector.size(); //cout << ": Expected size :" << hog.getDescriptorSize() << endl; - imageDataorig.release(); // Release the image again after features are extracted + image_data_orig.release(); // Release the image again after features are extracted } - void ODHOGTrainer::detectTrainingSetTest(const HOGDescriptor &hog, const double hitThreshold, const vector<string> &posFileNames, const vector<string> &negFileNames) + void ODHOGTrainer::detectTrainingSetTest(const HOGDescriptor & hog, double hit_threshold, const std::vector<std::string> & pos_file_names, + const vector<string> & neg_file_names) { - unsigned int truePositives = 0; - unsigned int trueNegatives = 0; - unsigned int falsePositives = 0; - unsigned int falseNegatives = 0; - vector<Point> foundDetection; + unsigned int true_positives = 0; + unsigned int true_negatives = 0; + unsigned int false_positives = 0; + unsigned int false_negatives = 0; + std::vector<cv::Point> found_detection; // Walk over positive training samples, generate images and detect - for(vector<string>::const_iterator posTrainingIterator = posFileNames.begin(); - posTrainingIterator != posFileNames.end(); ++posTrainingIterator) + for(auto & pf : pos_file_names.begin()) { - const Mat imageData = imread(*posTrainingIterator, 0); - hog.detect(imageData, foundDetection, hitThreshold, winStride, trainingPadding); - if(foundDetection.size() > 0) + const cv::Mat image_data = cv::imread(pf, 0); + hog.detect(image_data, found_detection, hit_threshold, win_stride_, training_padding_); + if(found_detection.size() > 0) { - ++truePositives; - falseNegatives += foundDetection.size() - 1; + ++true_positives; + false_negatives += found_detection.size() - 1; } else { - ++falseNegatives; + ++false_negatives; } } // Walk over negative training samples, generate images and detect - for(vector<string>::const_iterator negTrainingIterator = negFileNames.begin(); - negTrainingIterator != negFileNames.end(); ++negTrainingIterator) + for(auto & nf : negFileNames) { - const Mat imageData = imread(*negTrainingIterator, 0); - hog.detect(imageData, foundDetection, hitThreshold, winStride, trainingPadding); - if(foundDetection.size() > 0) + const cv::Mat imageData = cv::imread(nf, 0); + hog.detect(image_data, found_detection, hit_threshold, win_stride, training_padding); + if(found_detection.size() > 0) { - falsePositives += foundDetection.size(); + false_positives += found_detection.size(); } else { - ++trueNegatives; + ++true_negatives; } } - printf("Results:\n\tTrue Positives: %u\n\tTrue Negatives: %u\n\tFalse Positives: %u\n\tFalse Negatives: %u\n", - truePositives, trueNegatives, falsePositives, falseNegatives); + std::cout << "Results: " << std::endl; << "\tTrue Positives: " << true_positives << std::endl << "\tTrue Negatives: " + << true_negatives << "\tFalse Positives: " << false_positives << std::endl << " \tFalse Negatives:" << false_negatives << std::endl; } - void ODHOGTrainer::calculateFeaturesFromImageLoc(const Mat &imageData, vector<float> &featureVector, const HOGDescriptor &hog, Point startpos) + void ODHOGTrainer::calculateFeaturesFromImageLoc(const Mat & image_data, std::vector<float> & feature_vector, const HOGDescriptor & hog, + const cv::Point & start_pos) { - vector<Point> locations; - locations.push_back(startpos); - hog.compute(imageData, featureVector, winStride, trainingPadding, locations); + std::vector<cv::Point> locations; + locations.push_back(start_pos); + hog.compute(image_data, feature_vector, win_stride_, training_padding_, locations); } - void ODHOGTrainer::handleNegetivefile(string const imageFilename, HOGDescriptor &hog, fstream &file) + void ODHOGTrainer::handleNegetivefile(const std::string & image_file_name, HOGDescriptor & hog, std::fstream & file) { - Mat imageData; - imageData = imread(imageFilename, 0); + cv::Mat image_data = cv::imread(image_file_name, 0); //cout << "Image size : " << imageData.size() << "random points : " << endl; //get hog at random 10 image location - for(int i = 0; i < nofeatures_neg; i++) + for(size_t i = 0; i < nofeatures_neg; ++i) { - Point feat_loc(rand() % (imageData.cols - winSize.width), rand() % (imageData.rows - winSize.height)); + cv::Point feat_loc(rand() % (image_data.cols - win_size_.width), rand() % (image_data.rows - win_size_.height)); //cout << feat_loc << endl; //use this p for finding feature - vector<float> featureVector; - calculateFeaturesFromImageLoc(imageData, featureVector, hog, feat_loc); + std::vector<float> feature_vector; + calculateFeaturesFromImageLoc(image_data, feature_vector, hog, feat_loc); if(!featureVector.empty()) { @@ -188,199 +221,194 @@ namespace od */ file << "-1"; // Save feature vector components - for(unsigned int feature = 0; feature < featureVector.size(); ++feature) + for(size_t feature = 0; feature < featureVector.size(); ++feature) { - file << " " << (feature + 1) << ":" << featureVector.at(feature); + file << " " << (feature + 1) << ":" << feature_vector[feature]; } - file << endl; + file << std::endl; } } } - void ODHOGTrainer::createHardTrainingData(const HOGDescriptor &hog, const double hitThreshold, const vector<string> &negFileNames) + void ODHOGTrainer::createHardTrainingData(const HOGDescriptor & hog, double hit_threshold, const std::vector<std::string> & neg_file_names) { - fstream file; - file.open(featuresFile.c_str(), std::fstream::app); + std::fstream file; + file.open(features_file_.c_str(), std::fstream::app); if(!file.is_open()) { - cout << "ERROR opening previous feature file! HARD training failed!" << endl; + std::cout << "ERROR opening previous feature file! HARD training failed!" << std::endl; } - cout << "Appending HARD negetive features to " + featuresFile << endl; + std::cout << "Appending HARD negetive features to " + features_file_ << std::endl; //cvNamedWindow("hardneg", WINDOW_AUTOSIZE); // Walk over negative training samples, generate images and detect int counter = 0; - for(vector<string>::const_iterator negTrainingIterator = negFileNames.begin(); - negTrainingIterator != negFileNames.end(); ++negTrainingIterator) + for(auto & nf : neg_file_names) { - const Mat imageData = imread(*negTrainingIterator, 0); + const cv::Mat image_data = cv::imread(nf, 0); - vector<Rect> foundLocations; - hog.detectMultiScale(imageData, foundLocations, hitThreshold); + std::vector<cv::Rect> found_locations; + hog.detectMultiScale(image_data, found_locations, hit_threshold); - for(int i = 0; i < foundLocations.size(); i++) + for(size_t i = 0; i < found_locations.size(); ++i) { counter++; //cout << "## hard examples ## FALSE POS FOUND : including the descriptor in training set" << endl; - Mat negimg(imageData(foundLocations[i])); + cv::Mat neg_img(image_data(found_locations[i])); //imshow("hardneg", negimg); waitKey(2000); - Mat resized_neg; - resize(negimg, resized_neg, winSize); + cv::Mat resized_neg; + resize(neg_img, resized_neg, win_size_); // imshow("hardneg", resized_neg); waitKey(4000); - vector<float> featureVector; - calculateFeaturesFromImageLoc(resized_neg, featureVector, hog, Point(0, 0)); + cv::vector<float> feature_vector; + calculateFeaturesFromImageLoc(resized_neg, feature_vector, hog, cv::Point(0, 0)); - if(!featureVector.empty()) + if(!feature_vector.empty()) { file << "-1"; - for(unsigned int feature = 0; feature < featureVector.size(); ++feature) + for(size_t feature = 0; feature < feature_vector.size(); ++feature) { file << " " << (feature + 1) << ":" << featureVector.at(feature); } - file << endl; + file << std::endl; } } } - cout << "Wrote " << counter << " HARD negetive features" << endl; + std::cout << "Wrote " << counter << " HARD negetive features" << std::endl; //CLOSE THE APPENDED DATA FILE file.close(); } - double ODHOGTrainer::trainWithSVMLight(string svmModelFile, string svmDescriptorFile, vector<float> &descriptorVector) + double ODHOGTrainer::trainWithSVMLight(const std::string & svm_model_file, const string & svm_descriptor_file, + std::vector<float> & descriptor_vector) { //training takes featurefile as input, produces hitthreshold and vector as output - printf("Calling %s\n", TRAINHOG_SVM_TO_TRAIN::getInstance()->getSVMName()); - TRAINHOG_SVM_TO_TRAIN::getInstance()->read_problem(const_cast<char *> (featuresFile.c_str())); + std:: << "Calling " << TRAINHOG_SVM_TO_TRAIN::getInstance()->getSVMName() << std::endl; + TRAINHOG_SVM_TO_TRAIN::getInstance()->read_problem(const_cast<char *> (features_file_.c_str())); TRAINHOG_SVM_TO_TRAIN::getInstance()->train(); // Call the core libsvm training procedure - printf("Training done, saving model file!\n"); + std::cout << "Training done, saving model file!"<< std::endl; TRAINHOG_SVM_TO_TRAIN::getInstance()->saveModelToFile(svmModelFile); - printf("Generating representative single HOG feature vector using svmlight!\n"); + std::cout << "Generating representative single HOG feature vector using svmlight!" << std::endl; - descriptorVector.resize(0); - vector<unsigned int> descriptorVectorIndices; + descriptor_vector.resize(0); + std::vector<unsigned int> descriptor_vector_indices; // Generate a single detecting feature vector (v1 | b) from the trained support vectors, for use e.g. with the HOG algorithm - TRAINHOG_SVM_TO_TRAIN::getInstance()->getSingleDetectingVector(descriptorVector, descriptorVectorIndices); + TRAINHOG_SVM_TO_TRAIN::getInstance()->getSingleDetectingVector(descriptor_vector, descriptor_vector_indices); // And save the precious to file system - saveDescriptorVectorToFile(descriptorVector, descriptorVectorIndices, svmDescriptorFile); + saveDescriptorVectorToFile(descriptor_vector, descriptor_vector_indices, svm_descriptor_file); // Detector detection tolerance threshold - return hitThreshold = TRAINHOG_SVM_TO_TRAIN::getInstance()->getThreshold(); + hit_threshold_ = TRAINHOG_SVM_TO_TRAIN::getInstance()->getThreshold() + return hit_threshold; } int ODHOGTrainer::train() { - vector<string> positiveTrainingImages; - vector<string> negativeTrainingImages; - vector<string> validExtensions; - validExtensions.push_back(".jpg"); - validExtensions.push_back(".png"); - validExtensions.push_back(".ppm"); + std::vector<std::string> positive_training_images; + std::vector<std::string> negative_training_images; + std::vector<std::string> valid_extensions; + valid_extensions.push_back(".jpg"); + valid_extensions.push_back(".png"); + valid_extensions.push_back(".ppm"); + + FileUtils::getFilesInDirectoryRec(pos_samples_dir_, valid_extensions, positive_training_images); + FileUtils::getFilesInDirectoryRec(neg_samples_dir_, valid_extensions, negative_training_images); - FileUtils::getFilesInDirectoryRec(posSamplesDir, validExtensions, positiveTrainingImages); - FileUtils::getFilesInDirectoryRec(negSamplesDir, validExtensions, negativeTrainingImages); - cout << "No of positive Training Files: " << positiveTrainingImages.size() << endl; - cout << "No of neg Training Files: "<< negativeTrainingImages.size() << endl; + unsigned int pos_size = positive_training_images.size(); + unsigned int neg_size = negative_training_images.size(); + std::cout << "No of positive Training Files: " << pos_size << std::endl; + std::cout << "No of neg Training Files: "<< neg_size << std::endl; - if( positiveTrainingImages.size() + negativeTrainingImages.size() == 0) + if(pos_size + neg_size == 0) { - printf("No training sample files found, nothing to do!\n"); + std::cout << "No training sample files found, nothing to do!" << std::endl; return EXIT_SUCCESS; } - /// @WARNING: This is really important, some libraries (e.g. ROS) seems to set the system locale which takes decimal commata instead of points which causes the file input parsing to fail + /// @WARNING: This is really important, some libraries (e.g. ROS) seems to set the system locale + // which takes decimal commata instead of points which causes the file input parsing to fail setlocale(LC_ALL, "C"); // Do not use the system locale setlocale(LC_NUMERIC, "C"); setlocale(LC_ALL, "POSIX"); - - printf("Reading files, generating HOG features and save them to file '%s':\n", featuresFile.c_str()); + std::cout << "Reading files, generating HOG features and save them to file: " << features_file_ << std::endl; float percent; - - fstream File; - File.open(featuresFile.c_str(), std::fstream::out); - if(File.is_open()) + std::fstream file; + file.open(features_file_.c_str(), std::fstream::out); + if(file.is_open()) { // Iterate over POS IMAGES - for(unsigned long currentFile = 0; currentFile < positiveTrainingImages.size(); ++currentFile) + for(auto & pt : positive_training_images) { - vector<float> featureVector; - const string currentImageFile = positiveTrainingImages.at(currentFile); - - - calculateFeaturesFromInput(currentImageFile, featureVector, hog_); - if(!featureVector.empty()) + std::vector<float> feature_vector; + calculateFeaturesFromInput(pt, feature_vector, hog_); + if(!feature_vector.empty()) { /* Put positive or negative sample class to file, * true=positive, false=negative, * and convert positive class to +1 and negative class to -1 for SVMlight */ - File << "+1"; + file << "+1"; // Save feature vector components - for(unsigned int feature = 0; feature < featureVector.size(); ++feature) + for(unsigned int feature = 0; feature < feature_vector.size(); ++feature) { - File << " " << (feature + 1) << ":" << featureVector.at(feature); + file << " " << (feature + 1) << ":" << feature_vector[feature]; } - File << endl; + file << std::endl; } } // Iterate over NEG IMAGES - for(unsigned long currentFile = 0; currentFile < negativeTrainingImages.size(); ++currentFile) + for(auto & ni : negative_training_images) { - const string currentImageFile = negativeTrainingImages.at(currentFile); - handleNegetivefile(currentImageFile, hog_, File); + handleNegetivefile(ni, hog_, File); } - printf("\n"); - File.flush(); - File.close(); + std::cout << std::endl; + file.flush(); + file.close(); } else { - printf("Error opening file '%s'!\n", featuresFile.c_str()); + std::cout << "Error opening file " << features_file; return EXIT_FAILURE; } //train them with SVM - vector<float> descriptorVector; - hitThreshold = trainWithSVMLight(svmModelFile, descriptorVectorFile, descriptorVector); + std::vector<float> descriptor_vector; + hit_threshold_ = trainWithSVMLight(svm_model_file, descriptor_vector_file, descriptor_vector); // Pseudo test our custom detecting vector - hog_.setSVMDetector(descriptorVector); - printf( - "Testing training phase using training set as test set (just to check if training is ok - no detection quality conclusion with this!)\n"); - detectTrainingSetTest(hog_, hitThreshold, positiveTrainingImages, negativeTrainingImages); - - + hog_.setSVMDetector(descriptor_vector); + std::cout << "Testing training phase using training set as test set (just to check if training is ok - no detection quality conclusion with this!)" << std::endl; + detectTrainingSetTest(hog_, hit_threshold_, positive_training_images, negative_training_images); - if(train_hard_negetive_) + if(train_hard_negative_) { - cout << "Preparing for training HARD negetive windows" << endl; + std::cout << "Preparing for training HARD negetive windows" << std::endl; //create hard training examples - createHardTrainingData(hog_, hitThreshold, negativeTrainingImages); + createHardTrainingData(hog_, hit_threshold_, negative_training_images); //train again - hitThreshold = trainWithSVMLight(svmModelHard, descriptorVectorHard, descriptorVector); + hit_threshold_ = trainWithSVMLight(svm_model_hard, descriptor_vector_hard, descriptor_vector); // Pseudo test our custom detecting vector hog_.setSVMDetector(descriptorVector); - printf( - "Testing training phase using training set as test set after HARD EXAMPLES (just to check if training is ok - no detection quality conclusion with this!)\n"); - detectTrainingSetTest(hog_, hitThreshold, positiveTrainingImages, negativeTrainingImages); + std::cout << "Testing training phase using training set as test set after HARD EXAMPLES (just to check if training is ok - no detection quality conclusion with this!)" << std::endl; + detectTrainingSetTest(hog_, hit_threshold_, positive_training_images, negative_training_images); } @@ -390,32 +418,31 @@ namespace od } - void ODHOGTrainer::readDescriptorsFromFile(string fileName, vector<float> &descriptor_vector) + void ODHOGTrainer::readDescriptorsFromFile(const std::string & file_name, std::vector<float> & descriptor_vector) { - printf("Reading descriptor vector from file '%s'\n", fileName.c_str()); - string separator = " "; // Use blank as default separator between single features + std::cout << "Reading descriptor vector from file " << file_name << std::endl; - ifstream File; + std::ifstream file; float percent; - File.open(fileName.c_str(), ios::in); - if(File.good() && File.is_open()) + file.open(fileName.c_str(), ios::in); + if(file.good() && file.is_open()) { double d; - while(File >> d) + while(file >> d) { - //cout << d << " "; descriptor_vector.push_back(d); } - File.close(); + file.close(); } } - void ODHOGTrainer::save(std::string filename) + void ODHOGTrainer::save(const std::string & file_name) { - FileStorage fs(filename, FileStorage::WRITE); - fs << "hitThreshold" << hitThreshold; - hog_.write(fs, FileStorage::getDefaultObjectName(filename)); + + cv::FileStorage fs(file_name, cv::FileStorage::WRITE); + fs << "hitThreshold" << hit_threshold_; + hog_.write(fs, FileStorage::getDefaultObjectName(file_name)); } } diff --git a/detectors/src/global3D/detection/ODCADDetector3DGlobal.cpp b/detectors/src/global3D/detection/ODCADDetector3DGlobal.cpp index 815c678d..aa20819c 100644 --- a/detectors/src/global3D/detection/ODCADDetector3DGlobal.cpp +++ b/detectors/src/global3D/detection/ODCADDetector3DGlobal.cpp @@ -27,4 +27,4 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 16.06.15. // -#include "od/detectors/global3D/detection/ODCADDetector3DGlobal.h" +#include "od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp" diff --git a/detectors/src/misc/detection/ODDetectorMultiAlgo.cpp b/detectors/src/misc/detection/ODDetectorMultiAlgo.cpp index a573ff01..c32345d9 100644 --- a/detectors/src/misc/detection/ODDetectorMultiAlgo.cpp +++ b/detectors/src/misc/detection/ODDetectorMultiAlgo.cpp @@ -35,7 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/local2D/detection/ODCADRecognizer2DLocal.h" //3D detectors -#include "od/detectors/global3D/detection/ODCADDetector3DGlobal.h" +#include "od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp" using namespace std; diff --git a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md index b9cc2da2..15e30de0 100644 --- a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md +++ b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md @@ -40,7 +40,7 @@ Depending on OpenCV/PCL version activate/deactivate modules of the library. - **Task 9** - small task - merge other gsoc16 contributions : Merge changes from other contribution of GSOC. Other projects will be using existing APIs. There might me need to make small changes to fit to the changes made in this project - **Task 10** - moderate task - deb packaging : Create an automated way to generate a deb file for the library so it can be installed through debian packaging system. -##Fix 3rd party dependencies## +##Fix 3rd party dependencies 12/05/15## The library has some dependencies inserted as source code into the 3rdparty folder; these are pugixml and SiftGPU (and maybe others which will come later). There is also the dependency of svmlight, but this is not mandatory so it will be added as external dependency; if the library is present on the system all the depending libraries will be built. @@ -50,7 +50,7 @@ In general its a bad idea to integrate external source code into a library since - **Pugixml** is a bit more tricky since it has no cmake file; it contains a Make file which produces a test executable but no library. The library can be easily built since we have just two include files and one .cpp file. To fix the issue I created a separate pugixml_build folder with a simple custom cmake which builds the library. This way we can just add that folder as add_subdirectory and directly export the newly compile **libpugixml.so** library. - **Svmlight** will be inserted in the system with a find_package or something similar and a custom **WITH_SVMLIGHT** cmake flag. These steps will come shortly; we will just leave the svmlight binding. -##Refactoring file structure## +##Refactoring file structure 13/05/15## I started to move files in appropriate folders and to fix the "Main" *CmakeLists.txt* file. I renamed the ODconfig.h.in file to od_config.h and moved it into the cmake folder for now. Then I moved the opendetection.cpp source file away from the root (not nice to have source files in the root of a library) and added it as separate app in a new version folder which builds the od_version executable. @@ -66,7 +66,7 @@ This resembles also the structure used in the PCL library. After refactoring the folders, all the involved CMakeFiles had to be restructured to adapt to the new structure. The first version still uses all file names explicitly, but it should be possible to automate the file detection process by using the cmake command *file(GLOB VAR PATTERN)* which finds all file in a gve folder which match a given pattern. This can be done also in a recursive manner. I will probably use this for some folders to be independent (partially) of file names, locations and number. -##Making examples optional## +##Making examples optional 17/05/15## Examples should not be built always, or at least it should be possible to not build them at all or partially. To do so I added the **WITH_EXAMPLES** option whichi enables the building of the examples. Then for each example I added a variable to trigger the building process of that examples: @@ -80,7 +80,7 @@ endif() The option command adds an option in the cmake; the first parameter is the cmake option name (which for now is written in that way but it will probably change), the second is a description of the option, and the third is the default option value. I left it on on so that even unexperienced user can build some examples to test the library. The option is then followed by an if which checks the option vale and in case adds the example to the build process. I would like to change also the structure of the example folder by creating subfolders which then contain the different examples subdividing them by **type** . Each folder will then have its own *CMakeLists.txt* file which can be added by the parent cmake with the usual *add_subdirectory* command. This helps also since we can add automatically all the subfolders of the example folder to the build process without stating the names explicitly. -##Include structure## +##Include structure 18/05/15 ## While continuing to restructure the library I came across a decision which can be solved in different ways but for which I still don't have the best solution. Lets assume we want to include a global 3D detector for example, we would use *ODCADDetector3DGlobal.h* . We could have in our file @@ -116,7 +116,7 @@ and include the upper level folder. To use the first solution we would need to i The new structure compiles fines except for three examples which have a linker bug (undefined reference to `vtable for od::g3d::ODCADDetectTrainer3DGlobal' ) which I am trying to resolve. I fixed a bit the source code of all the examples removing unnecessary includes, fixing namespaces, maintaining a common interface and avoiding dynamic memory allocation where possible. The next step after fixing the linker bug will be to fix the install paths for includes and libs. -##Include structure 2 and install target## +##Include structure and install target 23/05/15## I fixed the linking error; it came from a wrong variable name in a cmake file which specified some source files which were not compiled and so the linking error. I split simple_ransac_detection in src and include and reinserted it in local2D detectors since it is used only there. The source files are just added to the local2D library and compiled together. @@ -132,4 +132,20 @@ with the DIRECTORY keyword to copy the whole folder. Also I removed the include OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${SOURCES}) @endcode -since you don't have to compile headers files.It is enough to specify the include folders to find the includes at compile time. It could be possible to add header precompilation, but I believe this would be an advanced step which could be implemented at the end. I still need to check if this way of installing includes using the *DIRECTORY* keyword without specifying the single files is the clearest way. \ No newline at end of file +since you don't have to compile headers files.It is enough to specify the include folders to find the includes at compile time. It could be possible to add header precompilation, but I believe this would be an advanced step which could be implemented at the end. I still need to check if this way of installing includes using the *DIRECTORY* keyword without specifying the single files is the clearest way. + + +##Code refactoring 01/06/15## + +Befor modifying and digging into the code I wanted to have a common coding style in all files, fixing also some common coding mistakes when found. Another decision was to use C++11 features whenever possible since it should be considered a basic standard, having already C++17 code online. I started by fixing the "common module"; here I did the following: + +- I removed the ToString method since in c++11 it is present as *to_string()*; +- I removed global variables from utils since they where just used in one function. +- I removed the *FileUtils* class which was a class with only static functions. This is equivalent to the new structure which is a namespace with defined functions which is more readable and less prone to errors. +- I removed the time.h headers everywhere in order to use chrono from the std library. +- I added constness modifiers and references for passed arguments whereever possible to avoid useless parameter copyes. +- I modified the frame generator template to have as parameter generic clouds and not only pointrgba point clouds + +After this I started to fix the detectors which I am still working on. The first class which has bee updated is the *ODHOGDetector* which had also a lot of naming issues since it used a different naming style. +There are stll some function which I dislike, for example the parsing methods. They are mainly based on creating a fake argc,argv couple of variables and the parsing them. This has to be removed in favor of a better and newer parsing library as the one included in boost. This will be one of the first steps which I will do as soon as I finished the basic refactoring step. + diff --git a/examples/objectdetector/od_pc_global.cpp b/examples/objectdetector/od_pc_global.cpp index ff1c351c..33777f50 100644 --- a/examples/objectdetector/od_pc_global.cpp +++ b/examples/objectdetector/od_pc_global.cpp @@ -53,7 +53,7 @@ int main(int argc, char *argv[]) trainer.train(); //detector - od::g3d::ODCADDetector3DGlobal<> detector ; + od::g3d::ODCADDetector3DGlobal<pcl::PointXYZRGBA> detector ; detector.setTrainingInputLocation(training_input_dir); detector.setTrainedDataLocation(trained_data_dir); detector.init(); diff --git a/examples/objectdetector/od_pc_global_files.cpp b/examples/objectdetector/od_pc_global_files.cpp index 2e2dc38f..d8986480 100644 --- a/examples/objectdetector/od_pc_global_files.cpp +++ b/examples/objectdetector/od_pc_global_files.cpp @@ -54,7 +54,7 @@ int main(int argc, char *argv[]) trainer.train(); //detector - od::g3d::ODCADDetector3DGlobal<> detector; + od::g3d::ODCADDetector3DGlobal<pcl::PointXYZRGBA> detector; detector.setTrainingInputLocation(training_input_dir); detector.setTrainedDataLocation(trained_data_dir); detector.init(); From fc6d7e5f1e1d76f7787254d32b656ff75d50e8e8 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Mon, 6 Jun 2016 16:34:57 +0200 Subject: [PATCH 15/79] almost finished simple ransac, only pugixml include bug --- CMakeLists.txt | 2 + .../include/od/common/pipeline/ODDetector.h | 2 +- .../detection/ODCADDetector3DGlobal.hpp | 13 +- .../global3D/ODPointCloudGlobalMatching.h | 33 +- .../training/ODCADDetectTrainer3DGlobal.h | 25 +- .../detectors/local2D/ODImageLocalMatching.h | 9 +- .../detection/ODCADRecognizer2DLocal.h | 251 ++----- .../simple_ransac_detection/CsvReader.h | 66 +- .../simple_ransac_detection/CsvWriter.h | 37 +- .../local2D/simple_ransac_detection/Mesh.h | 121 ++-- .../local2D/simple_ransac_detection/Model.h | 96 +-- .../ModelRegistration.h | 59 +- .../simple_ransac_detection/PnPProblem.h | 105 +-- .../simple_ransac_detection/RobustMatcher.h | 131 ++-- .../local2D/simple_ransac_detection/Utils.h | 103 +-- .../global2D/detection/ODCascadeDetector.cpp | 4 +- .../global3D/ODPointCloudGlobalMatching.cpp | 20 + .../training/ODCADDetectTrainer3DGlobal.cpp | 50 +- detectors/src/local2D/CMakeLists.txt | 7 +- .../detection/ODCADRecognizer2DLocal.cpp | 325 ++++++--- .../simple_ransac_detection/CsvReader.cpp | 146 ++-- .../simple_ransac_detection/CsvWriter.cpp | 86 +-- .../local2D/simple_ransac_detection/Mesh.cpp | 116 ++-- .../local2D/simple_ransac_detection/Model.cpp | 329 ++++----- .../ModelRegistration.cpp | 84 ++- .../simple_ransac_detection/PnPProblem.cpp | 611 ++++++++-------- .../simple_ransac_detection/RobustMatcher.cpp | 492 +++++++------ .../local2D/simple_ransac_detection/Utils.cpp | 651 +++++++++--------- 28 files changed, 2073 insertions(+), 1901 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cd9d9b10..7e1126a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,8 @@ set(OD_SOURCE_DIR ${OpenDetection_SOURCE_DIR}) set(OD_BINARY_DIR ${OpenDetection_BINARY_DIR}) set(OD_CMAKE_DIR "${OpenDetection_SOURCE_DIR}/cmake") set(CMAKE_MODULE_PATH ${OD_CMAKE_DIR}/modules) +set(CMAKE_3RDPARTY_DIR ${OD_SOURCE_DIR}/3rdparty) + # Initialize versioning include(${OD_CMAKE_DIR}/od_version.cmake) diff --git a/common/include/od/common/pipeline/ODDetector.h b/common/include/od/common/pipeline/ODDetector.h index 12033e19..e453f8d0 100644 --- a/common/include/od/common/pipeline/ODDetector.h +++ b/common/include/od/common/pipeline/ODDetector.h @@ -55,7 +55,7 @@ namespace od virtual ODDetections * detect(ODScene *scene){} virtual ODDetections * detectOmni(ODScene *scene){} - bool metainfo_; + bool meta_info_; }; diff --git a/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp b/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp index 5ae9d093..041ad4ef 100644 --- a/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp +++ b/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp @@ -72,7 +72,7 @@ namespace od boost::shared_ptr<pcl::rec_3d_framework::MeshSource<pcl::PointXYZ> > mesh_source(new pcl::rec_3d_framework::MeshSource<pcl::PointXYZ>); mesh_source->setPath(this->training_input_location_); std::string training_dir_specific = this->getSpecificTrainingDataLocation(); - mesh_source->setModelScale (1.f); + mesh_source->setModelScale(1.f); mesh_source->generate(training_dir_specific); boost::shared_ptr<pcl::rec_3d_framework::Source<pcl::PointXYZ> > cast_source; @@ -150,9 +150,9 @@ namespace od } template<typename PointT> - ODDetections3D* ODCADDetector3DGlobal<PointT>::detectOmni(ODScenePointCloud<PointT> *scene) + ODDetections3D * ODCADDetector3DGlobal<PointT>::detectOmni(ODScenePointCloud<PointT> * scene) { - ODDetections3D *detections = new ODDetections3D; + ODDetections3D * detections = new ODDetections3D; typename pcl::PointCloud<PointT>::Ptr frame; @@ -214,10 +214,9 @@ namespace od } template<typename PointT> - ODDetections* ODCADDetector3DGlobal<PointT>::detect(ODScenePointCloud<PointT> *scene) + ODDetections * ODCADDetector3DGlobal<PointT>::detect(ODScenePointCloud<PointT> * scene) { - ODDetections *detections = new ODDetections; - + ODDetections * detections = new ODDetections; typename pcl::PointCloud<PointT>::Ptr frame; float Z_DIST_ = 1.25f; @@ -240,7 +239,7 @@ namespace od std::string category = categories[0]; //now fill up the detection: - ODDetection *detection = new ODDetection3D(ODDetection::OD_DETECTION_CLASS, categories[0], conf[0]); + ODDetection * detection = new ODDetection3D(ODDetection::OD_DETECTION_CLASS, categories[0], conf[0]); detections->push_back(detection); return detections; diff --git a/detectors/include/od/detectors/global3D/ODPointCloudGlobalMatching.h b/detectors/include/od/detectors/global3D/ODPointCloudGlobalMatching.h index 6230ad4a..b8ea2fc1 100644 --- a/detectors/include/od/detectors/global3D/ODPointCloudGlobalMatching.h +++ b/detectors/include/od/detectors/global3D/ODPointCloudGlobalMatching.h @@ -26,9 +26,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */// // Created by sarkar on 16.06.15. // - -#ifndef OPENDETECTION_ODPOINTCLOUDGLOBALMATCHING_H -#define OPENDETECTION_ODPOINTCLOUDGLOBALMATCHING_H +#pragma once #include "od/common/pipeline/ODDetector.h" #include "od/common/pipeline/ODTrainer.h" @@ -49,30 +47,15 @@ namespace od class ODPointCloudGlobalMatching : ObjectDetector { - ODPointCloudGlobalMatching() - { - } - - void init() - { } - - - int train() - { - return trainer_->train(); - } - - int detect(ODScene *scene, std::vector<ODDetection *> detections) - { - //detector_->detect(scene, detections); - return 0; - } + void init(){} + int train(); + int detect(ODScene * scene, std::vector<ODDetection *> detections); protected: - std::string desc_name; - ODCADDetectTrainer3DGlobal *trainer_; - ODCADDetector3DGlobal<pcl::PointXYZ> *detector_; + + std::string desc_name_; + ODCADDetectTrainer3DGlobal * trainer_; + ODCADDetector3DGlobal<pcl::PointXYZ> * detector_; }; } } -#endif //OPENDETECTION_ODPOINTCLOUDGLOBALMATCHING_H diff --git a/detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h b/detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h index adb3e910..b410424b 100644 --- a/detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h +++ b/detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h @@ -26,10 +26,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */// // Created by sarkar on 16.06.15. // - -#ifndef OPENDETECTION_ODPOINTCLOUDGLOBALMATCHINGTRAINER_H -#define OPENDETECTION_ODPOINTCLOUDGLOBALMATCHINGTRAINER_H - +#pragma once #include "od/common/pipeline/ODTrainer.h" #include <iostream> @@ -71,31 +68,19 @@ namespace od { public: - ODCADDetectTrainer3DGlobal(std::string const &training_input_location_ = "", std::string const &training_data_location_ = "") : ODTrainer( - training_input_location_, training_data_location_) - { - desc_name = "esf"; - TRAINED_LOCATION_DENTIFIER_ = "GLOBAL3DVFH"; - } + ODCADDetectTrainer3DGlobal(const std::string & training_input_location_ = "", const std::string & training_data_location_ = ""); int train(); void init() {}; - std::string const &getDescName() const - { - return desc_name; - } + const std::string & getDescName() const; - void setDescName(std::string const &desc_name) - { - ODCADDetectTrainer3DGlobal::desc_name = desc_name; - } + void setDescName(const std::string & desc_name); protected: - std::string desc_name; + std::string desc_name_; }; } } -#endif //OPENDETECTION_ODPOINTCLOUDGLOBALMATCHINGTRAINER_H diff --git a/detectors/include/od/detectors/local2D/ODImageLocalMatching.h b/detectors/include/od/detectors/local2D/ODImageLocalMatching.h index d079cfd7..eae196fa 100644 --- a/detectors/include/od/detectors/local2D/ODImageLocalMatching.h +++ b/detectors/include/od/detectors/local2D/ODImageLocalMatching.h @@ -28,9 +28,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 05.06.15. // -#ifndef OPENDETECTION_ODIMAGELOCALMATCHINGSIMPLE_H -#define OPENDETECTION_ODIMAGELOCALMATCHINGSIMPLE_H - +#pragma once #include "od/common/pipeline/ODScene.h" #include "od/common/pipeline/ODTrainer.h" #include "od/common/pipeline/ODDetector.h" @@ -128,11 +126,10 @@ namespace od protected: - ODImageLocalMatchingTrainer *trainer_; - ODImageLocalMatchingDetector *detector_; + ODImageLocalMatchingTrainer * trainer_; + ODImageLocalMatchingDetector * detector_; }; } } -#endif //OPENDETECTION_ODIMAGELOCALMATCHINGSIMPLE_H diff --git a/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h b/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h index d4054b94..15c980c4 100644 --- a/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h +++ b/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h @@ -27,10 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Created by sarkar on 08.06.15. // - -#ifndef OPENDETECTION_SIMPLERANSACDETECTOR_H -#define OPENDETECTION_SIMPLERANSACDETECTOR_H - +#pragma once #include "od/detectors/local2D/ODImageLocalMatching.h" #include "od/common/pipeline/ODDetector.h" @@ -39,17 +36,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/common/utils/ODFeatureDetector2D.h" #include <iostream> -// OpenCV #include <opencv2/core/core.hpp> #include <opencv2/core/utility.hpp> #include <opencv2/calib3d.hpp> #include "od/detectors/local2D/simple_ransac_detection/Utils.h" - -class Model; -class PnpProblem; - namespace od { @@ -68,199 +60,89 @@ namespace od class ODCADRecognizer2DLocal : public ODImageLocalMatchingDetector { public: - string const &getCameraIntrinsicFile() const - { - return camera_intrinsic_file; - } - - void setCameraIntrinsicFile(string const &camera_intrinsic_file) - { - ODCADRecognizer2DLocal::camera_intrinsic_file = camera_intrinsic_file; - } - - int getNumKeyPoints() const - { - return numKeyPoints; - } - - void setNumKeyPoints(int numKeyPoints) - { - ODCADRecognizer2DLocal::numKeyPoints = numKeyPoints; - } - - float getRatioTest() const - { - return ratioTest; - } - - void setRatioTest(float ratioTest) - { - ODCADRecognizer2DLocal::ratioTest = ratioTest; - } - - bool isFast_match() const - { - return fast_match; - } - - void setFast_match(bool fast_match) - { - ODCADRecognizer2DLocal::fast_match = fast_match; - } - - bool isUse_gpu() const - { - return use_gpu; - } - - void setUse_gpu(bool use_gpu) - { - ODCADRecognizer2DLocal::use_gpu = use_gpu; - } - - bool isUse_gpu_match() const - { - return use_gpu_match; - } - - void setUse_gpu_match(bool use_gpu_match) - { - ODCADRecognizer2DLocal::use_gpu_match = use_gpu_match; - } - - bool isMetainfo() const - { - return metainfo_; - } - - void setMetainfo(bool metainfo) - { - ODCADRecognizer2DLocal::metainfo_ = metainfo; - } - - int getIterationsCount() const - { - return iterationsCount; - } - - void setIterationsCount(int iterationsCount) - { - ODCADRecognizer2DLocal::iterationsCount = iterationsCount; - } - - float getReprojectionError() const - { - return reprojectionError; - } - - void setReprojectionError(float reprojectionError) - { - ODCADRecognizer2DLocal::reprojectionError = reprojectionError; - } - - double getConfidence() const - { - return confidence; - } - - void setConfidence(double confidence) - { - ODCADRecognizer2DLocal::confidence = confidence; - } - - int getMinInliers() const - { - return minInliers; - } - - void setMinInliers(int minInliers) - { - ODCADRecognizer2DLocal::minInliers = minInliers; - } - - int getPnpMethod() const - { - return pnpMethod; - } - - void setPnpMethod(int pnpMethod) - { - ODCADRecognizer2DLocal::pnpMethod = pnpMethod; - } - - ODCADRecognizer2DLocal(string const &trained_data_location_ = 0) : ODImageLocalMatchingDetector( - trained_data_location_) - { - metainfo_ = true; - - camera_intrinsic_file = "Data/out_camera_data_lion_old.yml"; // mesh - - red = cv::Scalar(0, 0, 255); - green = cv::Scalar(0, 255, 0); - blue = cv::Scalar(255, 0, 0); - yellow = cv::Scalar(0, 255, 255); - - - numKeyPoints = 2000; // number of detected keypoints - ratioTest = 0.70f; // ratio test - fast_match = true; // fastRobustMatch() or robustMatch() - use_gpu = false; - use_gpu_match = false; - - iterationsCount = 500; // number of Ransac iterations. - reprojectionError = 2.0; // maximum allowed distance to consider it an inlier. - confidence = 0.95; // ransac successful confidence. - - minInliers = 30; // Kalman threshold updating - - pnpMethod = cv::SOLVEPNP_EPNP; - f_type_default = "SIFT"; - featureDetector = std::make_shared<ODFeatureDetector2D>(f_type_default, use_gpu); - } - - void parseParameterString(string parameter_string); - void init(); + ODCADRecognizer2DLocal(const std::string & trained_data_location_ = 0); + + const std::string & getCameraIntrinsicFile() const; + + void setCameraIntrinsicFile(const std::string & camera_intrinsic_file); + + int getNumKeyPoints() const; + void setNumKeyPoints(int num_key_points); + + float getRatioTest() const; + void setRatioTest(float ratio_test); + + bool isFastMatch() const; + void setFastMatch(bool fast_match); + + bool isUseGpu() const; + void setUseGpu(bool use_gpu); - ODDetections* detect(ODSceneImage *scene); + bool isUseGpuMatch() const; + void setUseGpuMatch(bool use_gpu_match); - ODDetections3D* detectOmni(ODSceneImage *scene); + bool isMetainfo() const; + void setMetainfo(bool meta_info); + + int getIterationsCount() const; + void setIterationsCount(int iterations_count); + + float getReprojectionError() const; + void setReprojectionError(float reprojection_error); + + double getConfidence() const; + void setConfidence(double confidence); + + int getMinInliers() const; + void setMinInliers(int min_inliers); + + int getPnpMethod() const; + void setPnpMethod(int pnp_method); + + void parseParameterString(const std::string & parameter_string); + + void init(); + + ODDetections * detect(ODSceneImage * scene); + ODDetections3D * detectOmni(ODSceneImage * scene); protected: - string camera_intrinsic_file; // mesh + bool detectSingleModel(ODSceneImage * scene, const Model & model, ODDetection3D * detection3D, const cv::Mat & frame_viz); + + std::string camera_intrinsic_file_; - cv::Scalar red; - cv::Scalar green; - cv::Scalar blue; - cv::Scalar yellow; + cv::Scalar red_; + cv::Scalar green_; + cv::Scalar blue_; + cv::Scalar yellow_; // Robust Matcher parameters - int numKeyPoints; // number of detected keypoints - float ratioTest; // ratio test - bool fast_match; // fastRobustMatch() or robustMatch() - bool use_gpu; - bool use_gpu_match; + int num_key_points_; // number of detected keypoints + float ratio_test_; // ratio test + bool fast_match_; // fastRobustMatch() or robustMatch() + bool use_gpu_; + bool use_gpu_match_; // RANSAC parameters - int iterationsCount; // number of Ransac iterations. - float reprojectionError; // maximum allowed distance to consider it an inlier. - double confidence; // ransac successful confidence. + int iterations_count_; // number of Ransac iterations. + float reprojection_error_; // maximum allowed distance to consider it an inlier. + double confidence_; // ransac successful confidence. // Kalman Filter parameters - int minInliers; // Kalman threshold updating + int min_inliers_; // Kalman threshold updating // PnP parameters - int pnpMethod; + int pnp_method_; //############NON-CONFIG PARAMETERS used for detection########### - vector<string> model_names; - vector<Model> models; - PnPProblem pnp_detection; - std::string f_type_default; - std::shared_ptr<ODFeatureDetector2D> featureDetector; + std::vector<std::string> model_names_; + std::vector<Model> models_; + PnPProblem pnp_detection_; + std::string f_type_default_; + std::shared_ptr<ODFeatureDetector2D> feature_detector_; - bool detectSingleModel(ODSceneImage *scene, Model const &model, ODDetection3D *&pD, cv::Mat &frame_viz); }; /** \example objectdetector/od_image_cadrecog_camera.cpp * \example objectdetector/od_image_cadrecog_files.cpp @@ -268,4 +150,3 @@ namespace od } } -#endif //OPENDETECTION_SIMPLERANSACDETECTOR_H diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvReader.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvReader.h index e4a85c16..01741c44 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvReader.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvReader.h @@ -1,40 +1,44 @@ -#ifndef CSVREADER_H -#define CSVREADER_H - +#pragma once #include <iostream> #include <fstream> #include <opencv2/core/core.hpp> +#include <string> +#include <stdlib.h> #include "od/detectors/local2D/simple_ransac_detection/Utils.h" -using namespace std; -using namespace cv; -class CsvReader { -public: - /** - * The default constructor of the CSV reader Class. - * The default separator is ' ' (empty space) - * - * @param path - The path of the file to read - * @param separator - The separator character between words per line - * @return - */ - CsvReader(const string &path, const char &separator = ' '); +namespace od { + + namespace l2d { + + class CsvReader { + public: + /** + * The default constructor of the CSV reader Class. + * The default separator is ' ' (empty space) + * + * @param path - The path of the file to read + * @param separator - The separator character between words per line + * @return + */ + CsvReader(const std::string & path, const char & separator = ' '); - /** - * Read a plane text file with .ply format - * - * @param list_vertex - The container of the vertices list of the mesh - * @param list_triangles - The container of the triangles list of the mesh - * @return - */ - void readPLY(vector<Point3f> &list_vertex, vector<vector<int> > &list_triangles); + /** + * Read a plane text file with .ply format + * + * @param list_vertex - The container of the vertices list of the mesh + * @param list_triangles - The container of the triangles list of the mesh + * @return + */ + void readPLY(std::vector<cv::Point3f> & list_vertex, std::vector<std::vector<int> > & list_triangles); -private: - /** The current stream file for the reader */ - ifstream _file; - /** The separator character between words for each line */ - char _separator; -}; + private: + /** The current stream file for the reader */ + std::ifstream _file_; + /** The separator character between words for each line */ + char _separator_; + }; -#endif + } + +} diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvWriter.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvWriter.h index c49dbe16..707c371f 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvWriter.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvWriter.h @@ -1,25 +1,28 @@ -#ifndef CSVWRITER_H -#define CSVWRITER_H - +#pragma once #include <iostream> #include <fstream> #include <opencv2/core/core.hpp> #include "od/detectors/local2D/simple_ransac_detection/Utils.h" -using namespace std; -using namespace cv; -class CsvWriter { -public: - CsvWriter(const string &path, const string &separator = " "); - ~CsvWriter(); - void writeXYZ(const vector<Point3f> &list_points3d); - void writeUVXYZ(const vector<Point3f> &list_points3d, const vector<Point2f> &list_points2d, const Mat &descriptors); +namespace od { + + namespace l2d { + + class CsvWriter { + public: + CsvWriter(const std::string & path, const std::string & separator = " "); + ~CsvWriter(); + void writeXYZ(const std::vector<cv::Point3f> & list_points3d); + void writeUVXYZ(const std::vector<cv::Point3f> & list_points3d, const std::vector<cv::Point2f> & list_points2d, const cv::Mat & descriptors); + + private: + std::ofstream _file_; + std::string _separator_; + bool _is_first_term_; + }; -private: - ofstream _file; - string _separator; - bool _isFirstTerm; -}; + } + +} -#endif diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/Mesh.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/Mesh.h index d82c8d48..71304db1 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/Mesh.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/Mesh.h @@ -5,83 +5,88 @@ * Author: edgar */ -#ifndef MESH_H_ -#define MESH_H_ - +#pragma once #include <iostream> #include <opencv2/core/core.hpp> +#include "od/detectors/local2D/simple_ransac_detection/CsvReader.h" +namespace od { + + namespace l2d { + // --------------------------------------------------- // + // TRIANGLE CLASS // + // --------------------------------------------------- // -// --------------------------------------------------- // -// TRIANGLE CLASS // -// --------------------------------------------------- // + class Triangle { + public: -class Triangle { -public: + explicit Triangle(int id, const cv::Point3f & V0, const cv::Point3f & V1, const cv::Point3f & V2); + virtual ~Triangle(); - explicit Triangle(int id, cv::Point3f V0, cv::Point3f V1, cv::Point3f V2); - virtual ~Triangle(); + cv::Point3f getV0() const { return v0_; } + cv::Point3f getV1() const { return v1_; } + cv::Point3f getV2() const { return v2_; } - cv::Point3f getV0() const { return v0_; } - cv::Point3f getV1() const { return v1_; } - cv::Point3f getV2() const { return v2_; } + private: + /** The identifier number of the triangle */ + int id_; + /** The three vertices that defines the triangle */ + cv::Point3f v0_, v1_, v2_; + }; -private: - /** The identifier number of the triangle */ - int id_; - /** The three vertices that defines the triangle */ - cv::Point3f v0_, v1_, v2_; -}; + // --------------------------------------------------- // + // RAY CLASS // + // --------------------------------------------------- // -// --------------------------------------------------- // -// RAY CLASS // -// --------------------------------------------------- // + class Ray { + public: -class Ray { -public: + explicit Ray(const cv::Point3f & P0, const cv::Point3f & P1); + virtual ~Ray(); - explicit Ray(cv::Point3f P0, cv::Point3f P1); - virtual ~Ray(); + cv::Point3f getP0() + { return p0_; } + cv::Point3f getP1() + { return p1_; } - cv::Point3f getP0() { return p0_; } - cv::Point3f getP1() { return p1_; } + private: + /** The two points that defines the ray */ + cv::Point3f p0_, p1_; + }; -private: - /** The two points that defines the ray */ - cv::Point3f p0_, p1_; -}; + // --------------------------------------------------- // + // OBJECT MESH CLASS // + // --------------------------------------------------- // -// --------------------------------------------------- // -// OBJECT MESH CLASS // -// --------------------------------------------------- // + class Mesh + { + public: -class Mesh -{ -public: + Mesh(); + virtual ~Mesh(); - Mesh(); - virtual ~Mesh(); + std::vector<std::vector<int> > getTrianglesList() const { return list_triangles_; } + std::vector<cv::Point3f> getVertices() const { return list_vertex_; } + cv::Point3f getVertex(int pos) const { return list_vertex_[pos]; } + int getNumVertices() const { return num_vertexs_; } - std::vector<std::vector<int> > getTrianglesList() const { return list_triangles_; } - std::vector<cv::Point3f> getVertices() const { return list_vertex_; } - cv::Point3f getVertex(int pos) const { return list_vertex_[pos]; } - int getNumVertices() const { return num_vertexs_; } + void load(const std::string & path_file); - void load(const std::string path_file); + private: + /** The identification number of the mesh */ + int id_; + /** The current number of vertices in the mesh */ + int num_vertexs_; + /** The current number of triangles in the mesh */ + int num_triangles_; + /* The list of triangles of the mesh */ + std::vector<cv::Point3f> list_vertex_; + /* The list of triangles of the mesh */ + std::vector<std::vector<int> > list_triangles_; + }; -private: - /** The identification number of the mesh */ - int id_; - /** The current number of vertices in the mesh */ - int num_vertexs_; - /** The current number of triangles in the mesh */ - int num_triangles_; - /* The list of triangles of the mesh */ - std::vector<cv::Point3f> list_vertex_; - /* The list of triangles of the mesh */ - std::vector<std::vector<int> > list_triangles_; -}; + } -#endif /* OBJECTMESH_H_ */ +} diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/Model.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/Model.h index ef029cd4..9ec64fb5 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/Model.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/Model.h @@ -4,64 +4,70 @@ * Created on: Apr 9, 2014 * Author: edgar */ - -#ifndef MODEL_H_ -#define MODEL_H_ - +#pragma once #include <iostream> #include <fstream> #include <opencv2/core/core.hpp> #include <opencv2/features2d/features2d.hpp> +#include <pugixml.hpp> +#include <sstream> +#include <boost/algorithm/string.hpp> +#include "od/detectors/local2D/simple_ransac_detection/CsvWriter.h" -class Model -{ -public: - Model(); - virtual ~Model(); - std::vector<cv::Point2f> get_points2d_in() const { return list_points2d_in_; } - std::vector<cv::Point2f> get_points2d_out() const { return list_points2d_out_; } - std::vector<cv::Point3f> get_points3d() const { return list_points3d_in_; } - std::vector<cv::KeyPoint> get_keypoints() const { return list_keypoints_; } - cv::Mat get_descriptors() const { return descriptors_; } - int get_numDescriptors() const { return descriptors_.rows; } +namespace od { + + namespace l2d { + class Model + { + public: + Model(); + virtual ~Model(); - void add_correspondence(const cv::Point2f &point2d, const cv::Point3f &point3d); - void add_outlier(const cv::Point2f &point2d); - void add_descriptor(const cv::Mat &descriptor); - void add_keypoint(const cv::KeyPoint &kp); + std::vector<cv::Point2f> getPoints2dIn() const; + std::vector<cv::Point2f> getPoints2dOut() const; + std::vector<cv::Point3f> getPoints3d() const; + std::vector<cv::KeyPoint> getKeypoints() const; + cv::Mat getDescriptors() const; + int getNumDescriptors() const; + void addCorrespondence(const cv::Point2f &point2d, const cv::Point3f &point3d); + void addOutlier(const cv::Point2f &point2d); + void addDescriptor(const cv::Mat &descriptor); + void addKeypoint(const cv::KeyPoint &kp); - void save(const std::string path); - void load(const std::string path); - void load_new_desc(const std::string path); - void load_new_xml(const std::string path); + void save(const std::string & path); + void load(const std::string & path); + void loadNewDesc(const std::string & path); + void loadNewXml(const std::string & path); + std::string f_type_; + std::string id_; - std::string f_type; - std::string id; + private: -private: + /** The current number of correspondecnes */ + int n_correspondences_; + /** The list of 2D points on the model surface */ + std::vector<cv::KeyPoint> list_keypoints_; + /** The list of 2D points on the model surface */ + std::vector<cv::Point2f> list_points2d_in_; + /** The list of 2D points outside the model surface */ + std::vector<cv::Point2f> list_points2d_out_; + /** The list of 3D points on the model surface */ + std::vector<cv::Point3f> list_points3d_in_; + /** The list of 2D points descriptors */ + cv::Mat descriptors_; - /** The current number of correspondecnes */ - int n_correspondences_; - /** The list of 2D points on the model surface */ - std::vector<cv::KeyPoint> list_keypoints_; - /** The list of 2D points on the model surface */ - std::vector<cv::Point2f> list_points2d_in_; - /** The list of 2D points outside the model surface */ - std::vector<cv::Point2f> list_points2d_out_; - /** The list of 3D points on the model surface */ - std::vector<cv::Point3f> list_points3d_in_; - /** The list of 2D points descriptors */ - cv::Mat descriptors_; + int getDescriptorSize() + { + //if (f_type == "SIFT") + //return 128; + return 128; + } + }; - int get_descriptor_size() - { - if (f_type == "SIFT") return 128; - return 128; } -}; - -#endif /* OBJECTMODEL_H_ */ + +} diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/ModelRegistration.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/ModelRegistration.h index f1e7aca4..5ae003e0 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/ModelRegistration.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/ModelRegistration.h @@ -4,40 +4,43 @@ * Created on: Apr 18, 2014 * Author: edgar */ - -#ifndef MODELREGISTRATION_H_ -#define MODELREGISTRATION_H_ - +#pragma once #include <iostream> #include <opencv2/core/core.hpp> -class ModelRegistration -{ -public: +namespace od { + + namespace l2d { + + class ModelRegistration + { + public: + + ModelRegistration(); + virtual ~ModelRegistration(); - ModelRegistration(); - virtual ~ModelRegistration(); + void setNumMax(int n); - void setNumMax(int n) { max_registrations_ = n; } + std::vector<cv::Point2f> getPoints2d() const; + std::vector<cv::Point3f> getPoints3d() const; + int getNumMax() const; + int getNumRegist() const; - std::vector<cv::Point2f> get_points2d() const { return list_points2d_; } - std::vector<cv::Point3f> get_points3d() const { return list_points3d_; } - int getNumMax() const { return max_registrations_; } - int getNumRegist() const { return n_registrations_; } + bool isRegistrable() const; + void registerPoint(const cv::Point2f & point2d, const cv::Point3f & point3d); + void reset(); - bool is_registrable() const { return (n_registrations_ < max_registrations_); } - void registerPoint(const cv::Point2f &point2d, const cv::Point3f &point3d); - void reset(); + private: + /** The current number of registered points */ + int n_registrations_; + /** The total number of points to register */ + int max_registrations_; + /** The list of 2D points to register the model */ + std::vector<cv::Point2f> list_points2d_; + /** The list of 3D points to register the model */ + std::vector<cv::Point3f> list_points3d_; + }; -private: -/** The current number of registered points */ -int n_registrations_; -/** The total number of points to register */ -int max_registrations_; -/** The list of 2D points to register the model */ -std::vector<cv::Point2f> list_points2d_; -/** The list of 3D points to register the model */ -std::vector<cv::Point3f> list_points3d_; -}; + } -#endif /* MODELREGISTRATION_H_ */ +} \ No newline at end of file diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/PnPProblem.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/PnPProblem.h index 41b15f03..6295b387 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/PnPProblem.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/PnPProblem.h @@ -4,68 +4,69 @@ * Created on: Mar 28, 2014 * Author: Edgar Riba */ - -#ifndef PNPPROBLEM_H_ -#define PNPPROBLEM_H_ - -#include <iostream> - +#pragma once #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> - +#include <opencv2/calib3d/calib3d.hpp> +#include <iostream> +#include <sstream> #include "od/detectors/local2D/simple_ransac_detection/Mesh.h" #include "od/detectors/local2D/simple_ransac_detection/ModelRegistration.h" -class PnPProblem -{ +class Mesh; +class Ray; +class Triangle; -public: - explicit PnPProblem(double const param[], double const dists[]); // custom constructor - PnPProblem(cv::Mat intrin = cv::Mat::eye(3, 4, CV_64F), cv::Mat dist = cv::Mat::zeros(1, 5, CV_64F)); - virtual ~PnPProblem(); +namespace od { + + namespace l2d { - bool backproject2DPoint(const Mesh *mesh, const cv::Point2f &point2d, cv::Point3f &point3d); - bool intersect_MollerTrumbore(Ray &R, Triangle &T, double *out); - std::vector<cv::Point2f> verify_points(Mesh *mesh); - cv::Point2f backproject3DPoint(const cv::Point3f &point3d); - bool estimatePose(const std::vector<cv::Point3f> &list_points3d, const std::vector<cv::Point2f> &list_points2d, int flags); - void estimatePoseRANSAC( const std::vector<cv::Point3f> &list_points3d, const std::vector<cv::Point2f> &list_points2d, - int flags, cv::Mat &inliers, - int iterationsCount, float reprojectionError, double confidence ); + class PnPProblem + { - cv::Mat get_A_matrix() const { return _A_matrix; } - cv::Mat get_dist_coef() const { return _dist; } - cv::Mat get_R_matrix() const { return _R_matrix; } - cv::Mat get_R_vect() const { return _R_vect; } - cv::Mat get_t_matrix() const { return _t_matrix; } - cv::Mat get_P_matrix() const { return _P_matrix; } + public: + explicit PnPProblem(double const param[], double const dists[]); // custom constructor + PnPProblem(const cv::Mat & intrin = cv::Mat::eye(3, 4, CV_64F), const cv::Mat & dist = cv::Mat::zeros(1, 5, CV_64F)); + virtual ~PnPProblem(); - void set_P_matrix( const cv::Mat &R_matrix, const cv::Mat &t_matrix); + cv::Point3f CROSS(const cv::Point3f & v1, const cv::Point3f & v2); + double DOT(const cv::Point3f & v1, const cv::Point3f & v2); + cv::Point3f SUB(const cv::Point3f & v1, const cv::Point3f & v2); + cv::Point3f getNearest3DPoint(std::vector<cv::Point3f> & points_list, const cv::Point3f & origin); + + bool backproject2DPoint(const Mesh * mesh, const cv::Point2f & point2d, cv::Point3f & point3d); + bool intersectMollerTrumbore(Ray & ray, Triangle & Triangle, double * out); + std::vector<cv::Point2f> verifyPoints(Mesh * mesh); + cv::Point2f backproject3DPoint(const cv::Point3f & point3d); + bool estimatePose(const std::vector<cv::Point3f> & list_points3d, const std::vector<cv::Point2f> & list_points2d, int flags); + void estimatePoseRANSAC(const std::vector<cv::Point3f> & list_points3d, const std::vector<cv::Point2f> & list_points2d, + int flags, const cv::Mat & inliers, int iterations_count, float reprojection_error, double confidence); - void clearExtrinsics() - { - _R_matrix = cv::Mat::zeros(3, 3, CV_64FC1); // rotation matrix - _t_matrix = cv::Mat::zeros(3, 1, CV_64FC1); // translation matrix - _P_matrix = cv::Mat::zeros(3, 4, CV_64FC1); - } + cv::Mat getAMatrix() const; + cv::Mat getDistCoef() const; + cv::Mat getRMatrix() const; + cv::Mat getRVect() const; + cv::Mat getTMatrix() const; + cv::Mat getPMatrix() const; + + void setPMatrix(const cv::Mat & r_matrix, const cv::Mat & t_matrix); + void clearExtrinsics(); -private: - /** The calibration matrix */ - cv::Mat _A_matrix; - //dist mat - cv::Mat _dist; - /** The computed rotation matrix */ - cv::Mat _R_matrix; - cv::Mat _R_vect; - /** The computed translation matrix */ - cv::Mat _t_matrix; - /** The computed projection matrix */ - cv::Mat _P_matrix; -}; + private: -// Functions for Möller–Trumbore intersection algorithm -cv::Point3f CROSS(cv::Point3f v1, cv::Point3f v2); -double DOT(cv::Point3f v1, cv::Point3f v2); -cv::Point3f SUB(cv::Point3f v1, cv::Point3f v2); + /** The calibration matrix */ + cv::Mat a_matrix_; + //dist mat + cv::Mat dist_; + /** The computed rotation matrix */ + cv::Mat r_matrix_; + cv::Mat r_vect_; + /** The computed translation matrix */ + cv::Mat t_matrix_; + /** The computed projection matrix */ + cv::Mat p_matrix_; + }; + + } -#endif /* PNPPROBLEM_H_ */ +} diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/RobustMatcher.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/RobustMatcher.h index e1e95536..1723fd38 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/RobustMatcher.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/RobustMatcher.h @@ -4,101 +4,102 @@ * Created on: Jun 4, 2014 * Author: eriba */ - -#ifndef ROBUSTMATCHER_H_ -#define ROBUSTMATCHER_H_ - +#pragma once #include <iostream> - #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/features2d/features2d.hpp> +#include <opencv2/features2d.hpp> +#include <opencv2/xfeatures2d.hpp> +#include <opencv2/ml.hpp> #include "od/common/utils/ODFeatureDetector2D.h" #include "od/common/utils/utils.h" #include "od/detectors/local2D/simple_ransac_detection/Model.h" - -using namespace std; - -class -RobustMatcher { -public: - RobustMatcher(Model const &model, bool use_gpu, bool b); +#include "od/detectors/local2D/simple_ransac_detection/Utils.h" - int mode_; - bool use_gpu_; +namespace od { + + namespace l2d { - virtual ~RobustMatcher(); + class + RobustMatcher { + public: + RobustMatcher(Model const & model, bool use_gpu, bool b); - // Set the feature detector - void setFeatureDetector(const cv::Ptr<cv::FeatureDetector>& detect) { detector_ = detect; } + virtual ~RobustMatcher(); - // Set the descriptor extractor - void setDescriptorExtractor(const cv::Ptr<cv::DescriptorExtractor>& desc) { extractor_ = desc; } + // Set the feature detector + void setFeatureDetector(const cv::Ptr<cv::FeatureDetector> & detect) { detector_ = detect; } - // Set the matcher - void setDescriptorMatcher(const cv::Ptr<cv::DescriptorMatcher>& match) { matcher_ = match; } + // Set the descriptor extractor + void setDescriptorExtractor(const cv::Ptr<cv::DescriptorExtractor> & desc) { extractor_ = desc; } - // Compute the keypoints of an image - void computeKeyPoints( const cv::Mat& image, std::vector<cv::KeyPoint>& keypoints); + // Set the matcher + void setDescriptorMatcher(const cv::Ptr<cv::DescriptorMatcher> & match) { matcher_ = match; } - // Compute the descriptors of an image given its keypoints - void computeDescriptors( const cv::Mat& image, std::vector<cv::KeyPoint>& keypoints, cv::Mat& descriptors); + // Compute the keypoints of an image + void computeKeyPoints( const cv::Mat& image, std::vector<cv::KeyPoint> & keypoints); - // Set ratio parameter for the ratio test - void setRatio( float rat) { ratio_ = rat; } + // Compute the descriptors of an image given its keypoints + void computeDescriptors( const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints, cv::Mat & descriptors); - // Clear matches for which NN ratio is > than threshold - // return the number of removed points - // (corresponding entries being cleared, - // i.e. size will be 0) - int ratioTest(std::vector<std::vector<cv::DMatch> > &matches); + // Set ratio parameter for the ratio test + void setRatio( float rat) { ratio_ = rat; } - // Insert symmetrical matches in symMatches vector - void symmetryTest( const std::vector<std::vector<cv::DMatch> >& matches1, - const std::vector<std::vector<cv::DMatch> >& matches2, - std::vector<cv::DMatch>& symMatches ); + // Clear matches for which NN ratio is > than threshold + // return the number of removed points + // (corresponding entries being cleared, + // i.e. size will be 0) + int ratioTest(std::vector<std::vector<cv::DMatch> > & matches); - // Match feature points using ratio and symmetry test - void robustMatch( const cv::Mat& frame, std::vector<cv::DMatch>& good_matches, - std::vector<cv::KeyPoint>& keypoints_frame, - const cv::Mat& descriptors_model ); + // Insert symmetrical matches in symMatches vector + void symmetryTest( const std::vector<std::vector<cv::DMatch> > & matches1, + const std::vector<std::vector<cv::DMatch> > & matches2, + std::vector<cv::DMatch> & symMatches ); - // Match feature points using ratio test - void fastRobustMatch( const cv::Mat& frame, std::vector<cv::DMatch>& good_matches, - std::vector<cv::KeyPoint>& keypoints_frame, - const cv::Mat& descriptors_model ); + // Match feature points using ratio and symmetry test + void robustMatch( const cv::Mat & frame, std::vector<cv::DMatch> & good_matches, + std::vector<cv::KeyPoint> & keypoints_frame, + const cv::Mat & descriptors_model ); - void findFeatureAndMatch(cv::Mat const &frame, vector<cv::DMatch> &good_matches, vector<cv::KeyPoint> &keypoints_frame, - cv::Mat const &descriptors_model); + // Match feature points using ratio test + void fastRobustMatch( const cv::Mat & frame, std::vector<cv::DMatch> & good_matches, + std::vector<cv::KeyPoint> & keypoints_frame, + const cv::Mat & descriptors_model ); - void match(const cv::Mat & descriptors_frame, const cv::Mat &descriptors_model, std::vector<cv::DMatch>& good_matches); + void findFeatureAndMatch(cv::Mat const & frame, std::vector<cv::DMatch> & good_matches, std::vector<cv::KeyPoint> & keypoints_frame, + cv::Mat const & descriptors_model); - void matchNormalized(cv::Mat &descriptors_frame, cv::Mat &descriptors_model, vector<cv::DMatch> &good_matches); + void match(const cv::Mat & descriptors_frame, const cv::Mat & descriptors_model, std::vector<cv::DMatch> & good_matches); -private: - // pointer to the feature point detector object - cv::Ptr<od::ODFeatureDetector2D> featureDetector_; + void matchNormalized(cv::Mat & descriptors_frame, cv::Mat & descriptors_model, std::vector<cv::DMatch> & good_matches); - cv::Ptr<cv::FeatureDetector> detector_; - // pointer to the feature descriptor extractor object - cv::Ptr<cv::DescriptorExtractor> extractor_; - // pointer to the matcher object + int mode_; + bool use_gpu_; - //The important feature of this class starts here - //DIFFERENT TYPES OF MATCHERS, add here in this list (based on the construction - cv::Ptr<cv::DescriptorMatcher> matcher_; - cv::Ptr<cv::cuda::DescriptorMatcher> matcher_gpu_; - // max ratio between 1st and 2nd NN - float ratio_; + private: - void instantiateMatcher(Model const &feature_type, bool use_gpu); + void instantiateMatcher(Model const & feature_type, bool use_gpu); + void instantiateMatcher1(Model const & model, bool use_gpu); + // pointer to the feature point detector object + cv::Ptr<od::ODFeatureDetector2D> featureDetector_; + cv::Ptr<cv::FeatureDetector> detector_; + // pointer to the feature descriptor extractor object + cv::Ptr<cv::DescriptorExtractor> extractor_; + // pointer to the matcher object + //The important feature of this class starts here + //DIFFERENT TYPES OF MATCHERS, add here in this list (based on the construction + cv::Ptr<cv::DescriptorMatcher> matcher_; + cv::Ptr<cv::cuda::DescriptorMatcher> matcher_gpu_; + // max ratio between 1st and 2nd NN + float ratio_; + }; - void instantiateMatcher1(Model const &model, bool use_gpu); + } -}; +} -#endif /* ROBUSTMATCHER_H_ */ diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/Utils.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/Utils.h index 6603cb59..73285b73 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/Utils.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/Utils.h @@ -4,77 +4,86 @@ * Created on: Mar 28, 2014 * Author: Edgar Riba */ - -#ifndef UTILS_H_ -#define UTILS_H_ - +#pragma once #include <iostream> #include <sys/time.h> - #include "od/detectors/local2D/simple_ransac_detection/PnPProblem.h" +#include "od/detectors/local2D/simple_ransac_detection/ModelRegistration.h" #include "od/detectors/local2D/simple_ransac_detection/Model.h" +#include "od/detectors/local2D/simple_ransac_detection/Mesh.h" +#include <opencv2/imgproc/imgproc.hpp> +#include <opencv2/calib3d/calib3d.hpp> + + +class Mesh; +class PnPProblem; +class Model; -using namespace std; +namespace od { + + namespace l2d { -void viewImage(cv::Mat image, vector<cv::KeyPoint> keypoints = vector<cv::KeyPoint>(0)); + void viewImage(cv::Mat image, std::vector<cv::KeyPoint> keypoints = std::vector<cv::KeyPoint>(0)); -// Draw a text with the question point -void drawQuestion(cv::Mat image, cv::Point3f point, cv::Scalar color); + // Draw a text with the question point + void drawQuestion(cv::Mat image, cv::Point3f point, cv::Scalar color); -// Draw a text with the number of entered points -void drawText(cv::Mat image, std::string text, cv::Scalar color); + // Draw a text with the number of entered points + void drawText(cv::Mat image, std::string text, cv::Scalar color); -// Draw a text with the number of entered points -void drawText2(cv::Mat image, std::string text, cv::Scalar color); + // Draw a text with the number of entered points + void drawText2(cv::Mat image, std::string text, cv::Scalar color); -// Draw a text with the frame ratio -void drawFPS(cv::Mat image, double fps, cv::Scalar color); + // Draw a text with the frame ratio + void drawFPS(cv::Mat image, double fps, cv::Scalar color); -// Draw a text with the frame ratio -void drawConfidence(cv::Mat image, double confidence, cv::Scalar color); + // Draw a text with the frame ratio + void drawConfidence(cv::Mat image, double confidence, cv::Scalar color); -// Draw a text with the number of entered points -void drawCounter(cv::Mat image, int n, int n_max, cv::Scalar color); + // Draw a text with the number of entered points + void drawCounter(cv::Mat image, int n, int n_max, cv::Scalar color); -// Draw the points and the coordinates -void drawPoints(cv::Mat image, std::vector<cv::Point2f> &list_points_2d, std::vector<cv::Point3f> &list_points_3d, cv::Scalar color); + // Draw the points and the coordinates + void drawPoints(cv::Mat image, std::vector<cv::Point2f> & list_points_2d, std::vector<cv::Point3f> & list_points_3d, cv::Scalar color); -// Draw only the 2D points -void draw2DPoints(cv::Mat image, std::vector<cv::Point2f> &list_points, cv::Scalar color); + // Draw only the 2D points + void draw2DPoints(cv::Mat image, std::vector<cv::Point2f> & list_points, cv::Scalar color); -// Draw an arrow into the image -void drawArrow(cv::Mat image, cv::Point2i p, cv::Point2i q, cv::Scalar color, int arrowMagnitude = 9, int thickness=1, int line_type=8, int shift=0); + // Draw an arrow into the image + void drawArrow(cv::Mat image, cv::Point2i p, cv::Point2i q, cv::Scalar color, int arrowMagnitude = 9, + int thickness = 1, int line_type = 8, int shift = 0); -// Draw the 3D coordinate axes -void draw3DCoordinateAxes(cv::Mat image, const std::vector<cv::Point2f> &list_points2d); + // Draw the 3D coordinate axes + void draw3DCoordinateAxes(cv::Mat image, const std::vector<cv::Point2f> & list_points2d); -// Draw the object mesh -void drawObjectMesh(cv::Mat image, const Mesh *mesh, PnPProblem *pnpProblem, cv::Scalar color); -void drawObjectMesh1(cv::Mat image, const Mesh *mesh, const Model *model, PnPProblem *pnpProblem, cv::Scalar color); -void drawModel(cv::Mat image, const Model *model, PnPProblem *pnpProblem, cv::Scalar color); -void drawModel(cv::Mat image, const Model *model, cv::Mat R_vect, cv::Mat t_mat, cv::Mat A_mat, cv::Mat dist, cv::Scalar color); + // Draw the object mesh + void drawObjectMesh(cv::Mat image, const Mesh * mesh, PnPProblem * pnpProblem, cv::Scalar color); + void drawObjectMesh1(cv::Mat image, const Mesh * mesh, const Model * model, PnPProblem * pnpProblem, cv::Scalar color); + void drawModel(cv::Mat image, const Model * model, PnPProblem * pnpProblem, cv::Scalar color); + void drawModel(cv::Mat image, const Model * model, cv::Mat R_vect, cv::Mat t_mat, cv::Mat A_mat, cv::Mat dist, cv::Scalar color); -// Computes the norm of the translation error -double get_translation_error(const cv::Mat &t_true, const cv::Mat &t); + // Computes the norm of the translation error + double get_translation_error(const cv::Mat & t_true, const cv::Mat & t); -// Computes the norm of the rotation error -double get_rotation_error(const cv::Mat &R_true, const cv::Mat &R); + // Computes the norm of the rotation error + double get_rotation_error(const cv::Mat & R_true, const cv::Mat & R); -// Converts a given Rotation Matrix to Euler angles -cv::Mat rot2euler(const cv::Mat & rotationMatrix); + // Converts a given Rotation Matrix to Euler angles + cv::Mat rot2euler(const cv::Mat & rotationMatrix); -// Converts a given Euler angles to Rotation Matrix -cv::Mat euler2rot(const cv::Mat & euler); + // Converts a given Euler angles to Rotation Matrix + cv::Mat euler2rot(const cv::Mat & euler); -// Converts a given string to an integer -int StringToInt ( const std::string &Text ); + // Converts a given string to an integer + int StringToInt (const std::string & Text ); -// Converts a given float to a string -std::string FloatToString ( float Number ); + // Converts a given float to a string + std::string FloatToString (float Number ); -// Converts a given integer to a string -std::string IntToString ( int Number ); + // Converts a given integer to a string + std::string IntToString (int Number ); + } +} -#endif /* UTILS_H_ */ diff --git a/detectors/src/global2D/detection/ODCascadeDetector.cpp b/detectors/src/global2D/detection/ODCascadeDetector.cpp index 8e02cf06..7f09fc59 100644 --- a/detectors/src/global2D/detection/ODCascadeDetector.cpp +++ b/detectors/src/global2D/detection/ODCascadeDetector.cpp @@ -42,7 +42,7 @@ namespace od { TRAINED_LOCATION_DENTIFIER_ = "CASCADE"; TRAINED_DATA_ID_ = "cascade.xml"; - metainfo_ = true; + meta_info_ = true; } void ODCascadeDetector::init() @@ -72,7 +72,7 @@ namespace od detection2D->setBoundingBox(face_i); detections->push_back(detection2D); - if(metainfo_) + if(meta_info_) { cv::rectangle(viz, face_i, CV_RGB(0, 255, 0), 1); } diff --git a/detectors/src/global3D/ODPointCloudGlobalMatching.cpp b/detectors/src/global3D/ODPointCloudGlobalMatching.cpp index 0dc02c0f..b90737cd 100644 --- a/detectors/src/global3D/ODPointCloudGlobalMatching.cpp +++ b/detectors/src/global3D/ODPointCloudGlobalMatching.cpp @@ -28,3 +28,23 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // #include "od/detectors/global3D/ODPointCloudGlobalMatching.h" + + +namespace od { + + namespace g3d { + + int ODPointCloudGlobalMatching ::train() + { + return trainer_->train(); + } + + int ODPointCloudGlobalMatching::detect(ODScene * scene, std::vector<ODDetection *> detections) + { + //detector_->detect(scene, detections); + return 0; + } + + } + +} \ No newline at end of file diff --git a/detectors/src/global3D/training/ODCADDetectTrainer3DGlobal.cpp b/detectors/src/global3D/training/ODCADDetectTrainer3DGlobal.cpp index 37cb9f22..cc8f2241 100644 --- a/detectors/src/global3D/training/ODCADDetectTrainer3DGlobal.cpp +++ b/detectors/src/global3D/training/ODCADDetectTrainer3DGlobal.cpp @@ -40,17 +40,43 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <pcl/apps/dominant_plane_segmentation.h> #include <pcl/console/parse.h> +namespace od { -int od::g3d::ODCADDetectTrainer3DGlobal::train() -{ - boost::shared_ptr<pcl::rec_3d_framework::MeshSource<pcl::PointXYZ> > mesh_source (new pcl::rec_3d_framework::MeshSource<pcl::PointXYZ>); - mesh_source->setPath (training_input_location_); - mesh_source->setResolution (150); - mesh_source->setTesselationLevel (1); - mesh_source->setViewAngle (57.f); - mesh_source->setRadiusSphere (1.5f); - mesh_source->setModelScale (1.f); - std::string training_dir = getSpecificTrainingDataLocation(); - mesh_source->generate (training_dir); - return 1; + namespace g3d { + + ODCADDetectTrainer3DGlobal::ODCADDetectTrainer3DGlobal(const std::string & training_input_location_, const std::string & training_data_location_) : + ODTrainer(training_input_location_, training_data_location_) + { + desc_name_ = "esf"; + TRAINED_LOCATION_DENTIFIER_ = "GLOBAL3DVFH"; + } + + + int ODCADDetectTrainer3DGlobal::train() + { + boost::shared_ptr<pcl::rec_3d_framework::MeshSource<pcl::PointXYZ> > mesh_source(new pcl::rec_3d_framework::MeshSource<pcl::PointXYZ>); + mesh_source->setPath(training_input_location_); + mesh_source->setResolution(150); + mesh_source->setTesselationLevel(1); + mesh_source->setViewAngle(57.f); + mesh_source->setRadiusSphere(1.5f); + mesh_source->setModelScale(1.f); + std::string location = getSpecificTrainingDataLocation(); + mesh_source->generate(location); + return 1; + } + + const std::string & ODCADDetectTrainer3DGlobal::getDescName() const + { + return desc_name_; + } + + void ODCADDetectTrainer3DGlobal::setDescName(const std::string & desc_name) + { + desc_name_ = desc_name; + } + + } } + + diff --git a/detectors/src/local2D/CMakeLists.txt b/detectors/src/local2D/CMakeLists.txt index 6bb957b5..885ed83d 100644 --- a/detectors/src/local2D/CMakeLists.txt +++ b/detectors/src/local2D/CMakeLists.txt @@ -3,11 +3,6 @@ set(LIB_NAME od_${SUBSYS_NAME}) set(SUBSYS_DESC "The local feature matching based detector") set(SUBSYS_DEPS od_common ${PCL_LIBRARIES} ${VTK_LIBRARIES} ${OpenCV_LIBS}) - - -include_directories(${CMAKE_SOURCE_DIR}/3rdparty/pugixml/src/) -#link_directories(${CMAKE_SOURCE_DIR}/3rdparty/pugixml/build/) - set(build TRUE) #PCL_SUBSYS_OPTION(build "${SUBSYS_NAME}" "${SUBSYS_DESC}" ON) #PCL_SUBSYS_DEPEND(build "${SUBSYS_NAME}" DEPS ${SUBSYS_DEPS}) @@ -17,7 +12,6 @@ if(build) set(IMPL_INCLUDES "") - set(SOURCES "ODImageLocalMatching.cpp" "training/ODCADRecogTrainerSnapshotBased.cpp" @@ -33,6 +27,7 @@ if(build) ) include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${CMAKE_3RDPARTY_DIR}/pugixml/src/) include_directories(${COMMON_INCLUDE_DIR}) OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${SOURCES}) diff --git a/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp b/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp index aff4666b..2f33376b 100644 --- a/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp +++ b/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp @@ -47,17 +47,163 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/local2D/simple_ransac_detection/ModelRegistration.h" #include "od/detectors/local2D/simple_ransac_detection/Utils.h" - -using namespace cv; -using namespace std; -using namespace cv::xfeatures2d; +//using namespace cv::xfeatures2d; namespace od { namespace l2d { - void ODCADRecognizer2DLocal::parseParameterString(string parameter_string) + ODCADRecognizer2DLocal::ODCADRecognizer2DLocal(const std::string & trained_data_location_): + ODImageLocalMatchingDetector(trained_data_location_) + { + meta_info_ = true; + + camera_intrinsic_file_ = "Data/out_camera_data_lion_old.yml"; // mesh + + red_ = cv::Scalar(0, 0, 255); + green_ = cv::Scalar(0, 255, 0); + blue_ = cv::Scalar(255, 0, 0); + yellow_ = cv::Scalar(0, 255, 255); + + num_key_points_ = 2000; // number of detected keypoints + ratio_test_ = 0.70f; // ratio test + fast_match_ = true; // fastRobustMatch() or robustMatch() + use_gpu_ = false; + use_gpu_match_ = false; + + iterations_count_ = 500; // number of Ransac iterations. + reprojection_error_ = 2.0; // maximum allowed distance to consider it an inlier. + confidence_ = 0.95; // ransac successful confidence. + + min_inliers_ = 30; // Kalman threshold updating + + pnp_method_ = cv::SOLVEPNP_EPNP; + f_type_default_ = "SIFT"; + feature_detector_ = std::make_shared<ODFeatureDetector2D>(f_type_default_, use_gpu_); + } + + const std::string & ODCADRecognizer2DLocal::getCameraIntrinsicFile() const + { + return camera_intrinsic_file_; + } + + void ODCADRecognizer2DLocal::setCameraIntrinsicFile(const std::string & camera_intrinsic_file) + { + camera_intrinsic_file_ = camera_intrinsic_file; + } + + int ODCADRecognizer2DLocal::getNumKeyPoints() const + { + return num_key_points_; + } + + void ODCADRecognizer2DLocal::setNumKeyPoints(int num_key_points) + { + num_key_points_ = num_key_points; + } + + float ODCADRecognizer2DLocal::getRatioTest() const + { + return ratio_test_; + } + + void ODCADRecognizer2DLocal::setRatioTest(float ratio_test) + { + ratio_test_ = ratio_test; + } + + bool ODCADRecognizer2DLocal::isFastMatch() const + { + return fast_match_; + } + + void ODCADRecognizer2DLocal::setFastMatch(bool fast_match) + { + fast_match_ = fast_match; + } + + bool ODCADRecognizer2DLocal::isUseGpu() const + { + return use_gpu_; + } + + void ODCADRecognizer2DLocal::setUseGpu(bool use_gpu) + { + use_gpu_ = use_gpu; + } + + bool ODCADRecognizer2DLocal::isUseGpuMatch() const + { + return use_gpu_match_; + } + + void ODCADRecognizer2DLocal::setUseGpuMatch(bool use_gpu_match) + { + use_gpu_match_ = use_gpu_match; + } + + bool ODCADRecognizer2DLocal::isMetainfo() const + { + return meta_info_; + } + + void ODCADRecognizer2DLocal::setMetainfo(bool meta_info) + { + meta_info_ = meta_info; + } + + int ODCADRecognizer2DLocal::getIterationsCount() const + { + return iterations_count_; + } + + void ODCADRecognizer2DLocal::setIterationsCount(int iterations_count) + { + iterations_count_ = iterations_count; + } + + float ODCADRecognizer2DLocal::getReprojectionError() const + { + return reprojection_error_; + } + + void ODCADRecognizer2DLocal::setReprojectionError(float reprojection_error) + { + reprojection_error_ = reprojection_error; + } + + double ODCADRecognizer2DLocal::getConfidence() const + { + return confidence_; + } + + void ODCADRecognizer2DLocal::setConfidence(double confidence) + { + confidence_ = confidence; + } + + int ODCADRecognizer2DLocal::getMinInliers() const + { + return min_inliers_; + } + + void ODCADRecognizer2DLocal::setMinInliers(int min_inliers) + { + min_inliers_ = min_inliers; + } + + int ODCADRecognizer2DLocal::getPnpMethod() const + { + return pnp_method_; + } + + void ODCADRecognizer2DLocal::setPnpMethod(int pnp_method) + { + pnp_method_ = pnp_method; + } + + void ODCADRecognizer2DLocal::parseParameterString(const std::string & parameter_string) { const std::string keys = "{help h | | print this message }" "{video v | | path to recorded video }" @@ -80,128 +226,99 @@ namespace od int argc; fileutils::getArgvArgc(parameter_string, &argv, argc); - CommandLineParser parser(argc, argv, keys); + cv::CommandLineParser parser(argc, argv, keys); if(parser.has("help")) { parser.printMessage(); } else { - camera_intrinsic_file = - parser.get<string>("camera_intrinsic_file").size() > 0 ? parser.get<string>("camera_intrinsic_file") : camera_intrinsic_file; - - use_gpu = parser.has("use_gpu"); - use_gpu_match = parser.has("use_gpu_match"); - - numKeyPoints = !parser.has("keypoints") ? parser.get<int>("keypoints") : numKeyPoints; - ratioTest = !parser.has("ratio") ? parser.get<float>("ratio") : ratioTest; - fast_match = !parser.has("fast") ? parser.get<bool>("fast") : fast_match; - iterationsCount = !parser.has("iterations") ? parser.get<int>("iterations") : iterationsCount; - reprojectionError = !parser.has("error") ? parser.get<float>("error") : reprojectionError; - confidence = !parser.has("confidence") ? parser.get<float>("confidence") : confidence; - minInliers = !parser.has("inliers") ? parser.get<int>("inliers") : minInliers; - pnpMethod = !parser.has("method") ? parser.get<int>("method") : pnpMethod; - metainfo_ = parser.has("metainfo"); + camera_intrinsic_file_ = + parser.get<std::string>("camera_intrinsic_file").size() > 0 ? parser.get<std::string>("camera_intrinsic_file") : camera_intrinsic_file_; + + use_gpu_ = parser.has("use_gpu"); + use_gpu_match_ = parser.has("use_gpu_match"); + num_key_points_ = !parser.has("keypoints") ? parser.get<int>("keypoints") : num_key_points_; + ratio_test_ = !parser.has("ratio") ? parser.get<float>("ratio") : ratio_test_; + fast_match_ = !parser.has("fast") ? parser.get<bool>("fast") : fast_match_; + iterations_count_ = !parser.has("iterations") ? parser.get<int>("iterations") : iterations_count_; + reprojection_error_ = !parser.has("error") ? parser.get<float>("error") : reprojection_error_; + confidence_ = !parser.has("confidence") ? parser.get<float>("confidence") : confidence_; + min_inliers_ = !parser.has("inliers") ? parser.get<int>("inliers") : min_inliers_; + pnp_method_ = !parser.has("method") ? parser.get<int>("method") : pnp_method_; + meta_info_ = parser.has("metainfo"); } } void ODCADRecognizer2DLocal::init() { - FileStorage fs(camera_intrinsic_file, FileStorage::READ); - Mat cam_man, dist_coeff; + cv::FileStorage fs(camera_intrinsic_file_, cv::FileStorage::READ); + cv::Mat cam_man, dist_coeff; fs["Camera_Matrix"] >> cam_man; fs["Distortion_Coefficients"] >> dist_coeff; - pnp_detection = PnPProblem(cam_man, dist_coeff); + pnp_detection_ = PnPProblem(cam_man, dist_coeff); // get all trained models - fileutils::getFilesInDirectoryRec(getSpecificTrainingDataLocation(), TRAINED_DATA_ID_, model_names); + fileutils::getFilesInDirectoryRec(getSpecificTrainingDataLocation(), TRAINED_DATA_ID_, model_names_); - for(int i = 0; i < model_names.size(); i++) + for(int i = 0; i < model_names_.size(); i++) { Model model; - model.load_new_xml(model_names[i]); - models.push_back(model); + model.loadNewXml(model_names_[i]); + models_.push_back(model); } - if(models.size() > 0) - f_type_default = models[0].f_type; + if(models_.size() > 0) + f_type_default_ = models_[0].f_type_; - featureDetector = std::make_shared<ODFeatureDetector2D>(f_type_default, use_gpu); + feature_detector_ = std::make_shared<ODFeatureDetector2D>(f_type_default_, use_gpu_); } - ODDetections* ODCADRecognizer2DLocal::detect(ODSceneImage *scene) + ODDetections * ODCADRecognizer2DLocal::detect(ODSceneImage * scene) { - ODDetections3D *detections = detectOmni(scene); + ODDetections3D * detections = detectOmni(scene); return detections; } - - ODDetections3D* ODCADRecognizer2DLocal::detectOmni(ODSceneImage *scene) - { - - vector<KeyPoint> keypoints_scene; - Mat descriptor_scene; - featureDetector->computeKeypointsAndDescriptors(scene->getCVImage(), descriptor_scene, keypoints_scene); - scene->setDescriptors(descriptor_scene); - scene->setKeypoints(keypoints_scene); - - ODDetections3D *detections = new ODDetections3D; - cv::Mat viz = scene->getCVImage().clone(); - - for(int i = 0; i < models.size(); i++) - { - - ODDetection3D *detection; - if(detectSingleModel(scene, models[i], detection, viz)) - { - detections->push_back(detection); - - if(metainfo_) - drawModel(viz, &models[i], pnp_detection.get_R_vect(), pnp_detection.get_t_matrix(), pnp_detection.get_A_matrix(), pnp_detection.get_dist_coef(), yellow); - } - } - detections->setMetainfoImage(viz); - return detections; - } - - bool ODCADRecognizer2DLocal::detectSingleModel(ODSceneImage *scene, Model const &model, ODDetection3D *&detection3D, Mat & frame_vis) + bool ODCADRecognizer2DLocal::detectSingleModel(ODSceneImage * scene, const Model & model, ODDetection3D * detection3D, const cv::Mat & frame_vis) { //reset - pnp_detection.clearExtrinsics(); + pnp_detection_.clearExtrinsics(); - vector<Point3f> list_points3d_model = model.get_points3d(); // list with model 3D coordinates - vector<KeyPoint> list_keypoints_model = model.get_keypoints(); // list with model 3D coordinates - Mat descriptors_model = model.get_descriptors(); // list with descriptors of each 3D coordinate + std::vector<cv::Point3f> list_points3d_model = model.getPoints3d(); // list with model 3D coordinates + std::vector<cv::KeyPoint> list_keypoints_model = model.getKeypoints(); // list with model 3D coordinates + cv::Mat descriptors_model = model.getDescriptors(); // list with descriptors of each 3D coordinate - RobustMatcher rmatcher(model, use_gpu, use_gpu_match); // instantiate RobustMatcher + RobustMatcher rmatcher(model, use_gpu_, use_gpu_match_); // instantiate RobustMatcher // -- Step 1: Robust matching between model descriptors and scene descriptors - vector<DMatch> good_matches; // to obtain the 3D points of the model - vector<KeyPoint> keypoints_scene = scene->getKeypoints(); // to obtain the 2D points of the scene + std::vector<cv::DMatch> good_matches; // to obtain the 3D points of the model + std::vector<cv::KeyPoint> keypoints_scene = scene->getKeypoints(); // to obtain the 2D points of the scene - Mat frame = scene->getCVImage(); + cv::Mat frame = scene->getCVImage(); //normalize - Mat scenedes = scene->getDescriptors(); + cv::Mat scenedes = scene->getDescriptors(); rmatcher.matchNormalized(scenedes, descriptors_model, good_matches); //rmatcher.match(scenedes, descriptors_model, good_matches); - if(good_matches.size() <= 0) return false; + if(good_matches.size() <= 0) + return false; - vector<Point3f> list_points3d_model_match; // container for the model 3D coordinates found in the scene - vector<KeyPoint> list_keypoints_model_match; // container for the model 2D coordinates in the textured image of the corresponding 3D coordinates - vector<Point2f> list_points2d_scene_match; // container for the model 2D coordinates found in the scene + std::vector<cv::Point3f> list_points3d_model_match; // container for the model 3D coordinates found in the scene + std::vector<cv::KeyPoint> list_keypoints_model_match; // container for the model 2D coordinates in the textured image of the corresponding 3D coordinates + std::vector<cv::Point2f> list_points2d_scene_match; // container for the model 2D coordinates found in the scene - for(unsigned int match_index = 0; match_index < good_matches.size(); ++match_index) + for(size_t match_index = 0; match_index < good_matches.size(); ++match_index) { - Point3f point3d_model = list_points3d_model[good_matches[match_index].trainIdx]; // 3D point from model - KeyPoint kp_model = list_keypoints_model[good_matches[match_index].trainIdx]; // 2D point from model - Point2f point2d_scene = keypoints_scene[good_matches[match_index].queryIdx].pt; // 2D point from the scene + cv::Point3f point3d_model = list_points3d_model[good_matches[match_index].trainIdx]; // 3D point from model + cv::KeyPoint kp_model = list_keypoints_model[good_matches[match_index].trainIdx]; // 2D point from model + cv::Point2f point2d_scene = keypoints_scene[good_matches[match_index].queryIdx].pt; // 2D point from the scene list_points3d_model_match.push_back(point3d_model); // add 3D point list_keypoints_model_match.push_back(kp_model); @@ -209,27 +326,55 @@ namespace od } - Mat inliers_idx; - vector<Point2f> list_points2d_inliers; + cv::Mat inliers_idx; + std::vector<cv::Point2f> list_points2d_inliers; // -- Step 3: Estimate the pose using RANSAC approach - pnp_detection.estimatePoseRANSAC(list_points3d_model_match, list_points2d_scene_match, pnpMethod, inliers_idx, - iterationsCount, reprojectionError, confidence); + pnp_detection_.estimatePoseRANSAC(list_points3d_model_match, list_points2d_scene_match, pnp_method_, inliers_idx, + iterations_count_, reprojection_error_, confidence_); - if(inliers_idx.rows < minInliers) return false; + if(inliers_idx.rows < min_inliers_) + return false; - cout << "RECOGNIZED: " << model.id << endl; + std::cout << "RECOGNIZED: " << model.id_ << std::endl; //else everything is fine; report the detection detection3D = new ODDetection3D(); - detection3D->setLocation(pnp_detection.get_t_matrix()); - detection3D->setPose(pnp_detection.get_R_matrix()); + detection3D->setLocation(pnp_detection_.getTMatrix()); + detection3D->setPose(pnp_detection_.getRMatrix()); detection3D->setType(ODDetection::OD_DETECTION_RECOG); - detection3D->setId(model.id); + detection3D->setId(model.id_); + return true; + } + ODDetections3D * ODCADRecognizer2DLocal::detectOmni(ODSceneImage * scene) + { - return true; + std::vector<cv::KeyPoint> keypoints_scene; + cv::Mat descriptor_scene; + feature_detector_->computeKeypointsAndDescriptors(scene->getCVImage(), descriptor_scene, keypoints_scene); + scene->setDescriptors(descriptor_scene); + scene->setKeypoints(keypoints_scene); + + ODDetections3D * detections = new ODDetections3D; + cv::Mat viz = scene->getCVImage().clone(); + + for(size_t i = 0; i < models_.size(); ++i) + { + + ODDetection3D * detection; + if(detectSingleModel(scene, models_[i], detection, viz)) + { + detections->push_back(detection); + + if(meta_info_) + drawModel(viz, &models_[i], pnp_detection_.getRVect(), pnp_detection_.getTMatrix(), pnp_detection_.getAMatrix(), + pnp_detection_.getDistCoef(), yellow_); + } + } + detections->setMetainfoImage(viz); + return detections; } } diff --git a/detectors/src/local2D/simple_ransac_detection/CsvReader.cpp b/detectors/src/local2D/simple_ransac_detection/CsvReader.cpp index 8522ea4e..f92e2e67 100644 --- a/detectors/src/local2D/simple_ransac_detection/CsvReader.cpp +++ b/detectors/src/local2D/simple_ransac_detection/CsvReader.cpp @@ -1,80 +1,88 @@ #include "od/detectors/local2D/simple_ransac_detection/CsvReader.h" -#include <stdlib.h> -/** The default constructor of the CSV reader Class */ -CsvReader::CsvReader(const string &path, const char &separator){ - _file.open(path.c_str(), ifstream::in); - _separator = separator; -} +namespace od { + + namespace l2d { -/* Read a plane text file with .ply format */ -void CsvReader::readPLY(vector<Point3f> &list_vertex, vector<vector<int> > &list_triangles) -{ - std::string line, tmp_str, n; - int num_vertex = 0, num_triangles = 0; - int count = 0; - bool end_header = false; - bool end_vertex = false; - - // Read the whole *.ply file - while (getline(_file, line)) { - stringstream liness(line); + /** The default constructor of the CSV reader Class */ + CsvReader::CsvReader(const std::string & path, const char & separator){ + _file_.open(path.c_str(), std::ifstream::in); + _separator_ = separator; + } - // read header - if(!end_header) - { - getline(liness, tmp_str, _separator); - if( tmp_str == "element" ) + /* Read a plane text file with .ply format */ + void CsvReader::readPLY(std::vector<cv::Point3f> & list_vertex, std::vector<std::vector<int> > & list_triangles) { - getline(liness, tmp_str, _separator); - getline(liness, n); - if(tmp_str == "vertex") num_vertex = StringToInt(n); - if(tmp_str == "face") num_triangles = StringToInt(n); - } - if(tmp_str == "end_header") end_header = true; - } + std::string line, tmp_str, n; + int num_vertex = 0, num_triangles = 0; + int count = 0; + bool end_header = false; + bool end_vertex = false; - // read file content - else if(end_header) - { - // read vertex and add into 'list_vertex' - if(!end_vertex && count < num_vertex) - { - string x, y, z; - getline(liness, x, _separator); - getline(liness, y, _separator); - getline(liness, z); + // Read the whole *.ply file + while(getline(_file_, line)) { + std::stringstream liness(line); - cv::Point3f tmp_p; - tmp_p.x = atof(x.c_str()); - tmp_p.y = atof(y.c_str()); - tmp_p.z = atof(z.c_str()); - list_vertex.push_back(tmp_p); + // read header + if(!end_header) + { + getline(liness, tmp_str, _separator_); + if(tmp_str == "element") + { + getline(liness, tmp_str, _separator_); + getline(liness, n); + if(tmp_str == "vertex") + num_vertex = std::stoi(n); + if(tmp_str == "face") + num_triangles = std::stoi(n); + } + if(tmp_str == "end_header") + end_header = true; + } - count++; - if(count == num_vertex) - { - count = 0; - end_vertex = !end_vertex; - } - } - // read faces and add into 'list_triangles' - else if(end_vertex && count < num_triangles) - { - string num_pts_per_face, id0, id1, id2; - getline(liness, num_pts_per_face, _separator); - getline(liness, id0, _separator); - getline(liness, id1, _separator); - getline(liness, id2); + // read file content + else if(end_header) + { + // read vertex and add into 'list_vertex' + if(!end_vertex && count < num_vertex) + { + std::string x, y, z; + getline(liness, x, _separator_); + getline(liness, y, _separator_); + getline(liness, z); - std::vector<int> tmp_triangle(3); - tmp_triangle[0] = StringToInt(id0); - tmp_triangle[1] = StringToInt(id1); - tmp_triangle[2] = StringToInt(id2); - list_triangles.push_back(tmp_triangle); + cv::Point3f tmp_p; + tmp_p.x = atof(x.c_str()); + tmp_p.y = atof(y.c_str()); + tmp_p.z = atof(z.c_str()); + list_vertex.push_back(tmp_p); - count++; - } + count++; + if(count == num_vertex) + { + count = 0; + end_vertex = !end_vertex; + } + } + // read faces and add into 'list_triangles' + else if(end_vertex && count < num_triangles) + { + std::string num_pts_per_face, id0, id1, id2; + getline(liness, num_pts_per_face, _separator_); + getline(liness, id0, _separator_); + getline(liness, id1, _separator_); + getline(liness, id2); + + std::vector<int> tmp_triangle(3); + tmp_triangle[0] = std::stoi(id0); + tmp_triangle[1] = std::stoi(id1); + tmp_triangle[2] = std::stoi(id2); + list_triangles.push_back(tmp_triangle); + + count++; + } + } + } + } } - } -} +} \ No newline at end of file diff --git a/detectors/src/local2D/simple_ransac_detection/CsvWriter.cpp b/detectors/src/local2D/simple_ransac_detection/CsvWriter.cpp index 94e990ec..b1855c91 100644 --- a/detectors/src/local2D/simple_ransac_detection/CsvWriter.cpp +++ b/detectors/src/local2D/simple_ransac_detection/CsvWriter.cpp @@ -1,48 +1,56 @@ #include "od/detectors/local2D/simple_ransac_detection/CsvWriter.h" -CsvWriter::CsvWriter(const string &path, const string &separator){ - _file.open(path.c_str(), ofstream::out); - _isFirstTerm = true; - _separator = separator; -} - -CsvWriter::~CsvWriter() { - _file.flush(); - _file.close(); -} - -void CsvWriter::writeXYZ(const vector<Point3f> &list_points3d) -{ - string x, y, z; - for(unsigned int i = 0; i < list_points3d.size(); ++i) - { - x = FloatToString(list_points3d[i].x); - y = FloatToString(list_points3d[i].y); - z = FloatToString(list_points3d[i].z); - - _file << x << _separator << y << _separator << z << std::endl; - } +namespace od { + + namespace l2d { + + CsvWriter::CsvWriter(const std::string & path, const std::string & separator){ + _file_.open(path.c_str(), std::ofstream::out); + _is_first_term_ = true; + _separator_ = separator; + } -} + CsvWriter::~CsvWriter() { + _file_.flush(); + _file_.close(); + } -void CsvWriter::writeUVXYZ(const vector<Point3f> &list_points3d, const vector<Point2f> &list_points2d, const Mat &descriptors) -{ - string u, v, x, y, z, descriptor_str; - for(unsigned int i = 0; i < list_points3d.size(); ++i) - { - u = FloatToString(list_points2d[i].x); - v = FloatToString(list_points2d[i].y); - x = FloatToString(list_points3d[i].x); - y = FloatToString(list_points3d[i].y); - z = FloatToString(list_points3d[i].z); + void CsvWriter::writeXYZ(const std::vector<cv::Point3f> & list_points3d) + { + std::string x, y, z; + for(size_t i = 0; i < list_points3d.size(); ++i) + { + x = std::to_string(list_points3d[i].x); + y = std::to_string(list_points3d[i].y); + z = std::to_string(list_points3d[i].z); - _file << u << _separator << v << _separator << x << _separator << y << _separator << z; + _file_ << x << _separator_ << y << _separator_ << z << std::endl; + } - for(int j = 0; j < 32; ++j) + } + + void CsvWriter::writeUVXYZ(const std::vector<cv::Point3f> & list_points3d, const std::vector<cv::Point2f> & list_points2d, const cv::Mat & descriptors) { - descriptor_str = FloatToString(descriptors.at<float>(i,j)); - _file << _separator << descriptor_str; + std::string u, v, x, y, z, descriptor_str; + for(size_t i = 0; i < list_points3d.size(); ++i) + { + u = std::to_string(list_points2d[i].x); + v = std::to_string(list_points2d[i].y); + x = std::to_string(list_points3d[i].x); + y = std::to_string(list_points3d[i].y); + z = std::to_string(list_points3d[i].z); + + _file_ << u << _separator_ << v << _separator_ << x << _separator_ << y << _separator_ << z; + + for(size_t j = 0; j < 32; ++j) + { + descriptor_str = std::to_string(descriptors.at<float>(i,j)); + _file_ << _separator_ << descriptor_str; + } + _file_ << std::endl; + } } - _file << std::endl; + } -} + +} \ No newline at end of file diff --git a/detectors/src/local2D/simple_ransac_detection/Mesh.cpp b/detectors/src/local2D/simple_ransac_detection/Mesh.cpp index eb12a286..b545d6ad 100644 --- a/detectors/src/local2D/simple_ransac_detection/Mesh.cpp +++ b/detectors/src/local2D/simple_ransac_detection/Mesh.cpp @@ -6,77 +6,83 @@ */ #include "od/detectors/local2D/simple_ransac_detection/Mesh.h" -#include "od/detectors/local2D/simple_ransac_detection/CsvReader.h" +namespace od { + + namespace l2d { -// --------------------------------------------------- // -// TRIANGLE CLASS // -// --------------------------------------------------- // + // --------------------------------------------------- // + // TRIANGLE CLASS // + // --------------------------------------------------- // -/** The custom constructor of the Triangle Class */ -Triangle::Triangle(int id, cv::Point3f V0, cv::Point3f V1, cv::Point3f V2) -{ - id_ = id; v0_ = V0; v1_ = V1; v2_ = V2; -} + /** The custom constructor of the Triangle Class */ + Triangle::Triangle(int id, const cv::Point3f & V0, const cv::Point3f & V1, const cv::Point3f & V2) + { + id_ = id; v0_ = V0; v1_ = V1; v2_ = V2; + } -/** The default destructor of the Class */ -Triangle::~Triangle() -{ - // TODO Auto-generated destructor stub -} + /** The default destructor of the Class */ + Triangle::~Triangle() + { + // TODO Auto-generated destructor stub + } -// --------------------------------------------------- // -// RAY CLASS // -// --------------------------------------------------- // + // --------------------------------------------------- // + // RAY CLASS // + // --------------------------------------------------- // -/** The custom constructor of the Ray Class */ -Ray::Ray(cv::Point3f P0, cv::Point3f P1) { - p0_ = P0; p1_ = P1; -} + /** The custom constructor of the Ray Class */ + Ray::Ray(const cv::Point3f & P0, const cv::Point3f & P1) { + p0_ = P0; p1_ = P1; + } -/** The default destructor of the Class */ -Ray::~Ray() -{ - // TODO Auto-generated destructor stub -} + /** The default destructor of the Class */ + Ray::~Ray() + { + // TODO Auto-generated destructor stub + } -// --------------------------------------------------- // -// OBJECT MESH CLASS // -// --------------------------------------------------- // + // --------------------------------------------------- // + // OBJECT MESH CLASS // + // --------------------------------------------------- // -/** The default constructor of the ObjectMesh Class */ -Mesh::Mesh() : list_vertex_(0) , list_triangles_(0) -{ - id_ = 0; - num_vertexs_ = 0; - num_triangles_ = 0; -} + /** The default constructor of the ObjectMesh Class */ + Mesh::Mesh() : list_vertex_(0) , list_triangles_(0) + { + id_ = 0; + num_vertexs_ = 0; + num_triangles_ = 0; + } -/** The default destructor of the ObjectMesh Class */ -Mesh::~Mesh() -{ - // TODO Auto-generated destructor stub -} + /** The default destructor of the ObjectMesh Class */ + Mesh::~Mesh() + { + // TODO Auto-generated destructor stub + } -/** Load a CSV with *.ply format **/ -void Mesh::load(const std::string path) -{ + /** Load a CSV with *.ply format **/ + void Mesh::load(const std::string & path) + { - // Create the reader - CsvReader csvReader(path); + // Create the reader + CsvReader csvReader(path); - // Clear previous data - list_vertex_.clear(); - list_triangles_.clear(); + // Clear previous data + list_vertex_.clear(); + list_triangles_.clear(); - // Read from .ply file - csvReader.readPLY(list_vertex_, list_triangles_); + // Read from .ply file + csvReader.readPLY(list_vertex_, list_triangles_); - // Update mesh attributes - num_vertexs_ = (int)list_vertex_.size(); - num_triangles_ = (int)list_triangles_.size(); + // Update mesh attributes + num_vertexs_ = (int)list_vertex_.size(); + num_triangles_ = (int)list_triangles_.size(); -} + } + + } + +} \ No newline at end of file diff --git a/detectors/src/local2D/simple_ransac_detection/Model.cpp b/detectors/src/local2D/simple_ransac_detection/Model.cpp index 47008ffd..132d1d22 100644 --- a/detectors/src/local2D/simple_ransac_detection/Model.cpp +++ b/detectors/src/local2D/simple_ransac_detection/Model.cpp @@ -5,177 +5,198 @@ * Author: edgar */ -#include <fstream> -#include <iostream> -#include <pugixml.hpp> + #include "od/detectors/local2D/simple_ransac_detection/Model.h" -#include "od/detectors/local2D/simple_ransac_detection/CsvWriter.h" -#include <sstream> -#include <ostream> -#include <boost/algorithm/string.hpp> - -Model::Model() : list_points2d_in_(0), list_points2d_out_(0), list_points3d_in_(0) -{ - n_correspondences_ = 0; -} - -Model::~Model() -{ - // TODO Auto-generated destructor stub -} - -void Model::add_correspondence(const cv::Point2f &point2d, const cv::Point3f &point3d) -{ - list_points2d_in_.push_back(point2d); - list_points3d_in_.push_back(point3d); - n_correspondences_++; -} - -void Model::add_outlier(const cv::Point2f &point2d) -{ - list_points2d_out_.push_back(point2d); -} - -void Model::add_descriptor(const cv::Mat &descriptor) -{ - descriptors_.push_back(descriptor); -} - -void Model::add_keypoint(const cv::KeyPoint &kp) -{ - list_keypoints_.push_back(kp); -} - - -/** Save a CSV file and fill the object mesh */ -void Model::save(const std::string path) -{ - cv::Mat points3dmatrix = cv::Mat(list_points3d_in_); - cv::Mat points2dmatrix = cv::Mat(list_points2d_in_); - //cv::Mat keyPointmatrix = cv::Mat(list_keypoints_); - - cv::FileStorage storage(path, cv::FileStorage::WRITE); - storage << "points_3d" << points3dmatrix; - storage << "points_2d" << points2dmatrix; - storage << "keypoints" << list_keypoints_; - storage << "descriptors" << descriptors_; - - storage.release(); -} - -/** Load a YAML file using OpenCv functions **/ -void Model::load(const std::string path) -{ - cv::Mat points3d_mat; - - cv::FileStorage storage(path, cv::FileStorage::READ); - storage["points_3d"] >> points3d_mat; - storage["descriptors"] >> descriptors_; - - points3d_mat.copyTo(list_points3d_in_); - - std::cout << list_points3d_in_.size() << endl; - std::cout << descriptors_; - storage.release(); - id = path; -} - -void Model::load_new_desc(const std::string path) -{ - f_type = "SIFT"; - - std::fstream infile(path.c_str()); - if (!infile.is_open()) - { - std::cout << "ERRROR opening model file!!"; - return ; - } - int nop, desc_size, point_size; - infile >> nop; - infile >> point_size >> desc_size; +namespace od { + + namespace l2d { - descriptors_ = Mat(nop, desc_size, CV_32FC1); + std::vector<cv::Point2f> Model::getPoints2dIn() const + { + return list_points2d_in_; + } + + std::vector<cv::Point2f> Model::getPoints2dOut() const + { + return list_points2d_out_; + } + std::vector<cv::Point3f> Model::getPoints3d() const + { + return list_points3d_in_; + } - for (int i = 0; i < nop; i++) - { + std::vector<cv::KeyPoint> Model::getKeypoints() const + { + return list_keypoints_; + } - float x, y, z; - infile >> x >> y >> z; - cv::Point3f p3d(x, y, z); - list_points3d_in_.push_back(p3d); + cv::Mat Model::getDescriptors() const + { + return descriptors_; + } - cv::KeyPoint kp; - infile >> kp.pt.x >> kp.pt.y >> kp.octave >> kp.angle >> kp.response >> kp.size; - list_keypoints_.push_back(kp); + int Model::getNumDescriptors() const + { + return descriptors_.rows; + } - for (int j = 0; j < desc_size; j++) + Model::Model() : list_points2d_in_(0), list_points2d_out_(0), list_points3d_in_(0) { - infile >> descriptors_.at<float>(i, j); + n_correspondences_ = 0; } - } - id = path; -} -template <typename T> -string ps ( T thing ) -{ - cout << thing; - ostringstream ss; - ss << thing; - return ss.str(); -} + Model::~Model() + { + // TODO Auto-generated destructor stub + } -void Model::load_new_xml(const std::string path) -{ + void Model::addCorrespondence(const cv::Point2f & point2d, const cv::Point3f & point3d) + { + list_points2d_in_.push_back(point2d); + list_points3d_in_.push_back(point3d); + n_correspondences_++; + } - pugi::xml_document doc; - pugi::xml_parse_result result = doc.load_file(path.c_str()); + void Model::addOutlier(const cv::Point2f & point2d) + { + list_points2d_out_.push_back(point2d); + } - if (!result) - { - std::cout << "ERRROR opening model file!!"; - return ; + void Model::addDescriptor(const cv::Mat & descriptor) + { + descriptors_.push_back(descriptor); + } - } - pugi::xml_node pts_node = doc.child("Model").child("Points"); - - if (pts_node.first_child()) - f_type = pts_node.first_child().attribute("desc_type").as_string(); - else return; - - - for (pugi::xml_node pt_node = pts_node.first_child(); pt_node; pt_node = pt_node.next_sibling()) - { - Mat single_desc = Mat::zeros(1, get_descriptor_size(), CV_32FC1); - stringstream ss; - ss.str(pt_node.attribute("p3d").as_string()); - float x, y, z; - ss >> x >> y >> z; ss.clear(); - cv::Point3f p3d(x, y, z); - list_points3d_in_.push_back(p3d); - - ss.str(pt_node.attribute("p2d_prop").as_string()); - cv::KeyPoint kp; - ss >> kp.pt.x >> kp.pt.y >> kp.octave >> kp.angle >> kp.response >> kp.size; ss.clear(); - list_keypoints_.push_back(kp); - - ss.str(pt_node.attribute("desc").as_string()); - float d; - for(int it = 0; it < get_descriptor_size(); it ++) + void Model::addKeypoint(const cv::KeyPoint & kp) { - ss >> d; - //cout <<it << " " << ss.eof() << " " << d << endl; - single_desc.at<float> (0, it) = d; + list_keypoints_.push_back(kp); } - ss.clear(); - descriptors_.push_back(single_desc); - } - id = path; - //std::cout << list_points3d_in_.size() << endl; - //std::cout << descriptors_; -} + /** Save a CSV file and fill the object mesh */ + void Model::save(const std::string & path) + { + cv::Mat points3dmatrix = cv::Mat(list_points3d_in_); + cv::Mat points2dmatrix = cv::Mat(list_points2d_in_); + //cv::Mat keyPointmatrix = cv::Mat(list_keypoints_); + + cv::FileStorage storage(path, cv::FileStorage::WRITE); + storage << "points_3d" << points3dmatrix; + storage << "points_2d" << points2dmatrix; + storage << "keypoints" << list_keypoints_; + storage << "descriptors" << descriptors_; + + storage.release(); + } + + /** Load a YAML file using OpenCv functions **/ + void Model::load(const std::string & path) + { + cv::Mat points3d_mat; + + cv::FileStorage storage(path, cv::FileStorage::READ); + storage["points_3d"] >> points3d_mat; + storage["descriptors"] >> descriptors_; + + points3d_mat.copyTo(list_points3d_in_); + + std::cout << list_points3d_in_.size() << std::endl; + std::cout << descriptors_; + storage.release(); + id_ = path; + } + + void Model::loadNewDesc(const std::string & path) + { + f_type_ = "SIFT"; + + std::fstream infile(path.c_str()); + if(!infile.is_open()) + { + std::cout << "ERRROR opening model file!!"; + return ; + + } + int nop, desc_size, point_size; + infile >> nop; + infile >> point_size >> desc_size; + + descriptors_ = cv::Mat(nop, desc_size, CV_32FC1); + + for(size_t i = 0; i < nop; ++i) + { + + float x, y, z; + infile >> x >> y >> z; + cv::Point3f p3d(x, y, z); + list_points3d_in_.push_back(p3d); + + cv::KeyPoint kp; + infile >> kp.pt.x >> kp.pt.y >> kp.octave >> kp.angle >> kp.response >> kp.size; + list_keypoints_.push_back(kp); + + for(size_t j = 0; j < desc_size; ++j) + { + infile >> descriptors_.at<float>(i, j); + } + } + id_ = path; + } + + void Model::loadNewXml(const std::string & path) + { + + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load_file(path.c_str()); + + if (!result) + { + std::cout << "ERRROR opening model file!!"; + return ; + + } + pugi::xml_node pts_node = doc.child("Model").child("Points"); + + if (pts_node.first_child()) + f_type_ = pts_node.first_child().attribute("desc_type").as_string(); + else return; + + + for(pugi::xml_node pt_node = pts_node.first_child(); pt_node; pt_node = pt_node.next_sibling()) + { + cv::Mat single_desc = cv::Mat::zeros(1, getDescriptorSize(), CV_32FC1); + std::stringstream ss; + ss.str(pt_node.attribute("p3d").as_string()); + float x, y, z; + ss >> x >> y >> z; ss.clear(); + cv::Point3f p3d(x, y, z); + list_points3d_in_.push_back(p3d); + + ss.str(pt_node.attribute("p2d_prop").as_string()); + cv::KeyPoint kp; + ss >> kp.pt.x >> kp.pt.y >> kp.octave >> kp.angle >> kp.response >> kp.size; ss.clear(); + list_keypoints_.push_back(kp); + + ss.str(pt_node.attribute("desc").as_string()); + float d; + for(size_t it = 0; it < getDescriptorSize(); it ++) + { + ss >> d; + //cout <<it << " " << ss.eof() << " " << d << endl; + single_desc.at<float> (0, it) = d; + } + ss.clear(); + + descriptors_.push_back(single_desc); + } + + id_ = path; + //std::cout << list_points3d_in_.size() << endl; + //std::cout << descriptors_; + } + + } +} \ No newline at end of file diff --git a/detectors/src/local2D/simple_ransac_detection/ModelRegistration.cpp b/detectors/src/local2D/simple_ransac_detection/ModelRegistration.cpp index b1bd9219..cb710f4e 100644 --- a/detectors/src/local2D/simple_ransac_detection/ModelRegistration.cpp +++ b/detectors/src/local2D/simple_ransac_detection/ModelRegistration.cpp @@ -7,29 +7,67 @@ #include "od/detectors/local2D/simple_ransac_detection/ModelRegistration.h" -ModelRegistration::ModelRegistration() -{ - n_registrations_ = 0; - max_registrations_ = 0; -} +namespace od { + + namespace l2d { -ModelRegistration::~ModelRegistration() -{ - // TODO Auto-generated destructor stub -} + void ModelRegistration::setNumMax(int n) + { + max_registrations_ = n; + } + + std::vector<cv::Point2f> ModelRegistration::getPoints2d() const + { + return list_points2d_; + } + + std::vector<cv::Point3f> ModelRegistration::getPoints3d() const + { + return list_points3d_; + } + + int ModelRegistration::getNumMax() const + { + return max_registrations_; + } + + int ModelRegistration::getNumRegist() const + { + return n_registrations_; + } + + bool ModelRegistration::isRegistrable() const + { + return (n_registrations_ < max_registrations_); + } + + ModelRegistration::ModelRegistration() + { + n_registrations_ = 0; + max_registrations_ = 0; + } + + ModelRegistration::~ModelRegistration() + { + // TODO Auto-generated destructor stub + } + + void ModelRegistration::registerPoint(const cv::Point2f & point2d, const cv::Point3f & point3d) + { + // add correspondence at the end of the vector + list_points2d_.push_back(point2d); + list_points3d_.push_back(point3d); + n_registrations_++; + } + + void ModelRegistration::reset() + { + n_registrations_ = 0; + max_registrations_ = 0; + list_points2d_.clear(); + list_points3d_.clear(); + } -void ModelRegistration::registerPoint(const cv::Point2f &point2d, const cv::Point3f &point3d) - { - // add correspondence at the end of the vector - list_points2d_.push_back(point2d); - list_points3d_.push_back(point3d); - n_registrations_++; - } - -void ModelRegistration::reset() -{ - n_registrations_ = 0; - max_registrations_ = 0; - list_points2d_.clear(); - list_points3d_.clear(); + } + } diff --git a/detectors/src/local2D/simple_ransac_detection/PnPProblem.cpp b/detectors/src/local2D/simple_ransac_detection/PnPProblem.cpp index a9a7416b..ba7d5181 100644 --- a/detectors/src/local2D/simple_ransac_detection/PnPProblem.cpp +++ b/detectors/src/local2D/simple_ransac_detection/PnPProblem.cpp @@ -5,332 +5,369 @@ * Author: Edgar Riba */ -#include <iostream> -#include <sstream> - #include "od/detectors/local2D/simple_ransac_detection/PnPProblem.h" -#include "od/detectors/local2D/simple_ransac_detection/Mesh.h" - -#include <opencv2/calib3d/calib3d.hpp> - -/* Functions headers */ -cv::Point3f CROSS(cv::Point3f v1, cv::Point3f v2); -double DOT(cv::Point3f v1, cv::Point3f v2); -cv::Point3f SUB(cv::Point3f v1, cv::Point3f v2); -cv::Point3f get_nearest_3D_point(std::vector<cv::Point3f> &points_list, cv::Point3f origin); - - -/* Functions for Möller–Trumbore intersection algorithm */ - -cv::Point3f CROSS(cv::Point3f v1, cv::Point3f v2) -{ - cv::Point3f tmp_p; - tmp_p.x = v1.y*v2.z - v1.z*v2.y; - tmp_p.y = v1.z*v2.x - v1.x*v2.z; - tmp_p.z = v1.x*v2.y - v1.y*v2.x; - return tmp_p; -} - -double DOT(cv::Point3f v1, cv::Point3f v2) -{ - return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z; -} - -cv::Point3f SUB(cv::Point3f v1, cv::Point3f v2) -{ - cv::Point3f tmp_p; - tmp_p.x = v1.x - v2.x; - tmp_p.y = v1.y - v2.y; - tmp_p.z = v1.z - v2.z; - return tmp_p; -} - -/* End functions for Möller–Trumbore intersection algorithm - * */ - -// Function to get the nearest 3D point to the Ray origin -cv::Point3f get_nearest_3D_point(std::vector<cv::Point3f> &points_list, cv::Point3f origin) -{ - cv::Point3f p1 = points_list[0]; - cv::Point3f p2 = points_list[1]; - - double d1 = std::sqrt( std::pow(p1.x-origin.x, 2) + std::pow(p1.y-origin.y, 2) + std::pow(p1.z-origin.z, 2) ); - double d2 = std::sqrt( std::pow(p2.x-origin.x, 2) + std::pow(p2.y-origin.y, 2) + std::pow(p2.z-origin.z, 2) ); - - if(d1 < d2) - { - return p1; - } - else - { - return p2; - } -} - -// Custom constructor given the intrinsic camera parameters - -PnPProblem::PnPProblem(double const params[], double const dists[]) -{ - _A_matrix = cv::Mat::zeros(3, 3, CV_64FC1); // intrinsic camera parameters - _A_matrix.at<double>(0, 0) = params[0]; // [ fx 0 cx ] - _A_matrix.at<double>(1, 1) = params[1]; // [ 0 fy cy ] - _A_matrix.at<double>(0, 2) = params[2]; // [ 0 0 1 ] - _A_matrix.at<double>(1, 2) = params[3]; - _A_matrix.at<double>(2, 2) = 1; - _dist = cv::Mat::zeros(4, 1, CV_64FC1); - _dist.at<double>(0,0) = dists[0]; _dist.at<double>(1,0) = dists[1]; _dist.at<double>(2,0) = dists[2]; _dist.at<double>(3,0) = dists[3]; - - _R_matrix = cv::Mat::zeros(3, 3, CV_64FC1); // rotation matrix - _t_matrix = cv::Mat::zeros(3, 1, CV_64FC1); // translation matrix - _P_matrix = cv::Mat::zeros(3, 4, CV_64FC1); // rotation-translation matrix - -} - -PnPProblem::PnPProblem(cv::Mat intrin, cv::Mat dist) { - intrin.copyTo(_A_matrix); - dist.copyTo(_dist); - - _R_matrix = cv::Mat::zeros(3, 3, CV_64FC1); // rotation matrix - _t_matrix = cv::Mat::zeros(3, 1, CV_64FC1); // translation matrix - _P_matrix = cv::Mat::zeros(3, 4, CV_64FC1); // rotation-translation matrix -} - - -PnPProblem::~PnPProblem() -{ - // TODO Auto-generated destructor stub -} - -void PnPProblem::set_P_matrix( const cv::Mat &R_matrix, const cv::Mat &t_matrix) -{ - // Rotation-Translation Matrix Definition - _P_matrix.at<double>(0,0) = R_matrix.at<double>(0,0); - _P_matrix.at<double>(0,1) = R_matrix.at<double>(0,1); - _P_matrix.at<double>(0,2) = R_matrix.at<double>(0,2); - _P_matrix.at<double>(1,0) = R_matrix.at<double>(1,0); - _P_matrix.at<double>(1,1) = R_matrix.at<double>(1,1); - _P_matrix.at<double>(1,2) = R_matrix.at<double>(1,2); - _P_matrix.at<double>(2,0) = R_matrix.at<double>(2,0); - _P_matrix.at<double>(2,1) = R_matrix.at<double>(2,1); - _P_matrix.at<double>(2,2) = R_matrix.at<double>(2,2); - - _P_matrix.at<double>(0,3) = t_matrix.at<double>(0); - _P_matrix.at<double>(1,3) = t_matrix.at<double>(1); - _P_matrix.at<double>(2,3) = t_matrix.at<double>(2); - -} - - -// Estimate the pose given a list of 2D/3D correspondences and the method to use -bool PnPProblem::estimatePose( const std::vector<cv::Point3f> &list_points3d, - const std::vector<cv::Point2f> &list_points2d, - int flags) -{ - //cv::Mat distCoeffs = cv::Mat::zeros(4, 1, CV_64FC1); - cv::Mat rvec = cv::Mat::zeros(3, 1, CV_64FC1); - cv::Mat tvec = cv::Mat::zeros(3, 1, CV_64FC1); - - bool useExtrinsicGuess = false; - - // Pose estimation - bool correspondence = cv::solvePnP( list_points3d, list_points2d, _A_matrix, _dist, rvec, tvec, - useExtrinsicGuess, flags); - - // Transforms Rotation Vector to Matrix - Rodrigues(rvec,_R_matrix); - _t_matrix = tvec; - - // Set projection matrix - this->set_P_matrix(_R_matrix, _t_matrix); - - return correspondence; -} - -// Estimate the pose given a list of 2D/3D correspondences with RANSAC and the method to use - -void PnPProblem::estimatePoseRANSAC( const std::vector<cv::Point3f> &list_points3d, // list with model 3D coordinates - const std::vector<cv::Point2f> &list_points2d, // list with scene 2D coordinates - int flags, cv::Mat &inliers, int iterationsCount, // PnP method; inliers container - float reprojectionError, double confidence ) // Ransac parameters -{ - //cv::Mat distCoeffs = cv::Mat::zeros(4, 1, CV_64FC1); // vector of distortion coefficients - cv::Mat rvec = cv::Mat::zeros(3, 1, CV_64FC1); // output rotation vector - cv::Mat tvec = cv::Mat::zeros(3, 1, CV_64FC1); // output translation vector - - bool useExtrinsicGuess = false; // if true the function uses the provided rvec and tvec values as - // initial approximations of the rotation and translation vectors - - cv::solvePnPRansac( list_points3d, list_points2d, _A_matrix, _dist, rvec, tvec, - useExtrinsicGuess, iterationsCount, reprojectionError, confidence, - inliers, flags ); - - rvec.copyTo(_R_vect); - Rodrigues(rvec,_R_matrix); // converts Rotation Vector to Matrix - _t_matrix = tvec; // set translation matrix - - this->set_P_matrix(_R_matrix, _t_matrix); // set rotation-translation matrix - -} - -// Given the mesh, backproject the 3D points to 2D to verify the pose estimation -std::vector<cv::Point2f> PnPProblem::verify_points(Mesh *mesh) -{ - std::vector<cv::Point2f> verified_points_2d; - for( int i = 0; i < mesh->getNumVertices(); i++) - { - cv::Point3f point3d = mesh->getVertex(i); - cv::Point2f point2d = this->backproject3DPoint(point3d); - verified_points_2d.push_back(point2d); - } - return verified_points_2d; -} +namespace od { + + namespace l2d { + /* Functions headers */ -// Backproject a 3D point to 2D using the estimated pose parameters + /* Functions for Möller–Trumbore intersection algorithm */ -cv::Point2f PnPProblem::backproject3DPoint(const cv::Point3f &point3d) -{ - // 3D point vector [x y z 1]' - cv::Mat point3d_vec = cv::Mat(4, 1, CV_64FC1); - point3d_vec.at<double>(0) = point3d.x; - point3d_vec.at<double>(1) = point3d.y; - point3d_vec.at<double>(2) = point3d.z; - point3d_vec.at<double>(3) = 1; + cv::Point3f PnPProblem::CROSS(const cv::Point3f & v1, const cv::Point3f & v2) + { + cv::Point3f tmp_p; + tmp_p.x = v1.y*v2.z - v1.z*v2.y; + tmp_p.y = v1.z*v2.x - v1.x*v2.z; + tmp_p.z = v1.x*v2.y - v1.y*v2.x; + return tmp_p; + } - // 2D point vector [u v 1]' - cv::Mat point2d_vec = cv::Mat(4, 1, CV_64FC1); - point2d_vec = _A_matrix * _P_matrix * point3d_vec; + double PnPProblem::DOT(const cv::Point3f & v1, const cv::Point3f & v2) + { + return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z; + } - // Normalization of [u v]' - cv::Point2f point2d; - point2d.x = (float)(point2d_vec.at<double>(0) / point2d_vec.at<double>(2)); - point2d.y = (float)(point2d_vec.at<double>(1) / point2d_vec.at<double>(2)); + cv::Point3f PnPProblem::SUB(const cv::Point3f & v1, const cv::Point3f & v2) + { + cv::Point3f tmp_p; + tmp_p.x = v1.x - v2.x; + tmp_p.y = v1.y - v2.y; + tmp_p.z = v1.z - v2.z; + return tmp_p; + } + cv::Mat PnPProblem::getAMatrix() const + { + return a_matrix_; + } - return point2d; -} + cv::Mat PnPProblem::getDistCoef() const + { + return dist_; + } -// Back project a 2D point to 3D and returns if it's on the object surface -bool PnPProblem::backproject2DPoint(const Mesh *mesh, const cv::Point2f &point2d, cv::Point3f &point3d) -{ - // Triangles list of the object mesh - std::vector<std::vector<int> > triangles_list = mesh->getTrianglesList(); + cv::Mat PnPProblem::getRMatrix() const + { + return r_matrix_; + } - double lambda = 8; - double u = point2d.x; - double v = point2d.y; + cv::Mat PnPProblem::getRVect() const + { + return r_vect_; + } - // Point in vector form - cv::Mat point2d_vec = cv::Mat::ones(3, 1, CV_64F); // 3x1 - point2d_vec.at<double>(0) = u * lambda; - point2d_vec.at<double>(1) = v * lambda; - point2d_vec.at<double>(2) = lambda; + cv::Mat PnPProblem::getTMatrix() const + { + return t_matrix_; + } - // Point in camera coordinates - cv::Mat X_c = _A_matrix.inv() * point2d_vec ; // 3x1 + cv::Mat PnPProblem::getPMatrix() const + { + return p_matrix_; + } - // Point in world coordinates - cv::Mat X_w = _R_matrix.inv() * ( X_c - _t_matrix ); // 3x1 + void PnPProblem::clearExtrinsics() + { + r_matrix_ = cv::Mat::zeros(3, 3, CV_64FC1); // rotation matrix + t_matrix_ = cv::Mat::zeros(3, 1, CV_64FC1); // translation matrix + p_matrix_ = cv::Mat::zeros(3, 4, CV_64FC1); + } + + /* End functions for Möller–Trumbore intersection algorithm + * */ + + // Function to get the nearest 3D point to the Ray origin + cv::Point3f PnPProblem::getNearest3DPoint(std::vector<cv::Point3f> & points_list, const cv::Point3f & origin) + { + cv::Point3f p1 = points_list[0]; + cv::Point3f p2 = points_list[1]; + + double d1 = std::sqrt( std::pow(p1.x-origin.x, 2) + std::pow(p1.y-origin.y, 2) + std::pow(p1.z-origin.z, 2) ); + double d2 = std::sqrt( std::pow(p2.x-origin.x, 2) + std::pow(p2.y-origin.y, 2) + std::pow(p2.z-origin.z, 2) ); + + if(d1 < d2) + { + return p1; + } + else + { + return p2; + } + } - // Center of projection - cv::Mat C_op = cv::Mat(_R_matrix.inv()).mul(-1) * _t_matrix; // 3x1 + // Custom constructor given the intrinsic camera parameters - // Ray direction vector - cv::Mat ray = X_w - C_op; // 3x1 - ray = ray / cv::norm(ray); // 3x1 + PnPProblem::PnPProblem(double const params[], double const dists[]) + { + a_matrix_ = cv::Mat::zeros(3, 3, CV_64FC1); // intrinsic camera parameters + a_matrix_.at<double>(0, 0) = params[0]; // [ fx 0 cx ] + a_matrix_.at<double>(1, 1) = params[1]; // [ 0 fy cy ] + a_matrix_.at<double>(0, 2) = params[2]; // [ 0 0 1 ] + a_matrix_.at<double>(1, 2) = params[3]; + a_matrix_.at<double>(2, 2) = 1; + dist_ = cv::Mat::zeros(4, 1, CV_64FC1); + dist_.at<double>(0,0) = dists[0]; dist_.at<double>(1,0) = dists[1]; dist_.at<double>(2,0) = dists[2]; dist_.at<double>(3,0) = dists[3]; + + r_matrix_ = cv::Mat::zeros(3, 3, CV_64FC1); // rotation matrix + t_matrix_ = cv::Mat::zeros(3, 1, CV_64FC1); // translation matrix + p_matrix_ = cv::Mat::zeros(3, 4, CV_64FC1); // rotation-translation matrix - // Set up Ray - Ray R((cv::Point3f)C_op, (cv::Point3f)ray); + } - // A vector to store the intersections found - std::vector<cv::Point3f> intersections_list; + PnPProblem::PnPProblem(const cv::Mat & intrin, const cv::Mat & dist) { + intrin.copyTo(a_matrix_); + dist.copyTo(dist_); - // Loop for all the triangles and check the intersection - for (unsigned int i = 0; i < triangles_list.size(); i++) - { - cv::Point3f V0 = mesh->getVertex(triangles_list[i][0]); - cv::Point3f V1 = mesh->getVertex(triangles_list[i][1]); - cv::Point3f V2 = mesh->getVertex(triangles_list[i][2]); + r_matrix_ = cv::Mat::zeros(3, 3, CV_64FC1); // rotation matrix + t_matrix_ = cv::Mat::zeros(3, 1, CV_64FC1); // translation matrix + p_matrix_ = cv::Mat::zeros(3, 4, CV_64FC1); // rotation-translation matrix + } - Triangle T(i, V0, V1, V2); - double out; - if(this->intersect_MollerTrumbore(R, T, &out)) + PnPProblem::~PnPProblem() { - cv::Point3f tmp_pt = R.getP0() + out*R.getP1(); // P = O + t*D - intersections_list.push_back(tmp_pt); + // TODO Auto-generated destructor stub } - } - // If there are intersection, find the nearest one - if (!intersections_list.empty()) - { - point3d = get_nearest_3D_point(intersections_list, R.getP0()); - return true; - } - else - { - return false; - } -} + void PnPProblem::setPMatrix(const cv::Mat & r_matrix, const cv::Mat & t_matrix) + { + // Rotation-Translation Matrix Definition + p_matrix_.at<double>(0,0) = r_matrix.at<double>(0,0); + p_matrix_.at<double>(0,1) = r_matrix.at<double>(0,1); + p_matrix_.at<double>(0,2) = r_matrix.at<double>(0,2); + p_matrix_.at<double>(1,0) = r_matrix.at<double>(1,0); + p_matrix_.at<double>(1,1) = r_matrix.at<double>(1,1); + p_matrix_.at<double>(1,2) = r_matrix.at<double>(1,2); + p_matrix_.at<double>(2,0) = r_matrix.at<double>(2,0); + p_matrix_.at<double>(2,1) = r_matrix.at<double>(2,1); + p_matrix_.at<double>(2,2) = r_matrix.at<double>(2,2); + + p_matrix_.at<double>(0,3) = t_matrix.at<double>(0); + p_matrix_.at<double>(1,3) = t_matrix.at<double>(1); + p_matrix_.at<double>(2,3) = t_matrix.at<double>(2); -// Möller–Trumbore intersection algorithm -bool PnPProblem::intersect_MollerTrumbore(Ray &Ray, Triangle &Triangle, double *out) -{ - const double EPSILON = 0.000001; + } - cv::Point3f e1, e2; - cv::Point3f P, Q, T; - double det, inv_det, u, v; - double t; - cv::Point3f V1 = Triangle.getV0(); // Triangle vertices - cv::Point3f V2 = Triangle.getV1(); - cv::Point3f V3 = Triangle.getV2(); + // Estimate the pose given a list of 2D/3D correspondences and the method to use + bool PnPProblem::estimatePose(const std::vector<cv::Point3f> & list_points3d, + const std::vector<cv::Point2f> & list_points2d, + int flags) + { + //cv::Mat distCoeffs = cv::Mat::zeros(4, 1, CV_64FC1); + cv::Mat rvec = cv::Mat::zeros(3, 1, CV_64FC1); + cv::Mat tvec = cv::Mat::zeros(3, 1, CV_64FC1); - cv::Point3f O = Ray.getP0(); // Ray origin - cv::Point3f D = Ray.getP1(); // Ray direction + bool use_extrinsic_guess = false; - //Find vectors for two edges sharing V1 - e1 = SUB(V2, V1); - e2 = SUB(V3, V1); + // Pose estimation + bool correspondence = cv::solvePnP(list_points3d, list_points2d, a_matrix_, dist_, rvec, tvec, + use_extrinsic_guess, flags); - // Begin calculation determinant - also used to calculate U parameter - P = CROSS(D, e2); + // Transforms Rotation Vector to Matrix + cv::Rodrigues(rvec, r_matrix_); + t_matrix_ = tvec; - // If determinant is near zero, ray lie in plane of triangle - det = DOT(e1, P); + // Set projection matrix + setPMatrix(r_matrix_, t_matrix_); - //NOT CULLING - if(det > -EPSILON && det < EPSILON) return false; - inv_det = 1.f / det; + return correspondence; + } - //calculate distance from V1 to ray origin - T = SUB(O, V1); + // Estimate the pose given a list of 2D/3D correspondences with RANSAC and the method to use - //Calculate u parameter and test bound - u = DOT(T, P) * inv_det; + void PnPProblem::estimatePoseRANSAC( const std::vector<cv::Point3f> & list_points3d, // list with model 3D coordinates + const std::vector<cv::Point2f> & list_points2d, // list with scene 2D coordinates + int flags, const cv::Mat & inliers, int iterations_count, // PnP method; inliers container + float reprojection_error, double confidence) // Ransac parameters + { + //cv::Mat distCoeffs = cv::Mat::zeros(4, 1, CV_64FC1); // vector of distortion coefficients + cv::Mat rvec = cv::Mat::zeros(3, 1, CV_64FC1); // output rotation vector + cv::Mat tvec = cv::Mat::zeros(3, 1, CV_64FC1); // output translation vector - //The intersection lies outside of the triangle - if(u < 0.f || u > 1.f) return false; + bool use_extrinsic_guess = false; // if true the function uses the provided rvec and tvec values as + // initial approximations of the rotation and translation vectors - //Prepare to test v parameter - Q = CROSS(T, e1); + cv::solvePnPRansac(list_points3d, list_points2d, a_matrix_, dist_, rvec, tvec, + use_extrinsic_guess, iterations_count, reprojection_error, confidence, + inliers, flags ); - //Calculate V parameter and test bound - v = DOT(D, Q) * inv_det; + rvec.copyTo(r_vect_); + Rodrigues(rvec, r_matrix_); // converts Rotation Vector to Matrix + t_matrix_ = tvec; // set translation matrix - //The intersection lies outside of the triangle - if(v < 0.f || u + v > 1.f) return false; + setPMatrix(r_matrix_, t_matrix_); // set rotation-translation matrix - t = DOT(e2, Q) * inv_det; + } + + // Given the mesh, backproject the 3D points to 2D to verify the pose estimation + std::vector<cv::Point2f> PnPProblem::verifyPoints(Mesh * mesh) + { + std::vector<cv::Point2f> verified_points_2d; + for(size_t i = 0; i < mesh->getNumVertices(); ++i) + { + cv::Point3f point3d = mesh->getVertex(i); + cv::Point2f point2d = backproject3DPoint(point3d); + verified_points_2d.push_back(point2d); + } + + return verified_points_2d; + } + + + // Backproject a 3D point to 2D using the estimated pose parameters + + cv::Point2f PnPProblem::backproject3DPoint(const cv::Point3f & point3d) + { + // 3D point vector [x y z 1]' + cv::Mat point3d_vec = cv::Mat(4, 1, CV_64FC1); + point3d_vec.at<double>(0) = point3d.x; + point3d_vec.at<double>(1) = point3d.y; + point3d_vec.at<double>(2) = point3d.z; + point3d_vec.at<double>(3) = 1; + + // 2D point vector [u v 1]' + cv::Mat point2d_vec = cv::Mat(4, 1, CV_64FC1); + point2d_vec = a_matrix_ * p_matrix_ * point3d_vec; + + // Normalization of [u v]' + cv::Point2f point2d; + point2d.x = static_cast<float>(point2d_vec.at<double>(0) / point2d_vec.at<double>(2)); + point2d.y = static_cast<float>(point2d_vec.at<double>(1) / point2d_vec.at<double>(2)); + + return point2d; + } + + // Back project a 2D point to 3D and returns if it's on the object surface + bool PnPProblem::backproject2DPoint(const Mesh * mesh, const cv::Point2f & point2d, cv::Point3f & point3d) + { + // Triangles list of the object mesh + std::vector<std::vector<int> > triangles_list = mesh->getTrianglesList(); + + double lambda = 8; + double u = point2d.x; + double v = point2d.y; + + // Point in vector form + cv::Mat point2d_vec = cv::Mat::ones(3, 1, CV_64F); // 3x1 + point2d_vec.at<double>(0) = u * lambda; + point2d_vec.at<double>(1) = v * lambda; + point2d_vec.at<double>(2) = lambda; + + // Point in camera coordinates + cv::Mat x_c = a_matrix_.inv() * point2d_vec ; // 3x1 + + // Point in world coordinates + cv::Mat x_w = r_matrix_.inv() * ( x_c - t_matrix_); // 3x1 + + // Center of projection + cv::Mat c_op = cv::Mat(r_matrix_.inv()).mul(-1) * t_matrix_; // 3x1 + + // Ray direction vector + cv::Mat ray = x_w - c_op; // 3x1 + ray = ray / cv::norm(ray); // 3x1 + + // Set up Ray + Ray r((cv::Point3f)c_op, (cv::Point3f)ray); + + // A vector to store the intersections found + std::vector<cv::Point3f> intersections_list; + + // Loop for all the triangles and check the intersection + for(size_t i = 0; i < triangles_list.size(); ++i) + { + cv::Point3f V0 = mesh->getVertex(triangles_list[i][0]); + cv::Point3f V1 = mesh->getVertex(triangles_list[i][1]); + cv::Point3f V2 = mesh->getVertex(triangles_list[i][2]); + + Triangle t(i, V0, V1, V2); + + double out; + if(intersectMollerTrumbore(r, t, &out)) + { + cv::Point3f tmp_pt = r.getP0() + out*r.getP1(); // P = O + t*D + intersections_list.push_back(tmp_pt); + } + } + + // If there are intersection, find the nearest one + if (!intersections_list.empty()) + { + point3d = getNearest3DPoint(intersections_list, r.getP0()); + return true; + } + else + { + return false; + } + } + + // Möller–Trumbore intersection algorithm + bool PnPProblem::intersectMollerTrumbore(Ray & ray, Triangle & triangle, double * out) + { + const double EPSILON = 0.000001; + + cv::Point3f e1, e2; + cv::Point3f P, Q, T; + double det, inv_det, u, v; + double t; + + cv::Point3f V1 = triangle.getV0(); // Triangle vertices + cv::Point3f V2 = triangle.getV1(); + cv::Point3f V3 = triangle.getV2(); + + cv::Point3f O = ray.getP0(); // Ray origin + cv::Point3f D = ray.getP1(); // Ray direction + + //Find vectors for two edges sharing V1 + e1 = SUB(V2, V1); + e2 = SUB(V3, V1); + + // Begin calculation determinant - also used to calculate U parameter + P = CROSS(D, e2); + + // If determinant is near zero, ray lie in plane of triangle + det = DOT(e1, P); + + //NOT CULLING + if(det > -EPSILON && det < EPSILON) + return false; + + inv_det = 1.f / det; + + //calculate distance from V1 to ray origin + T = SUB(O, V1); + + //Calculate u parameter and test bound + u = DOT(T, P) * inv_det; + + //The intersection lies outside of the triangle + if(u < 0.f || u > 1.f) + return false; + + //Prepare to test v parameter + Q = CROSS(T, e1); + + //Calculate V parameter and test bound + v = DOT(D, Q) * inv_det; + + //The intersection lies outside of the triangle + if(v < 0.f || u + v > 1.f) + return false; + + t = DOT(e2, Q) * inv_det; + + if(t > EPSILON) { //ray intersection + *out = t; + return true; + } + + // No hit, no win + return false; + } - if(t > EPSILON) { //ray intersection - *out = t; - return true; } - // No hit, no win - return false; -} +} \ No newline at end of file diff --git a/detectors/src/local2D/simple_ransac_detection/RobustMatcher.cpp b/detectors/src/local2D/simple_ransac_detection/RobustMatcher.cpp index ca859ee8..d2db03a3 100644 --- a/detectors/src/local2D/simple_ransac_detection/RobustMatcher.cpp +++ b/detectors/src/local2D/simple_ransac_detection/RobustMatcher.cpp @@ -6,323 +6,319 @@ */ #include "od/detectors/local2D/simple_ransac_detection/RobustMatcher.h" -#include "od/detectors/local2D/simple_ransac_detection/Utils.h" -#include "od/detectors/local2D/simple_ransac_detection/Model.h" -#include <time.h> -#include <opencv2/features2d/features2d.hpp> -#include <opencv2/features2d.hpp> -#include <opencv2/xfeatures2d.hpp> -#include <opencv2/ml.hpp> +namespace od { + + namespace l2d { + void convertToUnsignedSiftGPU(const cv::Mat & cv_des, std::vector<unsigned char> & siftgpu_des) + { + int totdes = cv_des.rows * 128; + siftgpu_des.resize(cv_des.rows * 128); -void convertToUnsignedSiftGPU(cv::Mat const &cv_des, vector<unsigned char> &siftgpu_des) -{ - int totdes = cv_des.rows * 128; - siftgpu_des.resize(cv_des.rows * 128); - - int desi = 0; + int desi = 0; - for (int i = 0; i < cv_des.rows; i++){ - for (int j = 0; j < 128; j++, desi++) - siftgpu_des[desi] = (unsigned char)cv_des.at<float>(i,j); - } -} + for (int i = 0; i < cv_des.rows; i++){ + for (int j = 0; j < 128; j++, desi++) + siftgpu_des[desi] = (unsigned char)cv_des.at<float>(i,j); + } + } -void convertToDmatch(int siftgpu_matches[][2], int num_match, vector<cv::DMatch> &cv_matches) -{ - cv_matches.resize(num_match); - for(int i = 0; i < num_match; ++i) - { - //How to get the feature matches: - //SiftGPU::SiftKeypoint & key1 = keys1[match_buf[i][0]]; - //SiftGPU::SiftKeypoint & key2 = keys2[match_buf[i][1]]; - //key1 in the first image matches with key2 in the second image - cv_matches[i].trainIdx = siftgpu_matches[i][0]; //Assigned first at init - cv_matches[i].queryIdx = siftgpu_matches[i][1]; - } -} + void convertToDmatch(int siftgpu_matches[][2], int num_match, std::vector<cv::DMatch> & cv_matches) + { + cv_matches.resize(num_match); + for(size_t i = 0; i < num_match; ++i) + { + //How to get the feature matches: + //SiftGPU::SiftKeypoint & key1 = keys1[match_buf[i][0]]; + //SiftGPU::SiftKeypoint & key2 = keys2[match_buf[i][1]]; + //key1 in the first image matches with key2 in the second image + cv_matches[i].trainIdx = siftgpu_matches[i][0]; //Assigned first at init + cv_matches[i].queryIdx = siftgpu_matches[i][1]; + } + } -void RobustMatcher::instantiateMatcher(Model const &model, bool use_gpu) -{ + void RobustMatcher::instantiateMatcher(const Model & model, bool use_gpu) + { - if (use_gpu_ == true) { + if (use_gpu_ == true) { - //####GPU + //####GPU - //1. for SIFT - matcher_gpu_ = cv::cuda::DescriptorMatcher::createBFMatcher(cv::NORM_L2); - cv::cuda::GpuMat train_descriptors(model.get_descriptors()); - matcher_gpu_->add(std::vector<cv::cuda::GpuMat>(1, train_descriptors)); - cout << "GPU matcher instantiated ..." << endl; + //1. for SIFT + matcher_gpu_ = cv::cuda::DescriptorMatcher::createBFMatcher(cv::NORM_L2); + cv::cuda::GpuMat train_descriptors(model.getDescriptors()); + matcher_gpu_->add(std::vector<cv::cuda::GpuMat>(1, train_descriptors)); + std::cout << "GPU matcher instantiated ..." << std::endl; - } - else - { - matcher_ = cv::makePtr<cv::FlannBasedMatcher>(); - } -} + } + else + { + matcher_ = cv::makePtr<cv::FlannBasedMatcher>(); + } + } -RobustMatcher::RobustMatcher(Model const &model, bool use_gpu, bool use_gpu_match) -{ + RobustMatcher::RobustMatcher(const Model & model, bool use_gpu, bool use_gpu_match) + { - use_gpu_ = use_gpu_match; + use_gpu_ = use_gpu_match; - // ORB is the default feature - ratio_ =0.8f; - detector_ = cv::ORB::create(); - extractor_ = cv::ORB::create(); + // ORB is the default feature + ratio_ =0.8f; + detector_ = cv::ORB::create(); + extractor_ = cv::ORB::create(); - instantiateMatcher(model, use_gpu_match); + instantiateMatcher(model, use_gpu_match); -// // BruteFroce matcher with Norm Hamming is the default matcher -// //matcher_ = cv::makePtr<cv::BFMatcher>((int)cv::NORM_HAMMING, false); -// //matcher_ = cv::makePtr<cv::BFMatcher>((int)cv::NORM_L2, false); -// -// -// //####################TEMPORARY CODE BELOW!!!!!!!!!!!!!!!!!!!!!!!! -// -// cv::Ptr<cv::flann::IndexParams> indexParams = cv::makePtr<cv::flann::LshIndexParams>(6, 12, 1); // instantiate LSH index parameters -// cv::Ptr<cv::flann::SearchParams> searchParams = cv::makePtr<cv::flann::SearchParams>(50); // instantiate flann search parameters -// // instantiate FlannBased matcher -// //Ptr<DescriptorMatcher> matcher = makePtr<FlannBasedMatcher>(indexParams, searchParams); -// //matcher_ = cv::makePtr<cv::FlannBasedMatcher>(indexParams, searchParams); -// matcher_ = cv::makePtr<cv::FlannBasedMatcher>(); + // // BruteFroce matcher with Norm Hamming is the default matcher + // //matcher_ = cv::makePtr<cv::BFMatcher>((int)cv::NORM_HAMMING, false); + // //matcher_ = cv::makePtr<cv::BFMatcher>((int)cv::NORM_L2, false); + // + // + // //####################TEMPORARY CODE BELOW!!!!!!!!!!!!!!!!!!!!!!!! + // + // cv::Ptr<cv::flann::IndexParams> indexParams = cv::makePtr<cv::flann::LshIndexParams>(6, 12, 1); // instantiate LSH index parameters + // cv::Ptr<cv::flann::SearchParams> searchParams = cv::makePtr<cv::flann::SearchParams>(50); // instantiate flann search parameters + // // instantiate FlannBased matcher + // //Ptr<DescriptorMatcher> matcher = makePtr<FlannBasedMatcher>(indexParams, searchParams); + // //matcher_ = cv::makePtr<cv::FlannBasedMatcher>(indexParams, searchParams); + // matcher_ = cv::makePtr<cv::FlannBasedMatcher>(); -} + } -RobustMatcher::~RobustMatcher() -{ - // TODO Auto-generated destructor stub -} + RobustMatcher::~RobustMatcher() + { + // TODO Auto-generated destructor stub + } -void RobustMatcher::computeKeyPoints( const cv::Mat& image, std::vector<cv::KeyPoint>& keypoints) -{ - detector_->detect(image, keypoints); -} + void RobustMatcher::computeKeyPoints(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) + { + detector_->detect(image, keypoints); + } -void RobustMatcher::computeDescriptors( const cv::Mat& image, std::vector<cv::KeyPoint>& keypoints, cv::Mat& descriptors) -{ - extractor_->compute(image, keypoints, descriptors); -} + void RobustMatcher::computeDescriptors(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints, cv::Mat & descriptors) + { + extractor_->compute(image, keypoints, descriptors); + } -int RobustMatcher::ratioTest(std::vector<std::vector<cv::DMatch> > &matches) -{ - int removed = 0; - // for all matches - for ( std::vector<std::vector<cv::DMatch> >::iterator - matchIterator= matches.begin(); matchIterator!= matches.end(); ++matchIterator) - { - // if 2 NN has been identified - if (matchIterator->size() > 1) + int RobustMatcher::ratioTest(std::vector<std::vector<cv::DMatch> > &matches) { - // check distance ratio - if ((*matchIterator)[0].distance / (*matchIterator)[1].distance > ratio_) + int removed = 0; + // for all matches + for(std::vector<std::vector<cv::DMatch> >::iterator + matchIterator= matches.begin(); matchIterator!= matches.end(); ++matchIterator) { - matchIterator->clear(); // remove match - removed++; + // if 2 NN has been identified + if(matchIterator->size() > 1) + { + // check distance ratio + if((*matchIterator)[0].distance / (*matchIterator)[1].distance > ratio_) + { + matchIterator->clear(); // remove match + removed++; + } + } + else + { // does not have 2 neighbours + matchIterator->clear(); // remove match + removed++; + } } + return removed; } - else - { // does not have 2 neighbours - matchIterator->clear(); // remove match - removed++; - } - } - return removed; -} -void get_good_matches(const std::vector<std::vector<cv::DMatch> >& matches, std::vector<cv::DMatch>& goodMatches) -{ - for (std::vector<std::vector<cv::DMatch> >::const_iterator - matchIterator1 = matches.begin(); matchIterator1 != matches.end(); ++matchIterator1) - { - // ignore deleted matches - if (matchIterator1->empty() || matchIterator1->size() < 2) - continue; - if ((*matchIterator1)[0].distance < (*matchIterator1)[1].distance) - goodMatches.push_back((*matchIterator1)[0]); - } + void getGoodMatches(const std::vector<std::vector<cv::DMatch> > & matches, std::vector<cv::DMatch> & goodMatches) + { + for (std::vector<std::vector<cv::DMatch> >::const_iterator + matchIterator1 = matches.begin(); matchIterator1 != matches.end(); ++matchIterator1) + { + // ignore deleted matches + if (matchIterator1->empty() || matchIterator1->size() < 2) + continue; + if ((*matchIterator1)[0].distance < (*matchIterator1)[1].distance) + goodMatches.push_back((*matchIterator1)[0]); + } -} + } -void RobustMatcher::symmetryTest( const std::vector<std::vector<cv::DMatch> >& matches1, - const std::vector<std::vector<cv::DMatch> >& matches2, - std::vector<cv::DMatch>& symMatches ) -{ + void RobustMatcher::symmetryTest(const std::vector<std::vector<cv::DMatch> > & matches1, + const std::vector<std::vector<cv::DMatch> >& matches2, + std::vector<cv::DMatch>& symMatches ) + { - // for all matches image 1 -> image 2 - for (std::vector<std::vector<cv::DMatch> >::const_iterator - matchIterator1 = matches1.begin(); matchIterator1 != matches1.end(); ++matchIterator1) - { + // for all matches image 1 -> image 2 + for(std::vector<std::vector<cv::DMatch> >::const_iterator + matchIterator1 = matches1.begin(); matchIterator1 != matches1.end(); ++matchIterator1) + { + + // ignore deleted matches + if(matchIterator1->empty() || matchIterator1->size() < 2) + continue; + + // for all matches image 2 -> image 1 + for(std::vector<std::vector<cv::DMatch> >::const_iterator + matchIterator2 = matches2.begin(); matchIterator2 != matches2.end(); ++matchIterator2) + { + // ignore deleted matches + if(matchIterator2->empty() || matchIterator2->size() < 2) + continue; + + // Match symmetry test + if((*matchIterator1)[0].queryIdx == + (*matchIterator2)[0].trainIdx && + (*matchIterator2)[0].queryIdx == + (*matchIterator1)[0].trainIdx) + { + // add symmetrical match + symMatches.push_back( + cv::DMatch((*matchIterator1)[0].queryIdx, + (*matchIterator1)[0].trainIdx, + (*matchIterator1)[0].distance)); + break; // next match in image 1 -> image 2 + } + } + } - // ignore deleted matches - if (matchIterator1->empty() || matchIterator1->size() < 2) - continue; + } - // for all matches image 2 -> image 1 - for (std::vector<std::vector<cv::DMatch> >::const_iterator - matchIterator2 = matches2.begin(); matchIterator2 != matches2.end(); ++matchIterator2) + void RobustMatcher::match(const cv::Mat & descriptors_frame, const cv::Mat & descriptors_model, std::vector<cv::DMatch> & good_matches) + { + std::vector<std::vector<cv::DMatch> > matches; + + if (use_gpu_) { - // ignore deleted matches - if (matchIterator2->empty() || matchIterator2->size() < 2) - continue; - - // Match symmetry test - if ((*matchIterator1)[0].queryIdx == - (*matchIterator2)[0].trainIdx && - (*matchIterator2)[0].queryIdx == - (*matchIterator1)[0].trainIdx) + matcher_gpu_->knnMatch(cv::cuda::GpuMat(descriptors_frame), matches, 2); // return 2 nearest neighbours + ratioTest(matches); + for ( std::vector<std::vector<cv::DMatch> >::iterator + matchIterator= matches.begin(); matchIterator!= matches.end(); ++matchIterator) { - // add symmetrical match - symMatches.push_back( - cv::DMatch((*matchIterator1)[0].queryIdx, - (*matchIterator1)[0].trainIdx, - (*matchIterator1)[0].distance)); - break; // next match in image 1 -> image 2 + if (!matchIterator->empty()) good_matches.push_back((*matchIterator)[0]); } } - } - -} - -void RobustMatcher::match(const cv::Mat & descriptors_frame, const cv::Mat &descriptors_model, std::vector<cv::DMatch>& good_matches) -{ - std::vector<std::vector<cv::DMatch> > matches; + else + { + matcher_->knnMatch(descriptors_frame, descriptors_model, matches, 2); // return 2 nearest neighbours + ratioTest(matches); + // 4. Fill good matches container + for ( std::vector<std::vector<cv::DMatch> >::iterator + matchIterator= matches.begin(); matchIterator!= matches.end(); ++matchIterator) + { + if (!matchIterator->empty()) good_matches.push_back((*matchIterator)[0]); + } - if (use_gpu_) - { - matcher_gpu_->knnMatch(cv::cuda::GpuMat(descriptors_frame), matches, 2); // return 2 nearest neighbours - ratioTest(matches); - for ( std::vector<std::vector<cv::DMatch> >::iterator - matchIterator= matches.begin(); matchIterator!= matches.end(); ++matchIterator) - { - if (!matchIterator->empty()) good_matches.push_back((*matchIterator)[0]); + } } - } - else - { - matcher_->knnMatch(descriptors_frame, descriptors_model, matches, 2); // return 2 nearest neighbours - ratioTest(matches); - // 4. Fill good matches container - for ( std::vector<std::vector<cv::DMatch> >::iterator - matchIterator= matches.begin(); matchIterator!= matches.end(); ++matchIterator) + + void RobustMatcher::matchNormalized(cv::Mat & descriptors_frame, cv::Mat & descriptors_model, std::vector<cv::DMatch> & good_matches) { - if (!matchIterator->empty()) good_matches.push_back((*matchIterator)[0]); + od::normL2(descriptors_frame); od::normL2(descriptors_model); + match(descriptors_frame, descriptors_model, good_matches); } - } -} - -void RobustMatcher::matchNormalized(cv::Mat & descriptors_frame, cv::Mat &descriptors_model, std::vector<cv::DMatch>& good_matches) -{ - od::normL2(descriptors_frame); od::normL2(descriptors_model); - match(descriptors_frame, descriptors_model, good_matches); -} - -void RobustMatcher::robustMatch( const cv::Mat& frame, std::vector<cv::DMatch>& good_matches, - std::vector<cv::KeyPoint>& keypoints_frame, const cv::Mat& descriptors_model ) -{ - // 1a. Detection of the ORB features - //this->computeKeyPoints(frame, keypoints_frame); + void RobustMatcher::robustMatch(const cv::Mat & frame, std::vector<cv::DMatch> & good_matches, + std::vector<cv::KeyPoint> & keypoints_frame, const cv::Mat & descriptors_model ) + { + // 1a. Detection of the ORB features + //this->computeKeyPoints(frame, keypoints_frame); - // 1b. Extraction of the ORB descriptors - cv::Mat descriptors_frame; - //this->computeDescriptors(frame, keypoints_frame, descriptors_frame); + // 1b. Extraction of the ORB descriptors + cv::Mat descriptors_frame; + //this->computeDescriptors(frame, keypoints_frame, descriptors_frame); - featureDetector_->computeKeypointsAndDescriptors(frame, descriptors_frame, keypoints_frame); - //cout << "After Restacking: \n" << descriptors_frame << endl; - // 2. Match the two image descriptors - std::vector<std::vector<cv::DMatch> > matches12, matches21; + featureDetector_->computeKeypointsAndDescriptors(frame, descriptors_frame, keypoints_frame); + //cout << "After Restacking: \n" << descriptors_frame << endl; + // 2. Match the two image descriptors + std::vector<std::vector<cv::DMatch> > matches12, matches21; - // 2a. From image 1 to image 2 - matcher_->knnMatch(descriptors_frame, descriptors_model, matches12, 2); // return 2 nearest neighbours + // 2a. From image 1 to image 2 + matcher_->knnMatch(descriptors_frame, descriptors_model, matches12, 2); // return 2 nearest neighbours - // 2b. From image 2 to image 1 - matcher_->knnMatch(descriptors_model, descriptors_frame, matches21, 2); // return 2 nearest neighbours + // 2b. From image 2 to image 1 + matcher_->knnMatch(descriptors_model, descriptors_frame, matches21, 2); // return 2 nearest neighbours - // 3. Remove matches for which NN ratio is > than threshold - // clean image 1 -> image 2 matches - ratioTest(matches12); - // clean image 2 -> image 1 matches - ratioTest(matches21); + // 3. Remove matches for which NN ratio is > than threshold + // clean image 1 -> image 2 matches + ratioTest(matches12); + // clean image 2 -> image 1 matches + ratioTest(matches21); - //get_good_matches(matches12, good_matches); + //get_good_matches(matches12, good_matches); - // 4. Remove non-symmetrical matches - symmetryTest(matches12, matches21, good_matches); + // 4. Remove non-symmetrical matches + symmetryTest(matches12, matches21, good_matches); + } + void RobustMatcher::findFeatureAndMatch(const cv::Mat & frame, std::vector<cv::DMatch> & good_matches, + std::vector<cv::KeyPoint> & keypoints_frame, + const cv::Mat & descriptors_model ) + { + good_matches.clear(); -} + // 1b. Extraction of the ORB descriptors + cv::Mat descriptors_frame; + featureDetector_->computeKeypointsAndDescriptors(frame, descriptors_frame, keypoints_frame); -void RobustMatcher::findFeatureAndMatch( const cv::Mat& frame, std::vector<cv::DMatch>& good_matches, - std::vector<cv::KeyPoint>& keypoints_frame, - const cv::Mat& descriptors_model ) -{ - good_matches.clear(); + std::cout << "df " << descriptors_model.rows << std::endl; + std::cout << "kf" << descriptors_frame.rows << std::endl; - // 1b. Extraction of the ORB descriptors - cv::Mat descriptors_frame; - featureDetector_->computeKeypointsAndDescriptors(frame, descriptors_frame, keypoints_frame); + match(descriptors_frame, descriptors_model, good_matches); - cout << "df " << descriptors_model.rows << endl; - cout << "kf" << descriptors_frame.rows << endl; + } - match(descriptors_frame, descriptors_model, good_matches); -} + void RobustMatcher::fastRobustMatch(const cv::Mat & frame, std::vector<cv::DMatch> & good_matches, + std::vector<cv::KeyPoint> & keypoints_frame, + const cv::Mat & descriptors_model ) + { + good_matches.clear(); + cv::Mat d_f, d_m; + // 1a. Detection of the ORB features + //this->computeKeyPoints(frame, keypoints_frame); -void RobustMatcher::fastRobustMatch( const cv::Mat& frame, std::vector<cv::DMatch>& good_matches, - std::vector<cv::KeyPoint>& keypoints_frame, - const cv::Mat& descriptors_model ) -{ - good_matches.clear(); + // 1b. Extraction of the ORB descriptors + cv::Mat descriptors_frame; + //this->computeDescriptors(frame, keypoints_frame, descriptors_frame); - cv::Mat d_f, d_m; - // 1a. Detection of the ORB features - //this->computeKeyPoints(frame, keypoints_frame); + featureDetector_->computeKeypointsAndDescriptors(frame, descriptors_frame, keypoints_frame); - // 1b. Extraction of the ORB descriptors - cv::Mat descriptors_frame; - //this->computeDescriptors(frame, keypoints_frame, descriptors_frame); + if(descriptors_frame.type() != CV_32F) { + descriptors_frame.convertTo(d_f, CV_32F); + } + else{ + d_f = descriptors_frame; + } + if(descriptors_model.type() != CV_32F) { + descriptors_model.convertTo(d_f, CV_32F); + } + else + { + d_m = descriptors_model; + } - featureDetector_->computeKeypointsAndDescriptors(frame, descriptors_frame, keypoints_frame); + // 2. Match the two image descriptors + std::vector<std::vector<cv::DMatch> > matches; + matcher_->knnMatch(d_f, d_m, matches, 2); - if (descriptors_frame.type() != CV_32F) { - descriptors_frame.convertTo(d_f, CV_32F); - } - else { - d_f = descriptors_frame; - } - if (descriptors_model.type() != CV_32F) { - descriptors_model.convertTo(d_f, CV_32F); - } - else - { - d_m = descriptors_model; - } + // 3. Remove matches for which NN ratio is > than threshold + ratioTest(matches); - // 2. Match the two image descriptors - std::vector<std::vector<cv::DMatch> > matches; - matcher_->knnMatch(d_f, d_m, matches, 2); + // 4. Fill good matches container + for(std::vector<std::vector<cv::DMatch> >::iterator + matchIterator= matches.begin(); matchIterator!= matches.end(); ++matchIterator) + { + if (!matchIterator->empty()) good_matches.push_back((*matchIterator)[0]); + } - // 3. Remove matches for which NN ratio is > than threshold - ratioTest(matches); + } - // 4. Fill good matches container - for ( std::vector<std::vector<cv::DMatch> >::iterator - matchIterator= matches.begin(); matchIterator!= matches.end(); ++matchIterator) - { - if (!matchIterator->empty()) good_matches.push_back((*matchIterator)[0]); } } - diff --git a/detectors/src/local2D/simple_ransac_detection/Utils.cpp b/detectors/src/local2D/simple_ransac_detection/Utils.cpp index ca3277f3..e62e3fbe 100644 --- a/detectors/src/local2D/simple_ransac_detection/Utils.cpp +++ b/detectors/src/local2D/simple_ransac_detection/Utils.cpp @@ -5,337 +5,330 @@ * Author: Edgar Riba */ -#include <iostream> - -#include "od/detectors/local2D/simple_ransac_detection/PnPProblem.h" -#include "od/detectors/local2D/simple_ransac_detection/ModelRegistration.h" #include "od/detectors/local2D/simple_ransac_detection/Utils.h" -#include "od/detectors/local2D/simple_ransac_detection/Model.h" - -#include <opencv2/imgproc/imgproc.hpp> -#include <opencv2/calib3d/calib3d.hpp> - -// For text -int fontFace = cv::FONT_ITALIC; -double fontScale = 0.75; -int thickness_font = 2; - -// For circles -int lineType = 16; -int radius = 1; -double thickness_circ = -1; - - -using namespace std; - - - -void viewImage(cv::Mat image, vector<cv::KeyPoint> keypoints) -{ - cv::Mat outimage = image; - cv::drawKeypoints(image, keypoints, outimage, cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS); - - cv::namedWindow( "Keypoints", cv::WINDOW_NORMAL ); // Create a window for display. - cv::resizeWindow("Keypoints", 800, 600); - - cv::imshow("Keypoints", outimage); - cv::waitKey(0); -} - -// Draw a text with the question point -void drawQuestion(cv::Mat image, cv::Point3f point, cv::Scalar color) -{ - std::string x = IntToString((int)point.x); - std::string y = IntToString((int)point.y); - std::string z = IntToString((int)point.z); - - std::string text = " Where is point (" + x + "," + y + "," + z + ") ?"; - cv::putText(image, text, cv::Point(25,50), fontFace, fontScale, color, thickness_font, 8); -} - -// Draw a text with the number of entered points -void drawText(cv::Mat image, std::string text, cv::Scalar color) -{ - cv::putText(image, text, cv::Point(25,50), fontFace, fontScale, color, thickness_font, 8); -} - -// Draw a text with the number of entered points -void drawText2(cv::Mat image, std::string text, cv::Scalar color) -{ - cv::putText(image, text, cv::Point(25,75), fontFace, fontScale, color, thickness_font, 8); -} - -// Draw a text with the frame ratio -void drawFPS(cv::Mat image, double fps, cv::Scalar color) -{ - std::string fps_str = IntToString((int)fps); - std::string text = fps_str + " FPS"; - cv::putText(image, text, cv::Point(500,50), fontFace, fontScale, color, thickness_font, 8); -} - -// Draw a text with the frame ratio -void drawConfidence(cv::Mat image, double confidence, cv::Scalar color) -{ - std::string conf_str = IntToString((int)confidence); - std::string text = conf_str + " %"; - cv::putText(image, text, cv::Point(500,75), fontFace, fontScale, color, thickness_font, 8); -} - -// Draw a text with the number of entered points -void drawCounter(cv::Mat image, int n, int n_max, cv::Scalar color) -{ - std::string n_str = IntToString(n); - std::string n_max_str = IntToString(n_max); - std::string text = n_str + " of " + n_max_str + " points"; - cv::putText(image, text, cv::Point(500,50), fontFace, fontScale, color, thickness_font, 8); -} - -// Draw the points and the coordinates -void drawPoints(cv::Mat image, std::vector<cv::Point2f> &list_points_2d, std::vector<cv::Point3f> &list_points_3d, cv::Scalar color) -{ - for (unsigned int i = 0; i < list_points_2d.size(); ++i) - { - cv::Point2f point_2d = list_points_2d[i]; - cv::Point3f point_3d = list_points_3d[i]; - - // Draw Selected points - cv::circle(image, point_2d, radius, color, -1, lineType ); - - std::string idx = IntToString(i+1); - std::string x = IntToString((int)point_3d.x); - std::string y = IntToString((int)point_3d.y); - std::string z = IntToString((int)point_3d.z); - std::string text = "P" + idx + " (" + x + "," + y + "," + z +")"; - - point_2d.x = point_2d.x + 10; - point_2d.y = point_2d.y - 10; - cv::putText(image, text, point_2d, fontFace, fontScale*0.5, color, thickness_font, 8); - } -} -// Draw only the 2D points -void draw2DPoints(cv::Mat image, std::vector<cv::Point2f> &list_points, cv::Scalar color) -{ - for( size_t i = 0; i < list_points.size(); i++) - { - cv::Point2f point_2d = list_points[i]; +namespace od { + + namespace l2d { + // For text + int fontFace = cv::FONT_ITALIC; + double fontScale = 0.75; + int thickness_font = 2; + + // For circles + int lineType = 16; + int radius = 1; + double thickness_circ = -1; + + void viewImage(cv::Mat image, std::vector<cv::KeyPoint> keypoints) + { + cv::Mat outimage = image; + cv::drawKeypoints(image, keypoints, outimage, cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS); + + cv::namedWindow( "Keypoints", cv::WINDOW_NORMAL); // Create a window for display. + cv::resizeWindow("Keypoints", 800, 600); + + cv::imshow("Keypoints", outimage); + cv::waitKey(0); + } + + // Draw a text with the question point + void drawQuestion(cv::Mat image, cv::Point3f point, cv::Scalar color) + { + std::string x = std::to_string((int)point.x); + std::string y = std::to_string((int)point.y); + std::string z = std::to_string((int)point.z); + + std::string text = "Where is point (" + x + "," + y + "," + z + ") ?"; + cv::putText(image, text, cv::Point(25,50), fontFace, fontScale, color, thickness_font, 8); + } + + // Draw a text with the number of entered points + void drawText(cv::Mat image, std::string text, cv::Scalar color) + { + cv::putText(image, text, cv::Point(25,50), fontFace, fontScale, color, thickness_font, 8); + } + + // Draw a text with the number of entered points + void drawText2(cv::Mat image, std::string text, cv::Scalar color) + { + cv::putText(image, text, cv::Point(25,75), fontFace, fontScale, color, thickness_font, 8); + } + + // Draw a text with the frame ratio + void drawFPS(cv::Mat image, double fps, cv::Scalar color) + { + std::string fps_str = std::to_string((int)fps); + std::string text = fps_str + " FPS"; + cv::putText(image, text, cv::Point(500,50), fontFace, fontScale, color, thickness_font, 8); + } + + // Draw a text with the frame ratio + void drawConfidence(cv::Mat image, double confidence, cv::Scalar color) + { + std::string conf_str = std::to_string((int)confidence); + std::string text = conf_str + " %"; + cv::putText(image, text, cv::Point(500,75), fontFace, fontScale, color, thickness_font, 8); + } + + // Draw a text with the number of entered points + void drawCounter(cv::Mat image, int n, int n_max, cv::Scalar color) + { + std::string n_str = std::to_string(n); + std::string n_max_str = std::to_string(n_max); + std::string text = n_str + " of " + n_max_str + " points"; + cv::putText(image, text, cv::Point(500,50), fontFace, fontScale, color, thickness_font, 8); + } + + // Draw the points and the coordinates + void drawPoints(cv::Mat image, std::vector<cv::Point2f> & list_points_2d, std::vector<cv::Point3f> & list_points_3d, cv::Scalar color) + { + for (unsigned int i = 0; i < list_points_2d.size(); ++i) + { + cv::Point2f point_2d = list_points_2d[i]; + cv::Point3f point_3d = list_points_3d[i]; + + // Draw Selected points + cv::circle(image, point_2d, radius, color, -1, lineType ); + + std::string idx = std::to_string(i+1); + std::string x = std::to_string((int)point_3d.x); + std::string y = std::to_string((int)point_3d.y); + std::string z = std::to_string((int)point_3d.z); + std::string text = "P" + idx + " (" + x + "," + y + "," + z +")"; + + point_2d.x = point_2d.x + 10; + point_2d.y = point_2d.y - 10; + cv::putText(image, text, point_2d, fontFace, fontScale*0.5, color, thickness_font, 8); + } + } + + // Draw only the 2D points + void draw2DPoints(cv::Mat image, std::vector<cv::Point2f> & list_points, cv::Scalar color) + { + for( size_t i = 0; i < list_points.size(); i++) + { + cv::Point2f point_2d = list_points[i]; + + // Draw Selected points + cv::circle(image, point_2d, radius, color, -1, lineType ); + } + } + + // Draw an arrow into the image + void drawArrow(cv::Mat image, cv::Point2i p, cv::Point2i q, cv::Scalar color, int arrowMagnitude, int thickness, int line_type, int shift) + { + //Draw the principle line + cv::line(image, p, q, color, thickness, line_type, shift); + const double PI = CV_PI; + //compute the angle alpha + double angle = atan2((double)p.y-q.y, (double)p.x-q.x); + //compute the coordinates of the first segment + p.x = (int) ( q.x + arrowMagnitude * cos(angle + PI/4)); + p.y = (int) ( q.y + arrowMagnitude * sin(angle + PI/4)); + //Draw the first segment + cv::line(image, p, q, color, thickness, line_type, shift); + //compute the coordinates of the second segment + p.x = (int) ( q.x + arrowMagnitude * cos(angle - PI/4)); + p.y = (int) ( q.y + arrowMagnitude * sin(angle - PI/4)); + //Draw the second segment + cv::line(image, p, q, color, thickness, line_type, shift); + } + + // Draw the 3D coordinate axes + void draw3DCoordinateAxes(cv::Mat image, const std::vector<cv::Point2f> & list_points2d) + { + cv::Scalar red(0, 0, 255); + cv::Scalar green(0,255,0); + cv::Scalar blue(255,0,0); + cv::Scalar black(0,0,0); + + cv::Point2i origin = list_points2d[0]; + cv::Point2i pointX = list_points2d[1]; + cv::Point2i pointY = list_points2d[2]; + cv::Point2i pointZ = list_points2d[3]; + + drawArrow(image, origin, pointX, red, 9, 2); + drawArrow(image, origin, pointY, blue, 9, 2); + drawArrow(image, origin, pointZ, green, 9, 2); + cv::circle(image, origin, radius/2, black, -1, lineType ); + + } + + // Draw the object mesh + void drawObjectMesh(cv::Mat image, const Mesh * mesh, PnPProblem * pnpProblem, cv::Scalar color) + { + std::vector<std::vector<int> > list_triangles = mesh->getTrianglesList(); + // if (pnpProblem->get_A_matrix().at<float>(0,2) < 300 && list_triangles.size() > 1000) + // skip = 10; + + for(size_t i = 0; i < list_triangles.size(); ++i) + { + std::vector<int> tmp_triangle = list_triangles.at(i); + + cv::Point3f point_3d_0 = mesh->getVertex(tmp_triangle[0]); + cv::Point3f point_3d_1 = mesh->getVertex(tmp_triangle[1]); + cv::Point3f point_3d_2 = mesh->getVertex(tmp_triangle[2]); + + //std::cout << point_3d_0 << std::endl << point_3d_1 << std::endl << point_3d_2 << std::endl << std::endl; + + cv::Point2f point_2d_0 = pnpProblem->backproject3DPoint(point_3d_0); + cv::Point2f point_2d_1 = pnpProblem->backproject3DPoint(point_3d_1); + cv::Point2f point_2d_2 = pnpProblem->backproject3DPoint(point_3d_2); + + cv::line(image, point_2d_0, point_2d_1, color, 1); + cv::line(image, point_2d_1, point_2d_2, color, 1); + cv::line(image, point_2d_2, point_2d_0, color, 1); + } + } + + void drawObjectMesh1(cv::Mat image, const Mesh * mesh, const Model * model, PnPProblem * pnpProblem, cv::Scalar color) + { + std::vector<cv::Point2f> imgpoints; + cv::projectPoints(model->getPoints3d(), pnpProblem->getRVect(), pnpProblem->getTMatrix(), pnpProblem->getAMatrix(), pnpProblem->getDistCoef(), imgpoints); + draw2DPoints(image, imgpoints, color); + + } + + void drawModel(cv::Mat image, const Model * model, PnPProblem * pnpProblem, cv::Scalar color) + { + std::vector<cv::Point2f> imgpoints; + cv::projectPoints(model->getPoints3d(), pnpProblem->getRVect(), pnpProblem->getTMatrix(), pnpProblem->getAMatrix(), pnpProblem->getDistCoef(), imgpoints); + draw2DPoints(image, imgpoints, color); + + } + + void drawModel(cv::Mat image, const Model * model, cv::Mat R_vect, cv::Mat t_mat, cv::Mat A_mat, cv::Mat dist, cv::Scalar color) + { + std::vector<cv::Point2f> imgpoints; + cv::projectPoints(model->getPoints3d(), R_vect, t_mat, A_mat, dist, imgpoints); + draw2DPoints(image, imgpoints, color); + + } + + + // Computes the norm of the translation error + double getTranslationError(const cv::Mat & t_true, const cv::Mat & t) + { + return cv::norm( t_true - t ); + } + + // Computes the norm of the rotation error + double getRotationError(const cv::Mat & R_true, const cv::Mat & R) + { + cv::Mat error_vec, error_mat; + error_mat = R_true * cv::Mat(R.inv()).mul(-1); + cv::Rodrigues(error_mat, error_vec); + + return cv::norm(error_vec); + } + + // Converts a given Rotation Matrix to Euler angles + cv::Mat rot2euler(const cv::Mat & rotationMatrix) + { + cv::Mat euler(3,1,CV_64F); + + double m00 = rotationMatrix.at<double>(0,0); + double m02 = rotationMatrix.at<double>(0,2); + double m10 = rotationMatrix.at<double>(1,0); + double m11 = rotationMatrix.at<double>(1,1); + double m12 = rotationMatrix.at<double>(1,2); + double m20 = rotationMatrix.at<double>(2,0); + double m22 = rotationMatrix.at<double>(2,2); + + double x, y, z; + + // Assuming the angles are in radians. + if (m10 > 0.998) { // singularity at north pole + x = 0; + y = CV_PI/2; + z = atan2(m02,m22); + } + else if (m10 < -0.998) { // singularity at south pole + x = 0; + y = -CV_PI/2; + z = atan2(m02,m22); + } + else + { + x = atan2(-m12,m11); + y = asin(m10); + z = atan2(-m20,m00); + } + + euler.at<double>(0) = x; + euler.at<double>(1) = y; + euler.at<double>(2) = z; + + return euler; + } + + // Converts a given Euler angles to Rotation Matrix + cv::Mat euler2rot(const cv::Mat & euler) + { + cv::Mat rotationMatrix(3,3,CV_64F); + + double x = euler.at<double>(0); + double y = euler.at<double>(1); + double z = euler.at<double>(2); + + // Assuming the angles are in radians. + double ch = cos(z); + double sh = sin(z); + double ca = cos(y); + double sa = sin(y); + double cb = cos(x); + double sb = sin(x); + + double m00, m01, m02, m10, m11, m12, m20, m21, m22; + + m00 = ch * ca; + m01 = sh*sb - ch*sa*cb; + m02 = ch*sa*sb + sh*cb; + m10 = sa; + m11 = ca*cb; + m12 = -ca*sb; + m20 = -sh*ca; + m21 = sh*sa*cb + ch*sb; + m22 = -sh*sa*sb + ch*cb; + + rotationMatrix.at<double>(0,0) = m00; + rotationMatrix.at<double>(0,1) = m01; + rotationMatrix.at<double>(0,2) = m02; + rotationMatrix.at<double>(1,0) = m10; + rotationMatrix.at<double>(1,1) = m11; + rotationMatrix.at<double>(1,2) = m12; + rotationMatrix.at<double>(2,0) = m20; + rotationMatrix.at<double>(2,1) = m21; + rotationMatrix.at<double>(2,2) = m22; + + return rotationMatrix; + } + + // Converts a given string to an integer + int StringToInt (const std::string & Text ) + { + std::istringstream ss(Text); + int result; + return ss >> result ? result : 0; + } + + // Converts a given float to a string + std::string FloatToString (float Number ) + { + std::ostringstream ss; + ss << Number; + return ss.str(); + } + + // Converts a given integer to a string + std::string IntToString (int Number ) + { + std::ostringstream ss; + ss << Number; + return ss.str(); + } - // Draw Selected points - cv::circle(image, point_2d, radius, color, -1, lineType ); - } -} - -// Draw an arrow into the image -void drawArrow(cv::Mat image, cv::Point2i p, cv::Point2i q, cv::Scalar color, int arrowMagnitude, int thickness, int line_type, int shift) -{ - //Draw the principle line - cv::line(image, p, q, color, thickness, line_type, shift); - const double PI = CV_PI; - //compute the angle alpha - double angle = atan2((double)p.y-q.y, (double)p.x-q.x); - //compute the coordinates of the first segment - p.x = (int) ( q.x + arrowMagnitude * cos(angle + PI/4)); - p.y = (int) ( q.y + arrowMagnitude * sin(angle + PI/4)); - //Draw the first segment - cv::line(image, p, q, color, thickness, line_type, shift); - //compute the coordinates of the second segment - p.x = (int) ( q.x + arrowMagnitude * cos(angle - PI/4)); - p.y = (int) ( q.y + arrowMagnitude * sin(angle - PI/4)); - //Draw the second segment - cv::line(image, p, q, color, thickness, line_type, shift); -} - -// Draw the 3D coordinate axes -void draw3DCoordinateAxes(cv::Mat image, const std::vector<cv::Point2f> &list_points2d) -{ - cv::Scalar red(0, 0, 255); - cv::Scalar green(0,255,0); - cv::Scalar blue(255,0,0); - cv::Scalar black(0,0,0); - - cv::Point2i origin = list_points2d[0]; - cv::Point2i pointX = list_points2d[1]; - cv::Point2i pointY = list_points2d[2]; - cv::Point2i pointZ = list_points2d[3]; - - drawArrow(image, origin, pointX, red, 9, 2); - drawArrow(image, origin, pointY, blue, 9, 2); - drawArrow(image, origin, pointZ, green, 9, 2); - cv::circle(image, origin, radius/2, black, -1, lineType ); - -} - -// Draw the object mesh -void drawObjectMesh(cv::Mat image, const Mesh *mesh, PnPProblem *pnpProblem, cv::Scalar color) -{ - std::vector<std::vector<int> > list_triangles = mesh->getTrianglesList(); - int skip = 1; -// if (pnpProblem->get_A_matrix().at<float>(0,2) < 300 && list_triangles.size() > 1000) -// skip = 10; - - for( size_t i = 0; i < list_triangles.size(); i+= skip) - { - std::vector<int> tmp_triangle = list_triangles.at(i); - - cv::Point3f point_3d_0 = mesh->getVertex(tmp_triangle[0]); - cv::Point3f point_3d_1 = mesh->getVertex(tmp_triangle[1]); - cv::Point3f point_3d_2 = mesh->getVertex(tmp_triangle[2]); - - //std::cout << point_3d_0 << std::endl << point_3d_1 << std::endl << point_3d_2 << std::endl << std::endl; - - cv::Point2f point_2d_0 = pnpProblem->backproject3DPoint(point_3d_0); - cv::Point2f point_2d_1 = pnpProblem->backproject3DPoint(point_3d_1); - cv::Point2f point_2d_2 = pnpProblem->backproject3DPoint(point_3d_2); - - cv::line(image, point_2d_0, point_2d_1, color, 1); - cv::line(image, point_2d_1, point_2d_2, color, 1); - cv::line(image, point_2d_2, point_2d_0, color, 1); - } -} - -void drawObjectMesh1(cv::Mat image, const Mesh *mesh, const Model *model, PnPProblem *pnpProblem, cv::Scalar color) -{ - std::vector<cv::Point2f> imgpoints; - cv::projectPoints(model->get_points3d(), pnpProblem->get_R_vect(), pnpProblem->get_t_matrix(), pnpProblem->get_A_matrix(), pnpProblem->get_dist_coef(), imgpoints); - draw2DPoints(image, imgpoints, color); - -} - -void drawModel(cv::Mat image, const Model *model, PnPProblem *pnpProblem, cv::Scalar color) -{ - std::vector<cv::Point2f> imgpoints; - cv::projectPoints(model->get_points3d(), pnpProblem->get_R_vect(), pnpProblem->get_t_matrix(), pnpProblem->get_A_matrix(), pnpProblem->get_dist_coef(), imgpoints); - draw2DPoints(image, imgpoints, color); - -} - -void drawModel(cv::Mat image, const Model *model, cv::Mat R_vect, cv::Mat t_mat, cv::Mat A_mat, cv::Mat dist, cv::Scalar color) -{ - std::vector<cv::Point2f> imgpoints; - cv::projectPoints(model->get_points3d(), R_vect, t_mat, A_mat, dist, imgpoints); - draw2DPoints(image, imgpoints, color); - -} - - -// Computes the norm of the translation error -double get_translation_error(const cv::Mat &t_true, const cv::Mat &t) -{ - return cv::norm( t_true - t ); -} - -// Computes the norm of the rotation error -double get_rotation_error(const cv::Mat &R_true, const cv::Mat &R) -{ - cv::Mat error_vec, error_mat; - error_mat = R_true * cv::Mat(R.inv()).mul(-1); - cv::Rodrigues(error_mat, error_vec); - - return cv::norm(error_vec); -} - -// Converts a given Rotation Matrix to Euler angles -cv::Mat rot2euler(const cv::Mat & rotationMatrix) -{ - cv::Mat euler(3,1,CV_64F); - - double m00 = rotationMatrix.at<double>(0,0); - double m02 = rotationMatrix.at<double>(0,2); - double m10 = rotationMatrix.at<double>(1,0); - double m11 = rotationMatrix.at<double>(1,1); - double m12 = rotationMatrix.at<double>(1,2); - double m20 = rotationMatrix.at<double>(2,0); - double m22 = rotationMatrix.at<double>(2,2); - - double x, y, z; - - // Assuming the angles are in radians. - if (m10 > 0.998) { // singularity at north pole - x = 0; - y = CV_PI/2; - z = atan2(m02,m22); - } - else if (m10 < -0.998) { // singularity at south pole - x = 0; - y = -CV_PI/2; - z = atan2(m02,m22); - } - else - { - x = atan2(-m12,m11); - y = asin(m10); - z = atan2(-m20,m00); } - euler.at<double>(0) = x; - euler.at<double>(1) = y; - euler.at<double>(2) = z; - - return euler; -} - -// Converts a given Euler angles to Rotation Matrix -cv::Mat euler2rot(const cv::Mat & euler) -{ - cv::Mat rotationMatrix(3,3,CV_64F); - - double x = euler.at<double>(0); - double y = euler.at<double>(1); - double z = euler.at<double>(2); - - // Assuming the angles are in radians. - double ch = cos(z); - double sh = sin(z); - double ca = cos(y); - double sa = sin(y); - double cb = cos(x); - double sb = sin(x); - - double m00, m01, m02, m10, m11, m12, m20, m21, m22; - - m00 = ch * ca; - m01 = sh*sb - ch*sa*cb; - m02 = ch*sa*sb + sh*cb; - m10 = sa; - m11 = ca*cb; - m12 = -ca*sb; - m20 = -sh*ca; - m21 = sh*sa*cb + ch*sb; - m22 = -sh*sa*sb + ch*cb; - - rotationMatrix.at<double>(0,0) = m00; - rotationMatrix.at<double>(0,1) = m01; - rotationMatrix.at<double>(0,2) = m02; - rotationMatrix.at<double>(1,0) = m10; - rotationMatrix.at<double>(1,1) = m11; - rotationMatrix.at<double>(1,2) = m12; - rotationMatrix.at<double>(2,0) = m20; - rotationMatrix.at<double>(2,1) = m21; - rotationMatrix.at<double>(2,2) = m22; - - return rotationMatrix; -} - -// Converts a given string to an integer -int StringToInt ( const std::string &Text ) -{ - std::istringstream ss(Text); - int result; - return ss >> result ? result : 0; -} - -// Converts a given float to a string -std::string FloatToString ( float Number ) -{ - std::ostringstream ss; - ss << Number; - return ss.str(); -} - -// Converts a given integer to a string -std::string IntToString ( int Number ) -{ - std::ostringstream ss; - ss << Number; - return ss.str(); -} +} \ No newline at end of file From 694911d5ea31abad930df53afc15dd0adabb2423 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Mon, 6 Jun 2016 16:47:47 +0200 Subject: [PATCH 16/79] fixes the pugixml build error but it would be better to export directly in pugi the include dir --- .../od/detectors/local2D/simple_ransac_detection/Utils.h | 1 - detectors/src/global2D/CMakeLists.txt | 1 - detectors/src/global3D/CMakeLists.txt | 4 ---- detectors/src/misc/CMakeLists.txt | 7 +------ examples/apps/CMakeLists.txt | 3 ++- examples/objectdetector/CMakeLists.txt | 1 + 6 files changed, 4 insertions(+), 13 deletions(-) diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/Utils.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/Utils.h index 73285b73..e6a9e714 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/Utils.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/Utils.h @@ -14,7 +14,6 @@ #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/calib3d/calib3d.hpp> - class Mesh; class PnPProblem; class Model; diff --git a/detectors/src/global2D/CMakeLists.txt b/detectors/src/global2D/CMakeLists.txt index 8c192591..cdb12115 100644 --- a/detectors/src/global2D/CMakeLists.txt +++ b/detectors/src/global2D/CMakeLists.txt @@ -10,7 +10,6 @@ set(build TRUE) if(build) - set(IMPL_INCLUDES "") if(WITH_SVMLIGHT) set(SOURCES diff --git a/detectors/src/global3D/CMakeLists.txt b/detectors/src/global3D/CMakeLists.txt index 70d684b0..e4c8be8c 100644 --- a/detectors/src/global3D/CMakeLists.txt +++ b/detectors/src/global3D/CMakeLists.txt @@ -7,10 +7,6 @@ set(build TRUE) if(build) - set(IMPL_INCLUDES - "${DETECTORS_IMPL_DIR}/od/global3D/detection/ODCADDetector3DGlobal.hpp" - ) - set(SOURCES "ODPointCloudGlobalMatching.cpp" "detection/ODCADDetector3DGlobal.cpp" diff --git a/detectors/src/misc/CMakeLists.txt b/detectors/src/misc/CMakeLists.txt index 6dd69dc1..f5c55aae 100644 --- a/detectors/src/misc/CMakeLists.txt +++ b/detectors/src/misc/CMakeLists.txt @@ -9,12 +9,6 @@ set(build TRUE) if(build) - set(INCLUDES - "${DETECTORS_INCLUDE_DIR}/od/detectors/misc/detection/ODDetectorMultiAlgo.h" - ) - - set(IMPL_INCLUDES "") - set(SOURCES "detection/ODDetectorMultiAlgo.cpp" ) @@ -23,6 +17,7 @@ if(build) include_directories(${COMMON_INCLUDE_DIR}) include_directories(${DETECTORS_IMPL_DIR}) include_directories(${SIMPLE_RANSAC_INCLUDE_DIR}) + include_directories(${CMAKE_3RDPARTY_DIR}/pugixml/src/) OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${SOURCES}) diff --git a/examples/apps/CMakeLists.txt b/examples/apps/CMakeLists.txt index ce070136..f4c19d50 100644 --- a/examples/apps/CMakeLists.txt +++ b/examples/apps/CMakeLists.txt @@ -1,10 +1,11 @@ include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) +include_directories(${CMAKE_3RDPARTY_DIR}/pugixml/src/) +include_directories(${OD_BINARY_DIR}/generated) OD_ADD_EXAMPLE(odcadrecog_test_single_model FILES cadrecog2D/od_test_single_db_single_model.cpp LINK_WITH od_common od_local_image_detector ) -include_directories("${OD_BINARY_DIR}/generated") set(SOURCE_FILES version/opendetection.cpp) add_executable(opendetection ${SOURCE_FILES}) diff --git a/examples/objectdetector/CMakeLists.txt b/examples/objectdetector/CMakeLists.txt index 8bd7a0fe..b65b5d06 100644 --- a/examples/objectdetector/CMakeLists.txt +++ b/examples/objectdetector/CMakeLists.txt @@ -3,6 +3,7 @@ include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) include_directories(${SIMPLE_RANSAC_INCLUDE_DIR}) include_directories(${DETECTORS_IMPL_DIR}) +include_directories(${CMAKE_3RDPARTY_DIR}/pugixml/src/) option(camera_recognition_example "Build the camera recognition example" ON) if(od_image_camera_example) From 84afdf31928ba6e86b029a2f3d674e8bb19ecd26 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Tue, 7 Jun 2016 11:24:26 +0200 Subject: [PATCH 17/79] second restructuring finished. Moved ODCADDetector3DAlgo to template hpp file --- detectors/CMakeLists.txt | 1 - .../detection/ODCADDetector3DGlobal.hpp | 1 - .../misc/detection/ODDetectorMultiAlgo.hpp} | 101 +++++++++----- .../training/ODCADRecogTrainerSnapshotBased.h | 84 +++++++++--- .../misc/detection/ODDetectorMultiAlgo.h | 101 -------------- .../ODCADRecogTrainerSnapshotBased.cpp | 128 +++++------------- detectors/src/misc/CMakeLists.txt | 30 ---- .../gsoc2016_blog_giacomo.md | 18 ++- .../objectdetector/od_multialgo_files.cpp | 2 +- examples/objectdetector/od_multialgo_pc.cpp | 2 +- 10 files changed, 182 insertions(+), 286 deletions(-) rename detectors/{src/misc/detection/ODDetectorMultiAlgo.cpp => impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp} (58%) delete mode 100644 detectors/include/od/detectors/misc/detection/ODDetectorMultiAlgo.h delete mode 100644 detectors/src/misc/CMakeLists.txt diff --git a/detectors/CMakeLists.txt b/detectors/CMakeLists.txt index 92701098..3126c695 100644 --- a/detectors/CMakeLists.txt +++ b/detectors/CMakeLists.txt @@ -9,6 +9,5 @@ set(DETECTORS_IMPL_DIR ${DETECTORS_DIR}/impl PARENT_SCOPE) add_subdirectory("src/local2D") add_subdirectory("src/global2D") add_subdirectory("src/global3D") -add_subdirectory("src/misc") install(DIRECTORY ${DETECTORS_INCLUDE_DIR}/od DESTINATION ${OD_INSTALL_INCLUDE_DIR}) \ No newline at end of file diff --git a/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp b/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp index 041ad4ef..f68fe09e 100644 --- a/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp +++ b/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp @@ -10,7 +10,6 @@ #include <pcl/apps/3d_rec_framework/feature_wrapper/global/vfh_estimator.h> #include <pcl/apps/3d_rec_framework/feature_wrapper/global/esf_estimator.h> #include <pcl/apps/3d_rec_framework/feature_wrapper/global/cvfh_estimator.h> -//#include <pcl/apps/3d_rec_framework/tools/openni_frame_source.h> #include <pcl/apps/3d_rec_framework/utils/metrics.h> #include <pcl/visualization/pcl_visualizer.h> #include <pcl/apps/dominant_plane_segmentation.h> diff --git a/detectors/src/misc/detection/ODDetectorMultiAlgo.cpp b/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp similarity index 58% rename from detectors/src/misc/detection/ODDetectorMultiAlgo.cpp rename to detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp index c32345d9..d90b0f3d 100644 --- a/detectors/src/misc/detection/ODDetectorMultiAlgo.cpp +++ b/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp @@ -26,31 +26,65 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */// // Created by sarkar on 06.08.15. // - -#include "od/detectors/misc/detection/ODDetectorMultiAlgo.h" +#pragma once +#include "od/common/pipeline/ODDetector.h" #include "od/detectors/global2D/detection/ODCascadeDetector.h" #include "od/detectors/global2D/detection/ODHOGDetector.h" #include "od/detectors/global2D/ODFaceRecognizer.h" - #include "od/detectors/local2D/detection/ODCADRecognizer2DLocal.h" - //3D detectors #include "od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp" - -using namespace std; -using namespace od::g2d; -using namespace od::g3d; -using namespace od::l2d; - namespace od { + template<typename PointT> + class ODDetectorMultiAlgo2D : public ODDetector2D + { + public: + ODDetectorMultiAlgo2D(const std::string & training_data_location_) : ODDetector2D(training_data_location_){} + + ODDetections * detect(ODSceneImage * scene) ; + ODDetections2D * detectOmni(ODSceneImage * scene); + + ODDetections * detect(ODScenePointCloud<PointT> * scene); + ODDetections3D * detectOmni(ODScenePointCloud<PointT> * scene); + + + void init(); + + private: + + std::vector<ODDetector2D *> detectors_2d_; + std::vector<ODDetector3D<PointT> *> detectors_3d_; + }; + template<typename PointT> + class ODDetectorMultiAlgo : public ODDetector + { + + public: + ODDetectorMultiAlgo(const std::string & training_data_location_) : ODDetector(training_data_location_){} + + ODDetections * detect(ODSceneImage * scene) ; + ODDetections2D * detectOmni(ODSceneImage * scene); + + ODDetections * detect(ODScenePointCloud<PointT> * scene); + ODDetections3D * detectOmni(ODScenePointCloud<PointT> * scene); + + void init(); + + private: + std::vector<ODDetector2D *> detectors_2d_; + std::vector<ODDetector3D<PointT> *> detectors_3d_; + }; + + //BASED ON 2D SCENE - ODDetections *ODDetectorMultiAlgo2D::detect(ODSceneImage *scene) + template<typename PointT> + ODDetections * ODDetectorMultiAlgo2D<PointT>::detect(ODSceneImage * scene) { ODDetections * detections_all = new ODDetections; - for (int i = 0; i < detectors_2d_.size(); i++) + for (size_t i = 0; i < detectors_2d_.size(); ++i) { ODDetections * detections_individual = detectors_2d_[i]->detect(scene); detections_all->append(detections_individual); @@ -59,10 +93,11 @@ namespace od return detections_all; } - ODDetections2D *ODDetectorMultiAlgo2D::detectOmni(ODSceneImage *scene) + template<typename PointT> + ODDetections2D * ODDetectorMultiAlgo2D<PointT>::detectOmni(ODSceneImage * scene) { ODDetections2D * detections_all = new ODDetections2D; - for (int i = 0; i < detectors_2d_.size(); i++) + for (size_t i = 0; i < detectors_2d_.size(); ++i) { ODDetections2D * detections_individual = detectors_2d_[i]->detectOmni(scene); detections_all->append(detections_individual); @@ -71,46 +106,38 @@ namespace od return detections_all; } - - - - - - void ODDetectorMultiAlgo2D::init() + template<typename PointT> + void ODDetectorMultiAlgo2D<PointT>::init() { //make a list of different algorithms //vector<ODDetector *> detectors = {new ODCascadeDetector(trained_data_location_), new ODHOGDetector(trained_data_location_), new ODCADRecognizer2DLocal(trained_data_location_)}; - detectors_2d_.push_back(new ODCascadeDetector(trained_data_location_)); - detectors_2d_.push_back(new ODHOGDetector(trained_data_location_)); + detectors_2d_.push_back(new g2d::ODCascadeDetector(trained_data_location_)); + detectors_2d_.push_back(new g2d::ODHOGDetector(trained_data_location_)); // detectors.push_back(new ODCADRecognizer2DLocal(trained_data_location_)); - for (int i = 0; i < detectors_2d_.size(); i++) + for(size_t i = 0; i < detectors_2d_.size(); ++i) { detectors_2d_[i]->init(); } } - - - /////############BASED ON 3D SCENE##################### - - void ODDetectorMultiAlgo::init() + template<typename PointT> + void ODDetectorMultiAlgo<PointT>::init() { //3D - detectors_3d_.push_back(new ODCADDetector3DGlobal<PointT>(trained_data_location_, training_input_location_)); - - for (int i = 0; i < detectors_3d_.size(); i++) + detectors_3d_.push_back(new g3d::ODCADDetector3DGlobal<PointT>(trained_data_location_, training_input_location_)); + for(size_t i = 0; i < detectors_3d_.size(); ++i) { detectors_3d_[i]->init(); } } - - ODDetections* ODDetectorMultiAlgo::detect(ODScenePointCloud<PointT> *scene) + template<typename PointT> + ODDetections * ODDetectorMultiAlgo<PointT>::detect(ODScenePointCloud<PointT> * scene) { ODDetections * detections_all = new ODDetections; - for (int i = 0; i < detectors_3d_.size(); i++) + for(size_t i = 0; i < detectors_3d_.size(); ++i) { ODDetections * detections_individual = detectors_3d_[i]->detect(scene); detections_all->append(detections_individual); @@ -119,10 +146,11 @@ namespace od return detections_all; } - ODDetections3D* ODDetectorMultiAlgo::detectOmni(ODScenePointCloud<PointT> *scene) + template<typename PointT> + ODDetections3D * ODDetectorMultiAlgo<PointT>::detectOmni(ODScenePointCloud<PointT> * scene) { ODDetections3D * detections_all = new ODDetections3D; - for (int i = 0; i < detectors_3d_.size(); i++) + for(size_t i = 0; i < detectors_3d_.size(); i++) { ODDetections3D * detections_individual = detectors_3d_[i]->detectOmni(scene); detections_all->append(detections_individual); @@ -130,4 +158,5 @@ namespace od return detections_all; } + } \ No newline at end of file diff --git a/detectors/include/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h b/detectors/include/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h index 46f86e43..a4f65b17 100644 --- a/detectors/include/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h +++ b/detectors/include/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h @@ -27,23 +27,74 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Created by sarkar on 17.03.15. // - -#ifndef _SNAPSHOT_SNAP_VTK_H_ -#define _SNAPSHOT_SNAP_VTK_H_ - - +#pragma once #include "od/common/pipeline/ODTrainer.h" #include "od/common/utils/utils.h" #include "od/detectors/local2D/ODImageLocalMatching.h" +#include <string> +#include <stdlib.h> + +#include <vtkSmartPointer.h> +#include <vtkSphereSource.h> +#include <vtkPolyData.h> +#include <vtkPolyDataMapper.h> +#include <vtkActor.h> +#include <vtkCamera.h> +#include <vtkMatrix4x4.h> +#include <vtkRendererCollection.h> +#include <vtkCommand.h> +#include <vtkRenderer.h> +#include <vtkRenderWindow.h> +#include <vtkRenderWindowInteractor.h> +#include <vtkOBJReader.h> +#include <vtkUnstructuredGrid.h> +#include <vtkCell.h> +#include <vtkCellArray.h> +#include <vtkIdList.h> +#include <vtkUnsignedCharArray.h> +#include <vtkPointData.h> +#include <string> +#include <vtkRendererCollection.h> +#include <vtkCellArray.h> +#include <vtkInteractorStyleTrackballCamera.h> +#include <vtkObjectFactory.h> +#include <vtkPlaneSource.h> +#include <vtkPoints.h> +#include <vtkPolyData.h> +#include <vtkPolyDataMapper.h> +#include <vtkPropPicker.h> +#include <vtkPointPicker.h> + +#include <vtkImageReader2Factory.h> +#include <vtkImageReader.h> +#include <vtkTexture.h> +#include <vtkAxesActor.h> +#include <vtkWindowToImageFilter.h> +#include <vtkPNGWriter.h> + +#include <sstream> +#include <opencv2/core/types.hpp> +#include <map> + +#include <boost/filesystem.hpp> + +//opencv +#include <opencv2/imgcodecs.hpp> +#include <opencv2/core/core.hpp> +#include <opencv2/core/utility.hpp> +#include <opencv2/highgui/highgui.hpp> +#include <opencv2/imgproc/imgproc.hpp> +#include <opencv2/calib3d/calib3d.hpp> +#include <opencv2/video/tracking.hpp> +#include <opencv2/xfeatures2d.hpp> +#include <vtkJPEGWriter.h> + +#include "pugixml.hpp" #define VIEW_ANGLE 30 #define NO_SNAPSHOTS 30 - - - - namespace od { namespace l2d @@ -58,17 +109,16 @@ namespace od { public: - ODCADRecogTrainerSnapshotBased(std::string const &training_input_location_ = "", std::string const &training_data_location_ = "") : ODImageLocalMatchingTrainer( - training_input_location_, training_data_location_) - { } - int train(); + ODCADRecogTrainerSnapshotBased(const std::string & training_input_location_ = "", const std::string & training_data_location_ = "") : + ODImageLocalMatchingTrainer(training_input_location_, training_data_location_){} + int train(); void init() {} - - void trainSingleModel(std::string objname); + void trainSingleModel(const std::string & objname); protected: + int no_ring_; float view_angle_; int no_snapshot_per_ring_; @@ -78,7 +128,3 @@ namespace od } } - - -#endif //_SNAPSHOT_SNAP_VTK_H_ - diff --git a/detectors/include/od/detectors/misc/detection/ODDetectorMultiAlgo.h b/detectors/include/od/detectors/misc/detection/ODDetectorMultiAlgo.h deleted file mode 100644 index 361fb60e..00000000 --- a/detectors/include/od/detectors/misc/detection/ODDetectorMultiAlgo.h +++ /dev/null @@ -1,101 +0,0 @@ -/* -Copyright (c) 2015, Kripasindhu Sarkar -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions 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. - * Neither the name of the copyright holder(s) nor the - names of its contributors may 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 COPYRIGHT HOLDER 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. -*/// -// Created by sarkar on 06.08.15. -// - -#ifndef OPENDETECTION_ODDETECTORMULTIALGO_H -#define OPENDETECTION_ODDETECTORMULTIALGO_H - -#include "od/common/pipeline/ODDetector.h" -#include "od/common/pipeline/ODDetection.h" - -namespace od -{ - - /** \brief Class for running multiple Detection algorithms (with default parameters) on the same scene. - * - * Using this class one can do object detection/recognition using multiple algorithms and provide outcome of detections (eg. people detected by HOG, face detected by Cascade, bottle detected PnPRansac in the same image). - * - * \author Kripasindhu Sarkar - * - */ - class ODDetectorMultiAlgo2D : public ODDetector2D - { - public: - ODDetectorMultiAlgo2D(std::string const &training_data_location_) : ODDetector2D(training_data_location_) - { } - - - typedef pcl::PointXYZRGBA PointT; - - - - ODDetections *detect(ODSceneImage *scene) ; - ODDetections2D *detectOmni(ODSceneImage *scene); - - ODDetections* detect(ODScenePointCloud<PointT> *scene); - ODDetections3D* detectOmni(ODScenePointCloud<PointT> *scene); - - - void init(); - - private: - std::vector<ODDetector2D *> detectors_2d_; - std::vector<ODDetector3D<PointT> *> detectors_3d_; - }; - - class ODDetectorMultiAlgo : public ODDetector - { - public: - ODDetectorMultiAlgo(std::string const &training_data_location_) : ODDetector(training_data_location_) - { } - - - typedef pcl::PointXYZRGBA PointT; - - - - ODDetections *detect(ODSceneImage *scene) ; - ODDetections2D *detectOmni(ODSceneImage *scene); - - ODDetections* detect(ODScenePointCloud<PointT> *scene); - ODDetections3D* detectOmni(ODScenePointCloud<PointT> *scene); - - - void init(); - - private: - std::vector<ODDetector2D *> detectors_2d_; - std::vector<ODDetector3D<PointT> *> detectors_3d_; - }; - - /** \example objectdetector/od_multialgo_files.cpp - * \example objectdetector/od_multialgo_pc.cpp - */ - -} -#endif //OPENDETECTION_ODDETECTORMULTIALGO_H diff --git a/detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp b/detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp index d724990a..55ed79c9 100644 --- a/detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp +++ b/detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp @@ -26,69 +26,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h" -#include <string> -#include <stdlib.h> - -#include <vtkSmartPointer.h> -#include <vtkSphereSource.h> -#include <vtkPolyData.h> -#include <vtkPolyDataMapper.h> -#include <vtkActor.h> -#include <vtkCamera.h> -#include <vtkMatrix4x4.h> -#include <vtkRendererCollection.h> -#include <vtkCommand.h> -#include <vtkRenderer.h> -#include <vtkRenderWindow.h> -#include <vtkRenderWindowInteractor.h> -#include <vtkOBJReader.h> -#include <vtkUnstructuredGrid.h> -#include <vtkCell.h> -#include <vtkCellArray.h> -#include <vtkIdList.h> -#include <vtkUnsignedCharArray.h> -#include <vtkPointData.h> -#include <string> -#include <vtkRendererCollection.h> -#include <vtkCellArray.h> -#include <vtkInteractorStyleTrackballCamera.h> -#include <vtkObjectFactory.h> -#include <vtkPlaneSource.h> -#include <vtkPoints.h> -#include <vtkPolyData.h> -#include <vtkPolyDataMapper.h> -#include <vtkPropPicker.h> -#include <vtkPointPicker.h> - -#include <vtkImageReader2Factory.h> -#include <vtkImageReader.h> -#include <vtkTexture.h> -#include <vtkAxesActor.h> -#include <vtkWindowToImageFilter.h> -#include <vtkPNGWriter.h> - -#include <sstream> -#include <opencv2/core/types.hpp> -#include <map> - -#include <boost/filesystem.hpp> - -//opencv -#include <opencv2/imgcodecs.hpp> -#include <opencv2/core/core.hpp> -#include <opencv2/core/utility.hpp> -#include <opencv2/highgui/highgui.hpp> -#include <opencv2/imgproc/imgproc.hpp> -#include <opencv2/calib3d/calib3d.hpp> -#include <opencv2/video/tracking.hpp> -#include <opencv2/xfeatures2d.hpp> -#include <vtkJPEGWriter.h> - -#include "pugixml.hpp" - - -using namespace std; - namespace od { namespace l2d @@ -97,18 +34,18 @@ namespace od class vtkTimerCallbackSnapshot : public vtkCommand { public: - static vtkTimerCallbackSnapshot *New() + static vtkTimerCallbackSnapshot * New() { - vtkTimerCallbackSnapshot *cb = new vtkTimerCallbackSnapshot; + vtkTimerCallbackSnapshot * cb = new vtkTimerCallbackSnapshot; cb->snap_count = 0; cb->snap_mode = true; cb->feature_type = "SIFT"; return cb; } - virtual void Execute(vtkObject *caller, unsigned long eventId, void * vtkNotUsed(callData)) + virtual void Execute(vtkObject * caller, unsigned long eventId, void * vtkNotUsed(callData)) { - vtkRenderWindowInteractor *iren = vtkRenderWindowInteractor::SafeDownCast(caller); + vtkRenderWindowInteractor * iren = vtkRenderWindowInteractor::SafeDownCast(caller); if(!this->snap_mode) return; @@ -125,7 +62,7 @@ namespace od //after getting all snaps, write everything together cout << "Processing finished... Writing the final descriptors" << endl; - string filename = boost::filesystem::path(input_file).filename().replace_extension(feature_type + "." + output_extension).c_str(); + std::string filename = boost::filesystem::path(input_file).filename().replace_extension(feature_type + "." + output_extension).c_str(); fileutils::createTrainingDir(output_dir); write_pairs(pairs_3d_2d, common_descriptors, output_dir + "/" + filename); @@ -150,13 +87,13 @@ namespace od int snap_count; public: - string takeSnapshot(vtkRenderWindow *renderWindow, int snap_no); + std::string takeSnapshot(vtkRenderWindow *renderWindow, int snap_no); - void write_pairs(vector<pair<cv::Point3f, cv::KeyPoint> > pairs, cv::Mat descriptors, string filename); + void write_pairs(std::vector<std::pair<cv::Point3f, cv::KeyPoint> > pairs, cv::Mat descriptors, std::string filename); - void write_pairs_xml(vector<pair<cv::Point3f, cv::KeyPoint> > pairs, cv::Mat descriptors, string filename); + void write_pairs_xml(std::vector<std::pair<cv::Point3f, cv::KeyPoint> > pairs, cv::Mat descriptors, std::string filename); - void process_image(string imgname, vtkRenderer *ren, vtkActor *actor, int ino); + void process_image(std::string imgname, vtkRenderer *ren, vtkActor *actor, int ino); void process(vtkRenderWindowInteractor *iren, vtkActor *actor, vtkRenderer *renderer, int ino); @@ -165,15 +102,15 @@ namespace od struct fcomp3d_euclidian { - bool operator()(const cv::Point3f &lhs, const cv::Point3f &rhs) const + bool operator()(const cv::Point3f & lhs, const cv::Point3f & rhs) const { return lhs.x < rhs.x; } }; - vector<pair<cv::Point3f, cv::KeyPoint> > pairs_3d_2d; + std::vector<std::pair<cv::Point3f, cv::KeyPoint> > pairs_3d_2d; cv::Mat common_descriptors; - map<cv::Point3f, cv::KeyPoint, fcomp3d_euclidian> map_3d_2d; - string feature_type; - string input_file, input_dir, output_dir, output_extension; + std::map<cv::Point3f, cv::KeyPoint, fcomp3d_euclidian> map_3d_2d; + std::string feature_type; + std::string input_file, input_dir, output_dir, output_extension; vtkActor * actor; @@ -194,7 +131,7 @@ namespace od //for each models in the train_input_directory, train them and put them on the training_directory - for(size_t i = 0; i < files.size(); i++) + for(size_t i = 0; i < files.size(); ++i) { trainSingleModel(training_input_location_ + "/" + files[i]); } @@ -202,7 +139,7 @@ namespace od return 1; } - void ODCADRecogTrainerSnapshotBased::trainSingleModel(std::string objname) + void ODCADRecogTrainerSnapshotBased::trainSingleModel(const std::string & objname) { ///////////setup object @@ -220,7 +157,7 @@ namespace od //texture // Read texture file - string texfilename = getTexfileinObj(objname); + std::string texfilename = getTexfileinObj(objname); vtkSmartPointer<vtkImageReader2Factory> readerFactory = vtkSmartPointer<vtkImageReader2Factory>::New(); vtkImageReader2 *imageReader = readerFactory->CreateImageReader2(texfilename.c_str()); imageReader->SetFileName(texfilename.c_str()); @@ -294,7 +231,7 @@ namespace od vtkSmartPointer<vtkJPEGWriter> writer = vtkSmartPointer<vtkJPEGWriter>::New(); std::string filename; - filename = input_dir + "/" + string("snapshot") + std::to_string(snap_no) + ".jpg"; + filename = input_dir + "/" + std::string("snapshot") + std::to_string(snap_no) + ".jpg"; writer->SetFileName(filename.c_str()); writer->SetInputConnection(windowToImageFilter->GetOutputPort()); @@ -304,10 +241,10 @@ namespace od } - void vtkTimerCallbackSnapshot::write_pairs(vector<pair<cv::Point3f, cv::KeyPoint> > pairs, cv::Mat descriptors, - string filename) + void vtkTimerCallbackSnapshot::write_pairs(std::vector<std::pair<cv::Point3f, cv::KeyPoint> > pairs, cv::Mat descriptors, + std::string filename) { - ofstream fout; + std::ofstream fout; fout.open(filename.c_str()); //headers @@ -326,8 +263,8 @@ namespace od fout.close(); } - void vtkTimerCallbackSnapshot::write_pairs_xml(vector<pair<cv::Point3f, cv::KeyPoint> > pairs, cv::Mat descriptors, - string filename) + void vtkTimerCallbackSnapshot::write_pairs_xml(std::vector<std::pair<cv::Point3f, cv::KeyPoint> > pairs, cv::Mat descriptors, + std::string filename) { pugi::xml_document doc; @@ -340,7 +277,7 @@ namespace od { pugi::xml_node p_node = points_node.append_child("Point"); - ostringstream ss; + std::ostringstream ss; ss << pairs[i].first.x << " " << pairs[i].first.y << " " << pairs[i].first.z; p_node.append_attribute("p3d") = ss.str().c_str(); p_node.append_attribute("nviews") = "1"; @@ -369,25 +306,26 @@ namespace od bool valid_3D_point(double *pt) { - if((pt[0] == 0) && (pt[1] == 0) && (pt[2] == 0)) return false; + if((pt[0] == 0) && (pt[1] == 0) && (pt[2] == 0)) + return false; return true; } //1. find interesting 2d points, 2. find their corresponding 3d points corresponding to the transformation defined by the "actor" //3. transform the 3d points to the original by using the inverse of the "actor" - void vtkTimerCallbackSnapshot::process_image(string imgname, vtkRenderer *ren, vtkActor *actor, int ino) + void vtkTimerCallbackSnapshot::process_image(std::string imgname, vtkRenderer *ren, vtkActor *actor, int ino) { //1. find interesting 2d location cv::Mat img = cv::imread(imgname); - vector<cv::KeyPoint> kpts; + std::vector<cv::KeyPoint> kpts; cv::Mat descriptors_local, descriptors_local_good; cv::Ptr<cv::FeatureDetector> fdetector = cv::xfeatures2d::SIFT::create(); fdetector->detect(img, kpts); fdetector->compute(img, kpts, descriptors_local); //2. find the corresponding 3d points for each 2d location - vector<pair<cv::Point3f, cv::KeyPoint> > pairs_local; - for(int i = 0; i < kpts.size(); i++) + std::vector<std::pair<cv::Point3f, cv::KeyPoint> > pairs_local; + for(size_t i = 0; i < kpts.size(); ++i) { //find 2d vtkSmartPointer<vtkPropPicker> picker = vtkSmartPointer<vtkPropPicker>::New(); @@ -406,7 +344,7 @@ namespace od if(valid_3D_point(pos)) { - pairs_local.push_back(make_pair(cv::Point3f(pos_model[0], pos_model[1], pos_model[2]), kpts[i])); + pairs_local.push_back(std::make_pair(cv::Point3f(pos_model[0], pos_model[1], pos_model[2]), kpts[i])); descriptors_local_good.push_back(descriptors_local.row(i)); //updating extra globals! @@ -420,7 +358,7 @@ namespace od //writing a local set write_pairs(pairs_local, descriptors_local_good, input_dir + "/" + "local" + std::to_string(ino) + ".pairs"); write_pairs_xml(pairs_local, descriptors_local_good, input_dir + "/" + "local" + std::to_string(ino) + ".xml"); - cout << "Processed view " << ino << endl; + std::cout << "Processed view " << ino << std::endl; } void vtkTimerCallbackSnapshot::process(vtkRenderWindowInteractor *iren, vtkActor *actor, vtkRenderer *renderer, int ino) @@ -429,7 +367,7 @@ namespace od vtkRenderWindow *win = iren->GetRenderWindow(); //snap - string snapname = takeSnapshot(win, ino); + std::string snapname = takeSnapshot(win, ino); //save process_image(snapname, renderer, actor, ino); diff --git a/detectors/src/misc/CMakeLists.txt b/detectors/src/misc/CMakeLists.txt deleted file mode 100644 index f5c55aae..00000000 --- a/detectors/src/misc/CMakeLists.txt +++ /dev/null @@ -1,30 +0,0 @@ -set(SUBSYS_NAME miscellaneous_detector) -set(LIB_NAME od_${SUBSYS_NAME}) -set(SUBSYS_DESC "global feature based detection in 2D images") -set(SUBSYS_DEPS od_common od_global_image_detector od_local_image_detector ${OpenCV_LIBS}) - -set(build TRUE) -#PCL_SUBSYS_OPTION(build "${SUBSYS_NAME}" "${SUBSYS_DESC}" ON) -#PCL_SUBSYS_DEPEND(build "${SUBSYS_NAME}" DEPS ${SUBSYS_DEPS}) - -if(build) - - set(SOURCES - "detection/ODDetectorMultiAlgo.cpp" - ) - - include_directories(${DETECTORS_INCLUDE_DIR}) - include_directories(${COMMON_INCLUDE_DIR}) - include_directories(${DETECTORS_IMPL_DIR}) - include_directories(${SIMPLE_RANSAC_INCLUDE_DIR}) - include_directories(${CMAKE_3RDPARTY_DIR}/pugixml/src/) - - OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${SOURCES}) - - if(SUBSYS_DEPS) - target_link_libraries("${LIB_NAME}" ${SUBSYS_DEPS}) - endif(SUBSYS_DEPS) - - #PCL_MAKE_PKGCONFIG("${LIB_NAME}" "${SUBSYS_NAME}" "${SUBSYS_DESC}" "${SUBSYS_DEPS}" "" "" "" "") - -endif(build) \ No newline at end of file diff --git a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md index 15e30de0..6a349810 100644 --- a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md +++ b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md @@ -137,7 +137,7 @@ since you don't have to compile headers files.It is enough to specify the includ ##Code refactoring 01/06/15## -Befor modifying and digging into the code I wanted to have a common coding style in all files, fixing also some common coding mistakes when found. Another decision was to use C++11 features whenever possible since it should be considered a basic standard, having already C++17 code online. I started by fixing the "common module"; here I did the following: +Before modifying and digging into the code I wanted to have a common coding style in all files, fixing also some common coding mistakes when found. Another decision was to use C++11 features whenever possible since it should be considered a basic standard, having already C++17 code online. I started by fixing the "common module"; here I did the following: - I removed the ToString method since in c++11 it is present as *to_string()*; - I removed global variables from utils since they where just used in one function. @@ -149,3 +149,19 @@ Befor modifying and digging into the code I wanted to have a common coding style After this I started to fix the detectors which I am still working on. The first class which has bee updated is the *ODHOGDetector* which had also a lot of naming issues since it used a different naming style. There are stll some function which I dislike, for example the parsing methods. They are mainly based on creating a fake argc,argv couple of variables and the parsing them. This has to be removed in favor of a better and newer parsing library as the one included in boost. This will be one of the first steps which I will do as soon as I finished the basic refactoring step. +##Code refactoring 2 07/06/15## + +The first code refactoring phase terminated today, since I finished to review a bit all files; the main steps have been: + +- moving around code in order to have implementations in the cpp files to reduce multiple compilation time, and declarations in .h files. +- added some minor c++11 features like *ranged-loops* instead of iterators which are hard to read, more standard function like *to_string()* and *itos()* where possible +- removed standard namespace and opencv namespace to avoid name clashing +- refactored the whole simple ransac file folder which was outside of the namespace +- used a common coding style through all files which is fundamental +- I moved the **ODDetectorMultiAlgo.h** and **ODDetectorMultiAlgo.cpp** files into ODDetectorMultiAlgo.hpp since I made the classes template to avoid having the explicit **Pcl::PointXYZRGBA** type since it was using ODCADDetector3DGlobal.hpp which already defined template classes. The new file is situated in the impl folder in detectors to maintain the usual include structure. Now, since the default vale has been removed for the template classes, the type has to be specified when the class is instantiated which can been seen in the examples. + +Missing parts and next steps: + +- I have to check that compilation works fine also with svmlight which was disabled till now +- Use shared pointer where normal pointer are used. We have to decide if we want to use boost or stds shared pointers since pcl is using the first version. The second would be better so we would have everything which is possible using std and then when pcl moves toward std shared pointer we move also the rest of the code to that. +- Remove where possible the argc,argv parsing methods using standard ones. \ No newline at end of file diff --git a/examples/objectdetector/od_multialgo_files.cpp b/examples/objectdetector/od_multialgo_files.cpp index e1b5b771..a6c42798 100644 --- a/examples/objectdetector/od_multialgo_files.cpp +++ b/examples/objectdetector/od_multialgo_files.cpp @@ -26,7 +26,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <opencv2/highgui.hpp> -#include "detectors/misc/detection/ODDetectorMultiAlgo.h" +#include "detectors/misc/detection/ODDetectorMultiAlgo.hpp" #include "od/common/utils/ODFrameGenerator.h" int main(int argc, char *argv[]) diff --git a/examples/objectdetector/od_multialgo_pc.cpp b/examples/objectdetector/od_multialgo_pc.cpp index bca1ba9c..88ed5d52 100644 --- a/examples/objectdetector/od_multialgo_pc.cpp +++ b/examples/objectdetector/od_multialgo_pc.cpp @@ -31,7 +31,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -#include "od/detectors/misc/detection/ODDetectorMultiAlgo.h" +#include "od/detectors/misc/detection/ODDetectorMultiAlgo.hpp" #include "od/detectors/global3D/ODPointCloudGlobalMatching.h" #include "common/utils/ODFrameGenerator.h" #include <string> From 75cd5f3e156d1163052164d60899577a50111bd3 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Wed, 8 Jun 2016 13:38:52 +0200 Subject: [PATCH 18/79] refactored also common --- CMakeLists.txt | 1 + common/CMakeLists.txt | 3 +- .../include/od/common/pipeline/ODDetection.h | 255 ++++------------ .../include/od/common/pipeline/ODDetector.h | 17 +- .../{ObjectDetector.h => ODObjectDetector.h} | 115 ++------ common/include/od/common/pipeline/ODScene.h | 130 ++++----- common/include/od/common/pipeline/ODTrainer.h | 3 +- .../include/od/common/utils/shared_pointers.h | 10 + common/include/od/common/utils/utils.h | 2 +- common/src/pipeline/ODDetection.cpp | 273 ++++++++++++++++++ common/src/pipeline/ODObjectDetector.cpp | 129 +++++++++ common/src/pipeline/ODScene.cpp | 52 ++++ common/src/pipeline/ObjectDetector.cpp | 31 -- common/src/utils/ODFeatureDetector2D.cpp | 2 - common/src/utils/utils.cpp | 151 +++------- .../detectors/local2D/ODImageLocalMatching.h | 4 +- examples/apps/global2D/od_multihog_app.cpp | 2 +- 17 files changed, 667 insertions(+), 513 deletions(-) rename common/include/od/common/pipeline/{ObjectDetector.h => ODObjectDetector.h} (67%) create mode 100644 common/include/od/common/utils/shared_pointers.h create mode 100644 common/src/pipeline/ODDetection.cpp create mode 100644 common/src/pipeline/ODObjectDetector.cpp delete mode 100644 common/src/pipeline/ObjectDetector.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 7e1126a0..f3623417 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,7 @@ option(WITH_DOCUMENTATION "Build the OD documentation" ON) option(WITH_GPU "Build GPU components of the project" ON) option(WITH_SVMLIGHT "Build SVM Light" OFF) option(WITH_EXAMPLES "Build examples" OFF) +option(WITH_BOOST_SHARED_PTR "use boost::shared_ptr instead of std::shared_ptr" ON) # Set modules set(OD_MODULES_NAMES 3rdparty common detectors doc examples) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 85c33f13..7994efeb 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -17,8 +17,9 @@ if(build) set(IMPL_INCLUDES "") set(SOURCES - "src/pipeline/ObjectDetector.cpp" + "src/pipeline/ODObjectDetector.cpp" "src/pipeline/ODScene.cpp" + "src/pipeline/ODDetection.cpp" "src/utils/utils.cpp" "src/utils/ODFeatureDetector2D.cpp" ) diff --git a/common/include/od/common/pipeline/ODDetection.h b/common/include/od/common/pipeline/ODDetection.h index d6a6d460..1aef447d 100644 --- a/common/include/od/common/pipeline/ODDetection.h +++ b/common/include/od/common/pipeline/ODDetection.h @@ -34,6 +34,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <Eigen/Core> #include <opencv2/core/eigen.hpp> #include <opencv2/imgproc.hpp> +#include "od/common/utils/shared_pointers.h" namespace od { @@ -53,53 +54,25 @@ namespace od OD_DEFINE_ENUM_WITH_STRING_CONVERSIONS(DetectionType, (OD_DETECTION_RECOG)(OD_DETECTION_CLASS)(OD_DETECTION_NULL)) - virtual ~ODDetection() - { } + virtual ~ODDetection() {} - ODDetection(DetectionType const &type_ = OD_DETECTION_NULL, std::string const &id_ = "", double confidence_ = 1) : type_(type_), id_(id_), - confidence_(confidence_) - { } + ODDetection(const DetectionType & type_ = OD_DETECTION_NULL, const std::string & id_ = "", double confidence_ = 1); - void printSelf() - { - std::cout << "--Detection-- \nType: " << enumToString(type_) << std::endl; - std::cout << "ID: " << id_ << std::endl; - } - - DetectionType const &getType() const - { - return type_; - } - - void setType(DetectionType const &type_) - { - ODDetection::type_ = type_; - } - - std::string const &getId() const - { - return id_; - } + void printSelf(); - void setId(std::string const &id_) - { - ODDetection::id_ = id_; - } + const DetectionType & getType() const; + void setType(const DetectionType & type); + const std::string & getId() const; + void setId(const std::string & id); /** \brief Get/Set the confidence of the detection. ODDetector can use this to provide confidence amnong several detections. */ - double getConfidence() const - { - return confidence_; - } + double getConfidence() const; /** \brief Get/Set the confidence of the detection. ODDetector can use this to provide confidence amnong several detections. */ - void setConfidence(double confidence_) - { - ODDetection::confidence_ = confidence_; - } + void setConfidence(double confidence); private: DetectionType type_; @@ -116,43 +89,19 @@ namespace od { public: - virtual ~ODDetection2D() - { } + virtual ~ODDetection2D(){} - ODDetection2D(DetectionType const &type_ = OD_DETECTION_NULL, std::string const &id_ = "", double confidence_ = 1) : ODDetection(type_, id_, confidence_) - { - location_2d_ = Eigen::Vector3d::UnitZ(); - } + ODDetection2D(const DetectionType & type_ = OD_DETECTION_NULL, const std::string & id_ = "", double confidence_ = 1); - Eigen::Vector3d const &getLocation() const - { - return location_2d_; - } + const Eigen::Vector3d & getLocation() const; - void setLocation(Eigen::Vector3d const &location_) - { - ODDetection2D::location_2d_ = location_; - } + void setLocation(const Eigen::Vector3d & location); - cv::Rect const &getBoundingBox() const - { - return bounding_box_2d_; - } + const cv::Rect & getBoundingBox() const; + void setBoundingBox(const cv::Rect & bounding_box); - void setBoundingBox(cv::Rect const &bounding_box_) - { - ODDetection2D::bounding_box_2d_ = bounding_box_; - } - - cv::Mat const &getMetainfoImage() const - { - return metainfo_image_; - } - - void setMetainfoImage(cv::Mat const &metainfo_image_) - { - ODDetection2D::metainfo_image_ = metainfo_image_; - } + const cv::Mat & getMetainfoImage() const; + void setMetainfoImage(const cv::Mat & metainfo_image); Eigen::Vector3d location_2d_; @@ -168,87 +117,36 @@ namespace od class ODDetection3D : public virtual ODDetection { public: - virtual ~ODDetection3D() - { } + virtual ~ODDetection3D(){} - Eigen::Vector4d const &getLocation() const - { - return location_3d_; - } + ODDetection3D(const DetectionType & type_ = OD_DETECTION_NULL, const std::string & id_ = "", double confidence_ = 1); - void setLocation(Eigen::Vector4d const &location_) - { - ODDetection3D::location_3d_ = location_; - } - void setLocation(cv::Mat const &location_) - { - cv::cv2eigen(location_, this->location_3d_); - } + const Eigen::Vector4d & getLocation() const; - Eigen::Matrix3Xd const &getPose() const - { - return orientation_; - } + void setLocation(const Eigen::Vector4d & location); + void setLocation(const cv::Mat & location); - void setPose(Eigen::Matrix3Xd const &pose_) - { - ODDetection3D::orientation_ = pose_; - } - void setPose(cv::Mat const & pose_cv) - { - this->orientation_ = Eigen::Map<Eigen::Matrix3d>(pose_cv.clone().ptr<double>()); - } - - double getScale() const - { - return scale_; - } - - void setScale(double scale) - { - ODDetection3D::scale_ = scale; - } - - cv::Mat const &getMetainfoImage() const - { - return metainfo_image_; - } + const Eigen::Matrix3Xd & getPose() const; - void setMetainfoImage(cv::Mat const &metainfo_image) - { - ODDetection3D::metainfo_image_ = metainfo_image; - } + void setPose(const Eigen::Matrix3Xd & pose); + void setPose(const cv::Mat & pose_cv); - typename pcl::PointCloud<pcl::PointXYZ>::Ptr const &getMetainfoCluster() const - { - return metainfo_cluster_; - } + double getScale() const; + void setScale(double scale); - void setMetainfoCluster(typename pcl::PointCloud<pcl::PointXYZ>::Ptr const &metainfo_cluster_) - { - ODDetection3D::metainfo_cluster_ = metainfo_cluster_; - } + const cv::Mat & getMetainfoImage() const; + void setMetainfoImage(const cv::Mat & metainfo_image); - ODDetection3D(DetectionType const &type_ = OD_DETECTION_NULL, std::string const &id_ = "", double confidence_ = 1) : ODDetection(type_, id_, confidence_) - { - location_3d_ = Eigen::Vector4d::UnitW(); - orientation_.setIdentity(); - scale_ = 1; - } + const pcl::PointCloud<pcl::PointXYZ>::Ptr & getMetainfoCluster() const; + void setMetainfoCluster(const pcl::PointCloud<pcl::PointXYZ>::Ptr & metainfo_cluster); - void printSelf() - { - ODDetection::printSelf(); - std::cout << "Location: " << location_3d_ << std::endl; - std::cout << "Pose: " << orientation_ << std::endl; - std::cout << "Scale: " << scale_ << std::endl; - } + void printSelf(); Eigen::Vector4d location_3d_; Eigen::Matrix3Xd orientation_; double scale_; cv::Mat metainfo_image_; - typename pcl::PointCloud<pcl::PointXYZ>::Ptr metainfo_cluster_; + pcl::PointCloud<pcl::PointXYZ>::Ptr metainfo_cluster_; }; /** \brief Detection in 2D with complete information. @@ -269,57 +167,31 @@ namespace od { public: - ODDetections (int n = 0): detections_(n) - { - } - - virtual ~ODDetections() - { - for (int i = 0; i < this->size(); i++) - delete detections_[i]; - detections_.resize(0); - } - - int size() { return detections_.size(); } + ODDetections (unsigned int n = 0); - void push_back(ODDetection* detection) - { - detections_.push_back(detection); - } + virtual ~ODDetections(); - void append(ODDetections* detections) - { - detections_.insert(detections_.end(), detections->detections_.begin(), detections->detections_.end()); - //note the meta information of the appended detections are lost here. - } + int size() const; - ODDetection * operator[](int i) { return detections_[i]; } - ODDetection * at(int i) { return (*this)[i]; } + void push_back(ODDetection * detection); - cv::Mat const &getMetainfoImage() const - { - return metainfo_image_; - } + void append(ODDetections * detections); - void setMetainfoImage(cv::Mat const &metainfo_image_) - { - this->metainfo_image_ = metainfo_image_.clone(); - } + ODDetection * operator[](unsigned int i); + ODDetection * at(unsigned int i); - typename pcl::PointCloud<pcl::PointXYZ>::Ptr const &getMetainfoCluster() const - { - return metainfo_cluster_; - } + const cv::Mat & getMetainfoImage() const; + void setMetainfoImage(const cv::Mat & metainfo_image_); - void setMetainfoCluster(typename pcl::PointCloud<pcl::PointXYZ>::Ptr const &metainfo_cluster_) - { - this->metainfo_cluster_ = metainfo_cluster_; - } + const pcl::PointCloud<pcl::PointXYZ>::Ptr & getMetainfoCluster() const; + void setMetainfoCluster(const pcl::PointCloud<pcl::PointXYZ>::Ptr & metainfo_cluster); protected: + std::vector<ODDetection*> detections_; cv::Mat metainfo_image_; typename pcl::PointCloud<pcl::PointXYZ>::Ptr metainfo_cluster_; + }; @@ -336,29 +208,10 @@ namespace od /** \brief Draws rectangles over the input image using the bounding box information present in all the 2D detections. This is a quick function to render and verify the detections made. */ - ODSceneImage renderMetainfo(ODSceneImage input) - { - - //picking up random colors for different detection algorithm, if exist - /*std::map<std::string, cv::Scalar> color_map; - for(int i = 0; i < detections_.size(); i++) - { - if(color_map.find(detections_[i]->getId()) == color_map.end()) - color_map[detections_[i]->getId()] = CV_RGB(rand()%255, rand()%255, rand()%255); - }*/ - - cv::Mat image = input.getCVImage().clone(); - for(int i = 0; i < detections_.size(); i++) - { - ODDetection2D * detection = dynamic_cast<ODDetection2D *>(detections_[i]); - cv::rectangle(image, detection->bounding_box_2d_, getHashedColor(detections_[i]->getId(), 100), 2); - } - return ODSceneImage(image); - } - + ODSceneImage renderMetainfo(ODSceneImage & input); - ODDetection2D * operator[](int i) { return dynamic_cast<ODDetection2D *>(detections_[i]); } - ODDetection2D * at(int i) { return dynamic_cast<ODDetection2D *>(detections_[i]); } + ODDetection2D * operator[](unsigned int i); + ODDetection2D * at(unsigned int i); }; @@ -391,8 +244,8 @@ namespace od }*/ - ODDetection3D * operator[](int i) { return dynamic_cast<ODDetection3D *>(detections_[i]); } - ODDetection3D * at(int i) { return dynamic_cast<ODDetection3D *>(detections_[i]); } + ODDetection3D * operator[](unsigned int i); + ODDetection3D * at(unsigned int i); }; /** \brief The container class for ODDetectionComplete returned by ODDetector2DComplete @@ -404,8 +257,8 @@ namespace od { public: - ODDetectionComplete * operator[](int i) { return dynamic_cast<ODDetectionComplete *>(detections_[i]); } - ODDetectionComplete * at(int i) { return dynamic_cast <ODDetectionComplete *>(detections_[i]); } + ODDetectionComplete * operator[](unsigned int i); + ODDetectionComplete * at(unsigned int i); }; } diff --git a/common/include/od/common/pipeline/ODDetector.h b/common/include/od/common/pipeline/ODDetector.h index e453f8d0..f3fb6c9d 100644 --- a/common/include/od/common/pipeline/ODDetector.h +++ b/common/include/od/common/pipeline/ODDetector.h @@ -29,10 +29,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // #pragma once -#include "od/common/pipeline/ObjectDetector.h" +#include "od/common/pipeline/ODObjectDetector.h" #include "od/common/pipeline/ODScene.h" #include "od/common/pipeline/ODDetection.h" -#include "od/common/pipeline/ObjectDetector.h" #include "od/common/pipeline/ODAlgorithmBase.h" namespace od @@ -49,8 +48,8 @@ namespace od { public: - ODDetector(std::string const &training_data_location_) : ODDetectorCommon(training_data_location_) - { } + ODDetector(const std::string & training_data_location) : ODDetectorCommon(training_data_location) + {} virtual ODDetections * detect(ODScene *scene){} virtual ODDetections * detectOmni(ODScene *scene){} @@ -69,7 +68,7 @@ namespace od class ODDetector2D: public ODDetector { public: - ODDetector2D(const std::string & trained_data_location_) : ODDetector(trained_data_location_) + ODDetector2D(const std::string & trained_data_location) : ODDetector(trained_data_location) { } ODDetections * detect(ODScene *scene) @@ -103,8 +102,8 @@ namespace od class ODDetector3D: public ODDetector { public: - ODDetector3D(const std::string & trained_data_location_) : ODDetector(trained_data_location_) - { } + ODDetector3D(const std::string & trained_data_location) : ODDetector(trained_data_location) + {} /** \brief Function for performing detection on a segmented scene. * The purpose of this function is to perform detection on a segmented scene or an 'object candidate'. i.e. the entire scene is considered as an 'object' or an detection. It is possible for a scene to trigger multiple detections. @@ -132,8 +131,8 @@ namespace od class ODDetector2DComplete: public ODDetector { public: - ODDetector2DComplete(const std::string & trained_data_location_) : ODDetector(trained_data_location_) - { } + ODDetector2DComplete(const std::string & trained_data_location) : ODDetector(trained_data_location) + {} /** \brief Function for performing detection on a segmented scene. * The purpose of this function is to perform detection on a segmented scene or an 'object candidate'. i.e. the entire scene is considered as an 'object' or an detection. It is possible for a scene to trigger multiple detections. diff --git a/common/include/od/common/pipeline/ObjectDetector.h b/common/include/od/common/pipeline/ODObjectDetector.h similarity index 67% rename from common/include/od/common/pipeline/ObjectDetector.h rename to common/include/od/common/pipeline/ODObjectDetector.h index 4860f9f7..0b8bd87f 100644 --- a/common/include/od/common/pipeline/ObjectDetector.h +++ b/common/include/od/common/pipeline/ODObjectDetector.h @@ -31,13 +31,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <string> #include <vector> #include "od/common/pipeline/ODDetection.h" +#include "od/common/pipeline/ODScene.h" namespace od { - class ODScene; - class ODDetection; - enum DetectionMethod { PC_GLOBAL_FEATUREMATCHING, PC_LOCAL_CORRESPONDENCE_GROUPING, @@ -57,74 +55,46 @@ namespace od { public: - ODDetectorCommon(const std::string & trained_data_location_= "") : trained_data_location_(trained_data_location_) - { - std::string clasname = typeid(this).name(); - TRAINED_DATA_ID_ = clasname; - std::transform(clasname.begin(), clasname.end(), clasname.begin(), ::toupper); - TRAINED_LOCATION_DENTIFIER_ = clasname; - } + ODDetectorCommon(const std::string & trained_data_location); virtual void init() = 0; /** \brief Gets/Sets the directory containing the data for training. The trainer uses the data from directory for training. Detectors can use this location to get additional information in its detection algirhtms as well. */ - std::string getTrainingInputLocation() const - { - return training_input_location_; - } + std::string getTrainingInputLocation() const; /** \brief Gets/Sets the directory containing the data for training. The trainer uses the data from directory for training. Detectors can use this location to get additional information in its detection algirhtms as well. */ - void setTrainingInputLocation(const std::string & training_input_location_) - { - this->training_input_location_ = training_input_location_; - } + void setTrainingInputLocation(const std::string & training_input_location); /** \brief Gets/Sets the base directory for trained data. This should be same for all Trainers and Detectors and can be considered as the 'database' of trained data. Trainers uses one of its * subdirectories based on its type to store algo specific trained data. The corresponding Detector would use the same directory to fetch the trained data for online detection. */ - std::string getTrainedDataLocation() const - { - return trained_data_location_; - } + std::string getTrainedDataLocation() const; /** \brief The base directory for trained data. This should be same for all Trainers and Detectors and can be considered as the 'database' of trained data. Trainers uses one of its * subdirectories based on its type to store algo specific trained data. The corresponding Detector would use the same directory to fetch the trained data for online detection. */ - virtual void setTrainedDataLocation(const std::string & trained_data_location_) - { - this->trained_data_location_ = trained_data_location_; - } + virtual void setTrainedDataLocation(const std::string & trained_data_location); /** \brief Gets the specific directory for a Trainer or a Detector inside trained_data_location_. */ - std::string getSpecificTrainingDataLocation() - { - return trained_data_location_ + "/" + "TD_" + TRAINED_LOCATION_DENTIFIER_; - } - - std::string getSpecificTrainingData() - { - return getSpecificTrainingDataLocation() + "/" + TRAINED_DATA_ID_; - } - - const std::string & getTrainedDataID() const - { - return TRAINED_DATA_ID_; - } - - void setTrainedDataID(const std::string & trainedDataID) - { - ODDetectorCommon::TRAINED_DATA_ID_ = trainedDataID; - } + std::string getSpecificTrainingDataLocation(); + + std::string getSpecificTrainingData(); + + const std::string & getTrainedDataID() const; + + void setTrainedDataID(const std::string & trainedDataID); protected: + std::string training_input_location_, trained_data_location_; std::string TRAINED_DATA_ID_, TRAINED_LOCATION_DENTIFIER_; + }; /** \brief This is the main class for object detection and recognition. @@ -136,54 +106,23 @@ namespace od class ObjectDetector { public: + const DetectionMethod & getMethod() const; - ObjectDetector() - { } - - const DetectionMethod & getMethod() const - { - return method_; - } - - void setDetectionMethod(const DetectionMethod & detection_method_) - { - this->method_ = detection_method_; - } + void setDetectionMethod(const DetectionMethod & detection_method); - bool getAlwaysTrain() const - { - return always_train_; - } + bool getAlwaysTrain() const; - void setAlwaysTrain(bool always_train_) - { - this->always_train_ = always_train_; - } + void setAlwaysTrain(bool always_train); - std::string getTrainingInputLocation() const - { - return training_input_location_; - } + std::string getTrainingInputLocation() const; - void setTrainingInputLocation(const std::string & training_input_location_) - { - this->training_input_location_ = training_input_location_; - } + void setTrainingInputLocation(const std::string & training_input_location); - std::string getTrainingDataLocation() const - { - return training_data_location_; - } + std::string getTrainingDataLocation() const; - void setTrainingDataLocation(const std::string & training_data_location_) - { - this->training_data_location_ = training_data_location_; - } + void setTrainingDataLocation(const std::string & training_data_location); - std::string getSpecificTrainingDataLocation() - { - return training_data_location_ + "/" + "TD_" + TRAINED_DATA_IDENTIFIER_; - } + std::string getSpecificTrainingDataLocation(); virtual void init() = 0; @@ -192,13 +131,13 @@ namespace od virtual int train() = 0; - virtual int detect(ODScene * scene, const std::vector<ODDetection *> & detections) {} - virtual ODDetection* detect(ODScene * scene) {} - virtual ODDetections* detectOmni(ODScene * scene) {} + virtual ODDetection * detect(ODScene * scene) {} + virtual ODDetections * detectOmni(ODScene * scene) {} protected: + DetectionMethod method_; bool always_train_; bool trained_; diff --git a/common/include/od/common/pipeline/ODScene.h b/common/include/od/common/pipeline/ODScene.h index c9b3b3b3..e1d732d0 100644 --- a/common/include/od/common/pipeline/ODScene.h +++ b/common/include/od/common/pipeline/ODScene.h @@ -47,13 +47,12 @@ namespace od virtual void * getData() = 0; - std::string const & getPath() const - { - return path_; - } + std::string const & getPath() const; protected: + std::string path_; + }; /** \brief Class for Image Scene. @@ -65,49 +64,21 @@ namespace od { public: - const std::vector<cv::KeyPoint> & getKeypoints() const - { - return keypoints_; - } - - void setKeypoints(const std::vector<cv::KeyPoint> & keypoints_) - { - ODSceneImage::keypoints_ = keypoints_; - } - - const cv::Mat & getDescriptors() const - { - return descriptors_; - } - - void setDescriptors(const cv::Mat & descriptors_) - { - ODSceneImage::descriptors_ = descriptors_; - is_trained_ = true; - } + ODSceneImage(const cv::Mat & cvimage); + ODSceneImage(const std::string & path); - ODSceneImage(const cv::Mat & cvimage): is_trained_(false) - { - this->cvimage_ = cvimage.clone(); - } + const std::vector<cv::KeyPoint> & getKeypoints() const; + void setKeypoints(const std::vector<cv::KeyPoint> & keypoints_); - ODSceneImage(const std::string & path): is_trained_(false) - { - this->cvimage_ = cv::imread(path); - this->path_ = path; - } + const cv::Mat & getDescriptors() const; + void setDescriptors(const cv::Mat & descriptors_); - cv::Mat getCVImage() - { - return cvimage_; - } + cv::Mat getCVImage(); - void *getData() - { - return &cvimage_; - } + void * getData(); protected: + cv::Mat cvimage_; std::vector<cv::KeyPoint> keypoints_; cv::Mat descriptors_; @@ -126,45 +97,62 @@ namespace od { public: - typedef typename pcl::PointCloud<PointType>::Ptr PointCloudPtr; - ODScenePointCloud(const PointCloudPtr & point_cloud) - { - point_cloud_ = point_cloud; - } + ODScenePointCloud(const typename pcl::PointCloud<PointType>::Ptr & point_cloud); + ODScenePointCloud(const std::string & point_cloud_file); + ODScenePointCloud(): point_cloud_(new typename pcl::PointCloud<PointType>()){} - ODScenePointCloud(const std::string & point_cloud_file): point_cloud_(new pcl::PointCloud<PointType>()) - { - if (pcl::io::loadPCDFile<PointType> (point_cloud_file, *point_cloud_ ) == -1) - { - std::cout << "ERROR: Couldn't read the file "<< point_cloud_file << std::endl; - } - this->path_ = point_cloud_file; - } + const typename pcl::PointCloud<PointType>::Ptr & getPointCloud() const; + typename pcl::PointCloud<PointType>::Ptr & getPointCloudRef() const; + void setPointCloud(const typename pcl::PointCloud<PointType>::Ptr & point_cloud_); - ODScenePointCloud(): point_cloud_(new pcl::PointCloud<PointType>()) - {} + void * getData(); - const PointCloudPtr & getPointCloud() const - { - return point_cloud_; - } + protected: - PointCloudPtr & getPointCloudRef() const - { - return point_cloud_; - } + typename pcl::PointCloud<PointType>::Ptr point_cloud_; - void setPointCloud(const PointCloudPtr & point_cloud_) + }; + + template <typename PointType> + ODScenePointCloud<PointType>::ODScenePointCloud(const typename pcl::PointCloud<PointType>::Ptr & point_cloud) + { + point_cloud_ = point_cloud; + } + + template <typename PointType> + ODScenePointCloud<PointType>::ODScenePointCloud(const std::string & point_cloud_file): point_cloud_(new pcl::PointCloud<PointType>()) + { + if(pcl::io::loadPCDFile<PointType> (point_cloud_file, *point_cloud_ ) == -1) { - ODScenePointCloud::point_cloud_ = point_cloud_; + std::cout << "ERROR: Couldn't read the file "<< point_cloud_file << std::endl; } + path_ = point_cloud_file; + } - void * getData() - { return (void *)point_cloud_.get(); } + template <typename PointType> + const typename pcl::PointCloud<PointType>::Ptr & ODScenePointCloud<PointType>::getPointCloud() const + { + return point_cloud_; + } + + template <typename PointType> + typename pcl::PointCloud<PointType>::Ptr & ODScenePointCloud<PointType>::getPointCloudRef() const + { + return point_cloud_; + } + + template <typename PointType> + void ODScenePointCloud<PointType>::setPointCloud(const typename pcl::PointCloud<PointType>::Ptr & point_cloud) + { + point_cloud_ = point_cloud; + } + + template <typename PointType> + void * ODScenePointCloud<PointType>::getData() + { + return (void *)point_cloud_.get(); + } - protected: - PointCloudPtr point_cloud_; - }; } diff --git a/common/include/od/common/pipeline/ODTrainer.h b/common/include/od/common/pipeline/ODTrainer.h index 10f05813..fd4274dc 100644 --- a/common/include/od/common/pipeline/ODTrainer.h +++ b/common/include/od/common/pipeline/ODTrainer.h @@ -30,8 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #pragma once #include <boost/filesystem.hpp> #include "od/common/pipeline/ODAlgorithmBase.h" -#include "od/common/pipeline/ObjectDetector.h" - +#include "od/common/pipeline/ODObjectDetector.h" namespace od { diff --git a/common/include/od/common/utils/shared_pointers.h b/common/include/od/common/utils/shared_pointers.h new file mode 100644 index 00000000..1e943f27 --- /dev/null +++ b/common/include/od/common/utils/shared_pointers.h @@ -0,0 +1,10 @@ +#ifdef WITH_BOOST_SHARED_PTR + #define shared_ptr boost:shared_ptr + #define make_shared boost::make_shared + #include <boost/shared_ptr.hpp> + #include <boost/make_shared.hpp> +#else + #define shared_ptr std::shared_ptr + #define make_shared std::make_shared + #include <memory> +#endif \ No newline at end of file diff --git a/common/include/od/common/utils/utils.h b/common/include/od/common/utils/utils.h index 48d0588f..91516336 100644 --- a/common/include/od/common/utils/utils.h +++ b/common/include/od/common/utils/utils.h @@ -20,7 +20,7 @@ BOOST_PP_SEQ_ENUM(enumerators) \ }; \ \ - inline const char* enumToString(name v) \ + inline const char * enumToString(name v) \ { \ switch (v) \ { \ diff --git a/common/src/pipeline/ODDetection.cpp b/common/src/pipeline/ODDetection.cpp new file mode 100644 index 00000000..d3e114fa --- /dev/null +++ b/common/src/pipeline/ODDetection.cpp @@ -0,0 +1,273 @@ +#include "od/common/pipeline/ODDetection.h" + +namespace od +{ + + ODDetection::ODDetection(const DetectionType & type_, const std::string & id_, double confidence_) : + type_(type_), id_(id_), confidence_(confidence_) {} + + void ODDetection::printSelf() + { + std::cout << "--Detection-- \nType: " << enumToString(type_) << std::endl; + std::cout << "ID: " << id_ << std::endl; + } + + const ODDetection::DetectionType & ODDetection::getType() const + { + return type_; + } + + void ODDetection::setType(const DetectionType & type) + { + type_ = type; + } + + const std::string & ODDetection::getId() const + { + return id_; + } + + void ODDetection::setId(const std::string & id) + { + id_ = id_; + } + + + double ODDetection::getConfidence() const + { + return confidence_; + } + + + void ODDetection::setConfidence(double confidence) + { + confidence_ = confidence; + } + + + + + ODDetection2D::ODDetection2D(const DetectionType & type_, const std::string & id_ , double confidence_ ) : + ODDetection(type_, id_, confidence_) + { + location_2d_ = Eigen::Vector3d::UnitZ(); + } + + const Eigen::Vector3d & ODDetection2D::getLocation() const + { + return location_2d_; + } + + void ODDetection2D::setLocation(const Eigen::Vector3d & location) + { + location_2d_ = location; + } + + const cv::Rect & ODDetection2D::getBoundingBox() const + { + return bounding_box_2d_; + } + + void ODDetection2D::setBoundingBox(const cv::Rect & bounding_box) + { + bounding_box_2d_ = bounding_box; + } + + const cv::Mat & ODDetection2D::getMetainfoImage() const + { + return metainfo_image_; + } + + void ODDetection2D::setMetainfoImage(const cv::Mat & metainfo_image) + { + metainfo_image_ = metainfo_image; + } + + + + ODDetection3D::ODDetection3D(const DetectionType & type_, const std::string & id_, double confidence_) : + ODDetection(type_, id_, confidence_) + { + location_3d_ = Eigen::Vector4d::UnitW(); + orientation_.setIdentity(); + scale_ = 1; + } + + const Eigen::Vector4d & ODDetection3D::getLocation() const + { + return location_3d_; + } + + void ODDetection3D::setLocation(const Eigen::Vector4d & location) + { + location_3d_ = location; + } + void ODDetection3D::setLocation(const cv::Mat & location) + { + cv::cv2eigen(location, location_3d_); + } + + const Eigen::Matrix3Xd & ODDetection3D::getPose() const + { + return orientation_; + } + + void ODDetection3D::setPose(const Eigen::Matrix3Xd & pose) + { + orientation_ = pose; + } + void ODDetection3D::setPose(const cv::Mat & pose_cv) + { + orientation_ = Eigen::Map<Eigen::Matrix3d>(pose_cv.clone().ptr<double>()); + } + + double ODDetection3D::getScale() const + { + return scale_; + } + + void ODDetection3D::setScale(double scale) + { + ODDetection3D::scale_ = scale; + } + + const cv::Mat & ODDetection3D::getMetainfoImage() const + { + return metainfo_image_; + } + + void ODDetection3D::setMetainfoImage(const cv::Mat & metainfo_image) + { + metainfo_image_ = metainfo_image; + } + + const pcl::PointCloud<pcl::PointXYZ>::Ptr & ODDetection3D::getMetainfoCluster() const + { + return metainfo_cluster_; + } + + void ODDetection3D::setMetainfoCluster(const pcl::PointCloud<pcl::PointXYZ>::Ptr & metainfo_cluster) + { + metainfo_cluster_ = metainfo_cluster; + } + + void ODDetection3D::printSelf() + { + ODDetection::printSelf(); + std::cout << "Location: " << location_3d_ << std::endl; + std::cout << "Pose: " << orientation_ << std::endl; + std::cout << "Scale: " << scale_ << std::endl; + } + + + ODDetections::ODDetections(unsigned int n): detections_(n) {} + + ODDetections::~ODDetections() + { + for(size_t i = 0; i < size(); ++i) + delete detections_[i]; + detections_.resize(0); + } + + int ODDetections::size() const + { + return detections_.size(); + } + + void ODDetections::push_back(ODDetection * detection) + { + detections_.push_back(detection); + } + + void ODDetections::append(ODDetections * detections) + { + detections_.insert(detections_.end(), detections->detections_.begin(), detections->detections_.end()); + } + + ODDetection * ODDetections::operator[](unsigned int i) + { + return detections_[i]; + } + + ODDetection * ODDetections::at(unsigned int i) + { + return (*this)[i]; + } + + const cv::Mat & ODDetections::getMetainfoImage() const + { + return metainfo_image_; + } + + void ODDetections::setMetainfoImage(const cv::Mat & metainfo_image) + { + metainfo_image_ = metainfo_image.clone(); + } + + const pcl::PointCloud<pcl::PointXYZ>::Ptr & ODDetections::getMetainfoCluster() const + { + return metainfo_cluster_; + } + + void ODDetections::setMetainfoCluster(const pcl::PointCloud<pcl::PointXYZ>::Ptr & metainfo_cluster) + { + metainfo_cluster_ = metainfo_cluster; + } + + + ODSceneImage ODDetections2D::renderMetainfo(ODSceneImage & input) + { + + //picking up random colors for different detection algorithm, if exist + /*std::map<std::string, cv::Scalar> color_map; + for(int i = 0; i < detections_.size(); i++) + { + if(color_map.find(detections_[i]->getId()) == color_map.end()) + color_map[detections_[i]->getId()] = CV_RGB(rand()%255, rand()%255, rand()%255); + }*/ + + cv::Mat image = input.getCVImage().clone(); + for(size_t i = 0; i < detections_.size(); ++i) + { + ODDetection2D * detection = dynamic_cast<ODDetection2D *>(detections_[i]); + if(detection) + cv::rectangle(image, detection->bounding_box_2d_, getHashedColor(detections_[i]->getId(), 100), 2); + } + return ODSceneImage(image); + } + + + ODDetection2D * ODDetections2D::operator[](unsigned int i) + { + return dynamic_cast<ODDetection2D *>(detections_[i]); + } + + ODDetection2D * ODDetections2D::at(unsigned int i) + { + return dynamic_cast<ODDetection2D *>(detections_[i]); + } + + + ODDetection3D * ODDetections3D::operator[](unsigned int i) + { + return dynamic_cast<ODDetection3D *>(detections_[i]); + } + + ODDetection3D * ODDetections3D::at(unsigned int i) + { + return dynamic_cast<ODDetection3D *>(detections_[i]); + } + + + ODDetectionComplete * ODDetectionsComplete::operator[](unsigned int i) + { + return dynamic_cast<ODDetectionComplete *>(detections_[i]); + } + + ODDetectionComplete * ODDetectionsComplete::at(unsigned int i) + { + return dynamic_cast <ODDetectionComplete *>(detections_[i]); + } + + +} \ No newline at end of file diff --git a/common/src/pipeline/ODObjectDetector.cpp b/common/src/pipeline/ODObjectDetector.cpp new file mode 100644 index 00000000..c98b3bbb --- /dev/null +++ b/common/src/pipeline/ODObjectDetector.cpp @@ -0,0 +1,129 @@ +/* +Copyright (c) 2015, Kripasindhu Sarkar +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions 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. + * Neither the name of the copyright holder(s) nor the + names of its contributors may 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 COPYRIGHT HOLDER 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. +*/ +// +// Created by sarkar on 03.06.15. +// + +#include "od/common/pipeline/ODObjectDetector.h" + +namespace od { + + ODDetectorCommon::ODDetectorCommon(const std::string & trained_data_location) : trained_data_location_(trained_data_location) + { + std::string classname = typeid(this).name(); + TRAINED_DATA_ID_ = classname; + std::transform(classname.begin(), classname.end(), classname.begin(), ::toupper); + TRAINED_LOCATION_DENTIFIER_ = classname; + } + + std::string ODDetectorCommon::getTrainingInputLocation() const + { + return training_input_location_; + } + + void ODDetectorCommon::setTrainingInputLocation(const std::string & training_input_location) + { + training_input_location_ = training_input_location; + } + + std::string ODDetectorCommon::getTrainedDataLocation() const + { + return trained_data_location_; + } + + void ODDetectorCommon::setTrainedDataLocation(const std::string & trained_data_location) + { + trained_data_location_ = trained_data_location; + } + + std::string ODDetectorCommon::getSpecificTrainingDataLocation() + { + return trained_data_location_ + "/" + "TD_" + TRAINED_LOCATION_DENTIFIER_; + } + + std::string ODDetectorCommon::getSpecificTrainingData() + { + return getSpecificTrainingDataLocation() + "/" + TRAINED_DATA_ID_; + } + + const std::string & ODDetectorCommon::getTrainedDataID() const + { + return TRAINED_DATA_ID_; + } + + void ODDetectorCommon::setTrainedDataID(const std::string & trainedDataID) + { + ODDetectorCommon::TRAINED_DATA_ID_ = trainedDataID; + } + + + const DetectionMethod & ObjectDetector::getMethod() const + { + return method_; + } + + void ObjectDetector::setDetectionMethod(const DetectionMethod & detection_method) + { + method_ = detection_method; + } + + bool ObjectDetector::getAlwaysTrain() const + { + return always_train_; + } + + void ObjectDetector::setAlwaysTrain(bool always_train) + { + always_train_ = always_train; + } + + std::string ObjectDetector::getTrainingInputLocation() const + { + return training_input_location_; + } + + void ObjectDetector::setTrainingInputLocation(const std::string & training_input_location) + { + training_input_location_ = training_input_location; + } + + std::string ObjectDetector::getTrainingDataLocation() const + { + return training_data_location_; + } + + void ObjectDetector::setTrainingDataLocation(const std::string & training_data_location) + { + training_data_location_ = training_data_location; + } + + std::string ObjectDetector::getSpecificTrainingDataLocation() + { + return training_data_location_ + "/" + "TD_" + TRAINED_DATA_IDENTIFIER_; + } + +} \ No newline at end of file diff --git a/common/src/pipeline/ODScene.cpp b/common/src/pipeline/ODScene.cpp index 00fa53de..80341b04 100644 --- a/common/src/pipeline/ODScene.cpp +++ b/common/src/pipeline/ODScene.cpp @@ -29,3 +29,55 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // #include "od/common/pipeline/ODScene.h" + +namespace od { + + std::string const & ODScene::getPath() const + { + return path_; + } + + + ODSceneImage::ODSceneImage(const cv::Mat & cvimage): is_trained_(false) + { + cvimage_ = cvimage.clone(); + } + + ODSceneImage::ODSceneImage(const std::string & path): is_trained_(false) + { + cvimage_ = cv::imread(path); + path_ = path; + } + + const std::vector<cv::KeyPoint> & ODSceneImage::getKeypoints() const + { + return keypoints_; + } + + void ODSceneImage::setKeypoints(const std::vector<cv::KeyPoint> & keypoints) + { + keypoints_ = keypoints; + } + + const cv::Mat & ODSceneImage::getDescriptors() const + { + return descriptors_; + } + + void ODSceneImage::setDescriptors(const cv::Mat & descriptors) + { + descriptors_ = descriptors; + is_trained_ = true; + } + + cv::Mat ODSceneImage::getCVImage() + { + return cvimage_; + } + + void * ODSceneImage::getData() + { + return &cvimage_; + } + +} \ No newline at end of file diff --git a/common/src/pipeline/ObjectDetector.cpp b/common/src/pipeline/ObjectDetector.cpp deleted file mode 100644 index 08e25ad3..00000000 --- a/common/src/pipeline/ObjectDetector.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* -Copyright (c) 2015, Kripasindhu Sarkar -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions 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. - * Neither the name of the copyright holder(s) nor the - names of its contributors may 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 COPYRIGHT HOLDER 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. -*/ -// -// Created by sarkar on 03.06.15. -// - -#include "od/common/pipeline/ObjectDetector.h" diff --git a/common/src/utils/ODFeatureDetector2D.cpp b/common/src/utils/ODFeatureDetector2D.cpp index 346bb28a..15beb754 100644 --- a/common/src/utils/ODFeatureDetector2D.cpp +++ b/common/src/utils/ODFeatureDetector2D.cpp @@ -39,8 +39,6 @@ namespace od } } - - ODFeatureDetector2D::ODFeatureDetector2D(FeatureType type) { diff --git a/common/src/utils/utils.cpp b/common/src/utils/utils.cpp index b2293974..18de04df 100644 --- a/common/src/utils/utils.cpp +++ b/common/src/utils/utils.cpp @@ -57,8 +57,6 @@ namespace od const int resultImgW = cell_width * imgs_in_row; const int resultImgH = cell_height * imgs_in_col; - - cv::Mat result_img = cv::Mat::zeros(resultImgH, resultImgW, CV_8UC3); unsigned int ind = 0; cv::Mat tmp; @@ -128,129 +126,76 @@ namespace od return CV_RGB((hashed + offset) % 255, (hashed + 2*offset) % 255, (hashed + 3*offset) % 255); } - cv::Mat makeCanvasMultiImage1(std::vector<cv::Mat> & input, int window_height, int rows) - { - const unsigned int input_size = input.size(); - const unsigned int n_rows = rows > input_size ? input_size : rows; - const unsigned int edge_thickness = 10; - const unsigned int images_per_row = ceil(static_cast<float>(input_size) / static_cast<float>(n_rows)); - const unsigned int resize_height = floor(2.0 * ((floor(static_cast<float>(window_height - edge_thickness) / static_cast<float>(n_rows))) / 2.0)) - static_cast<float>(edge_thickness); - unsigned int max_row_length = 0; - - std::vector<int> resize_width; - - for(size_t i = 0; i < input_size;){ - int this_row_len = 0; - for(size_t k = 0; k < images_per_row; k++){ - float aspect_ratio = float(input[i].cols) / input[i].rows; - const int temp = static_cast<int>(ceil(resize_height * aspect_ratio)); - resize_width.push_back(temp); - this_row_len += temp; - if(++i == input_size) - break; - } - const int tmp = this_row_len + edge_thickness * (images_per_row + 1); - if(tmp > max_row_length){ - max_row_length = tmp; - } - } - - cv::Mat canvas_image(window_height, max_row_length, CV_8UC3, cv::Scalar(0, 0, 0)); - - for(size_t k = 0, i = 0; i < n_rows; i++){ - int y = i * resize_height + (i + 1) * edge_thickness; - int x_end = edge_thickness; - cv::Size s; - for(size_t j = 0; j < images_per_row && k < input_size; k++, j++){ - cv::Rect roi(x_end, y, resize_width[k], resize_height); - s = canvas_image(roi).size(); - // change the number of channels to three - cv::Mat target_roi(s, CV_8UC3); - if(input[k].channels() != canvas_image.channels()){ - if(input[k].channels() == 1){ - cv::cvtColor(input[k], target_roi, CV_GRAY2BGR); - } - } - cv::resize(target_roi, target_roi, s); - if(target_roi.type() != canvas_image.type()){ - target_roi.convertTo(target_roi, canvas_image.type()); - } - target_roi.copyTo(canvas_image(roi)); - x_end += resize_width[k] + edge_thickness; - } - } - return canvas_image; - } namespace fileutils { std::string getFirstFile(const std::string & base_path, const std::string & extension) - { - std::vector<std::string> files; - fileutils::getFilesInDirectoryInternal(base_path, "", files, extension); - if(files.size() == 0) - { - std::cout << "No file with extension " << extension << " present!" << std::endl; - std::cout << "Returning empty string" << std::endl; - return ""; - } - return files[0]; - } + { + std::vector<std::string> files; + fileutils::getFilesInDirectoryInternal(base_path, "", files, extension); + if(files.size() == 0) + { + std::cout << "No file with extension " << extension << " present!" << std::endl; + std::cout << "Returning empty string" << std::endl; + return std::string(""); + } + return files[0]; + } - void getFilesInDirectoryRec(const std::string & base_path, const std::string & extension, std::vector<std::string> & files) - { - fileutils::getFilesInDirectoryInternal(base_path, "", files, extension); - } + void getFilesInDirectoryRec(const std::string & base_path, const std::string & extension, std::vector<std::string> & files) + { + fileutils::getFilesInDirectoryInternal(base_path, "", files, extension); + } - void getFilesInDirectoryRec(const std::string & base_path, const std::vector<std::string> & extensions, std::vector<std::string> & files) - { - fileutils::getFilesInDirectoryInternal(base_path, "", files, extensions); - } + void getFilesInDirectoryRec(const std::string & base_path, const std::vector<std::string> & extensions, std::vector<std::string> & files) + { + fileutils::getFilesInDirectoryInternal(base_path, "", files, extensions); + } - void getFilesInDirectoryInternal(const boost::filesystem::path & dir, const std::string & rel_path_so_far, std::vector<std::string> & relative_paths, const std::vector<std::string> & exts) - { - boost::filesystem::directory_iterator end_itr; - for(boost::filesystem::directory_iterator itr(dir); itr != end_itr; ++itr) { - //check if its a directory, then get models in it - if(boost::filesystem::is_directory(*itr)) { + void getFilesInDirectoryInternal(const boost::filesystem::path & dir, const std::string & rel_path_so_far, std::vector<std::string> & relative_paths, const std::vector<std::string> & exts) + { + boost::filesystem::directory_iterator end_itr; + for(boost::filesystem::directory_iterator itr(dir); itr != end_itr; ++itr) { + //check if its a directory, then get models in it + if(boost::filesystem::is_directory(*itr)) { #if BOOST_FILESYSTEM_VERSION == 3 - std::string so_far = rel_path_so_far + (itr->path().filename()).string() + "/"; + std::string so_far = rel_path_so_far + (itr->path().filename()).string() + "/"; #else - std::string so_far = rel_path_so_far + (itr->path ()).filename () + "/"; + std::string so_far = rel_path_so_far + (itr->path ()).filename () + "/"; #endif - boost::filesystem::path curr_path = itr->path(); - getFilesInDirectoryInternal(curr_path, so_far, relative_paths, exts); - } else { - //check that it is a ply file and then add, otherwise ignore.. - std::vector<std::string> strs; + boost::filesystem::path curr_path = itr->path(); + getFilesInDirectoryInternal(curr_path, so_far, relative_paths, exts); + } else { + //check that it is a ply file and then add, otherwise ignore.. + std::vector<std::string> strs; #if BOOST_FILESYSTEM_VERSION == 3 - std::string file = (itr->path().filename()).string(); + std::string file = (itr->path().filename()).string(); #else - std::string file = (itr->path ()).filename (); + std::string file = (itr->path ()).filename (); #endif - boost::split(strs, file, boost::is_any_of(".")); - std::string extension = strs[strs.size() - 1]; + boost::split(strs, file, boost::is_any_of(".")); + std::string extension = strs[strs.size() - 1]; - bool flagfound = false; - for (int exti = 0; exti < exts.size(); exti ++) - if(file.rfind(exts[exti]) != std::string::npos) - { flagfound = true; break; } + bool flagfound = false; + for (int exti = 0; exti < exts.size(); exti ++) + if(file.rfind(exts[exti]) != std::string::npos) + { flagfound = true; break; } - if( flagfound == true ) - { + if( flagfound == true ) + { #if BOOST_FILESYSTEM_VERSION == 3 - std::string path = rel_path_so_far + (itr->path().filename()).string(); + std::string path = rel_path_so_far + (itr->path().filename()).string(); #else - std::string path = rel_path_so_far + (itr->path ()).filename (); + std::string path = rel_path_so_far + (itr->path ()).filename (); #endif - std::string fullpath = rel_path_so_far + itr->path().string(); - relative_paths.push_back(fullpath); - } - } + std::string fullpath = rel_path_so_far + itr->path().string(); + relative_paths.push_back(fullpath); } } + } + } void getFilesInDirectoryInternal(const boost::filesystem::path & dir, const std::string & rel_path_so_far, std::vector<std::string> & relative_paths, const std::string & ext) { diff --git a/detectors/include/od/detectors/local2D/ODImageLocalMatching.h b/detectors/include/od/detectors/local2D/ODImageLocalMatching.h index eae196fa..b5526ba6 100644 --- a/detectors/include/od/detectors/local2D/ODImageLocalMatching.h +++ b/detectors/include/od/detectors/local2D/ODImageLocalMatching.h @@ -27,13 +27,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Created by sarkar on 05.06.15. // - #pragma once #include "od/common/pipeline/ODScene.h" #include "od/common/pipeline/ODTrainer.h" #include "od/common/pipeline/ODDetector.h" -#include "od/common/pipeline/ObjectDetector.h" - +#include "od/common/pipeline/ODObjectDetector.h" namespace od { diff --git a/examples/apps/global2D/od_multihog_app.cpp b/examples/apps/global2D/od_multihog_app.cpp index 27e5e2f3..cc6967fe 100644 --- a/examples/apps/global2D/od_multihog_app.cpp +++ b/examples/apps/global2D/od_multihog_app.cpp @@ -33,13 +33,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/global2D/detection/ODHOGDetector.h" #include "od/common/utils/ODFrameGenerator.h" -cv::Size sizesingle(640, 480); int main(int argc, char *argv[]) { std::string trained_data_dir(argv[1]), input_video(argv[2]), output_video = "output.avi"; if (argc > 3) output_video = argv[3]; + cv::Size sizesingle(640, 480); //get 3 detectors of different types std::vector<string> messages; From 01aa549571ba369d02564ece12780182ca06ab61 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Thu, 9 Jun 2016 17:03:27 +0200 Subject: [PATCH 19/79] adds shared pointer and now fixes alsoall the pointer for the detector, but has still to be tested --- CMakeLists.txt | 4 ++ .../include/od/common/pipeline/ODDetection.h | 37 +++++++----- .../include/od/common/pipeline/ODDetector.h | 32 +++++----- .../od/common/pipeline/ODObjectDetector.h | 6 +- common/include/od/common/pipeline/ODScene.h | 3 +- common/include/od/common/pipeline/ODTrainer.h | 1 - .../od/common/utils/ODFeatureDetector2D.h | 2 +- .../od/common/utils/ODFrameGenerator.h | 19 +++--- .../od/common/utils/ODShared_pointers.h | 16 +++++ .../include/od/common/utils/shared_pointers.h | 10 ---- common/include/od/common/utils/utils.h | 2 +- common/src/pipeline/ODDetection.cpp | 38 ++++++------ common/src/utils/ODDetector.cpp | 30 ---------- common/src/utils/ODFeatureDetector2D.cpp | 12 ++-- common/src/utils/utils.cpp | 4 +- .../detection/ODCADDetector3DGlobal.hpp | 51 ++++++++-------- .../misc/detection/ODDetectorMultiAlgo.hpp | 49 +++++++-------- .../od/detectors/global2D/ODFaceRecognizer.h | 6 +- .../global2D/detection/ODCascadeDetector.h | 6 +- .../global2D/detection/ODHOGDetector.h | 6 +- .../global2D/training/ODHOGTrainer.h | 6 +- .../global3D/ODPointCloudGlobalMatching.h | 9 +-- .../training/ODCADDetectTrainer3DGlobal.h | 12 +++- .../detectors/local2D/ODImageLocalMatching.h | 57 ++++-------------- .../detection/ODCADRecognizer2DLocal.h | 24 ++++++-- .../simple_ransac_detection/CsvReader.h | 6 +- .../simple_ransac_detection/CsvWriter.h | 8 ++- .../simple_ransac_detection/PnPProblem.h | 4 +- .../simple_ransac_detection/RobustMatcher.h | 10 ++-- detectors/src/global2D/ODFaceRecognizer.cpp | 10 ++-- .../global2D/detection/ODCascadeDetector.cpp | 15 +++-- .../src/global2D/detection/ODHOGDetector.cpp | 30 +++++----- detectors/src/global3D/CMakeLists.txt | 1 - .../global3D/ODPointCloudGlobalMatching.cpp | 5 +- .../detection/ODCADDetector3DGlobal.cpp | 30 ---------- .../training/ODCADDetectTrainer3DGlobal.cpp | 19 ++---- .../src/local2D/ODImageLocalMatching.cpp | 60 +++++++++++++++++++ .../detection/ODCADRecognizer2DLocal.cpp | 43 ++++--------- .../simple_ransac_detection/CsvReader.cpp | 14 ++--- .../simple_ransac_detection/CsvWriter.cpp | 18 +++--- .../ModelRegistration.cpp | 2 +- .../simple_ransac_detection/PnPProblem.cpp | 4 +- .../ODCADRecogTrainerSnapshotBased.cpp | 24 ++++---- .../od_test_single_db_single_model.cpp | 15 +++-- examples/apps/global2D/od_multihog_app.cpp | 9 +-- examples/objectdetector/od_cascade_cam.cpp | 3 +- examples/objectdetector/od_cascade_files.cpp | 3 +- .../od_example_framegenerator.cpp | 2 +- examples/objectdetector/od_hog_train.cpp | 3 +- .../od_image_cadrecog_camera.cpp | 5 +- .../od_image_cadrecog_files.cpp | 3 +- .../objectdetector/od_image_customhog.cpp | 3 +- .../objectdetector/od_image_facerecog.cpp | 3 +- .../objectdetector/od_image_hog_files.cpp | 3 +- .../objectdetector/od_multialgo_files.cpp | 3 +- examples/objectdetector/od_multialgo_pc.cpp | 10 ++-- examples/objectdetector/od_pc_global.cpp | 5 +- .../objectdetector/od_pc_global_files.cpp | 7 ++- .../objectdetector/od_pc_global_real_time.cpp | 7 ++- 59 files changed, 410 insertions(+), 419 deletions(-) create mode 100644 common/include/od/common/utils/ODShared_pointers.h delete mode 100644 common/include/od/common/utils/shared_pointers.h delete mode 100644 common/src/utils/ODDetector.cpp delete mode 100644 detectors/src/global3D/detection/ODCADDetector3DGlobal.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f3623417..5d753e8c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,10 @@ option(WITH_SVMLIGHT "Build SVM Light" OFF) option(WITH_EXAMPLES "Build examples" OFF) option(WITH_BOOST_SHARED_PTR "use boost::shared_ptr instead of std::shared_ptr" ON) +if(WITH_BOOST_SHARED_PTR) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWITH_BOOST_SHARED_PTR") +endif() + # Set modules set(OD_MODULES_NAMES 3rdparty common detectors doc examples) set(OD_MODULES_DIRS ${OD_MODULES_NAMES}) diff --git a/common/include/od/common/pipeline/ODDetection.h b/common/include/od/common/pipeline/ODDetection.h index 1aef447d..8053d6fe 100644 --- a/common/include/od/common/pipeline/ODDetection.h +++ b/common/include/od/common/pipeline/ODDetection.h @@ -28,13 +28,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 12.06.15. // #pragma once - #include "od/common/utils/utils.h" #include "od/common/pipeline/ODScene.h" +#include "od/common/utils/ODShared_pointers.h" + #include <Eigen/Core> #include <opencv2/core/eigen.hpp> #include <opencv2/imgproc.hpp> -#include "od/common/utils/shared_pointers.h" namespace od { @@ -56,7 +56,7 @@ namespace od virtual ~ODDetection() {} - ODDetection(const DetectionType & type_ = OD_DETECTION_NULL, const std::string & id_ = "", double confidence_ = 1); + ODDetection(const DetectionType & type_ = OD_DETECTION_NULL, const std::string & id_ = "", double confidence_ = 1.0); void printSelf(); @@ -75,9 +75,11 @@ namespace od void setConfidence(double confidence); private: + DetectionType type_; std::string id_; double confidence_; + }; /** \brief Detection for 2D with 2D location information @@ -91,7 +93,7 @@ namespace od virtual ~ODDetection2D(){} - ODDetection2D(const DetectionType & type_ = OD_DETECTION_NULL, const std::string & id_ = "", double confidence_ = 1); + ODDetection2D(const DetectionType & type_ = OD_DETECTION_NULL, const std::string & id_ = "", double confidence_ = 1.0); const Eigen::Vector3d & getLocation() const; @@ -107,6 +109,7 @@ namespace od Eigen::Vector3d location_2d_; cv::Rect bounding_box_2d_; cv::Mat metainfo_image_; + }; /** \brief Detection in 3D with 3D location information. @@ -117,9 +120,10 @@ namespace od class ODDetection3D : public virtual ODDetection { public: + virtual ~ODDetection3D(){} - ODDetection3D(const DetectionType & type_ = OD_DETECTION_NULL, const std::string & id_ = "", double confidence_ = 1); + ODDetection3D(const DetectionType & type_ = OD_DETECTION_NULL, const std::string & id_ = "", double confidence_ = 1.0); const Eigen::Vector4d & getLocation() const; @@ -147,6 +151,7 @@ namespace od double scale_; cv::Mat metainfo_image_; pcl::PointCloud<pcl::PointXYZ>::Ptr metainfo_cluster_; + }; /** \brief Detection in 2D with complete information. @@ -173,12 +178,12 @@ namespace od int size() const; - void push_back(ODDetection * detection); + void push_back(shared_ptr<ODDetection> detection); - void append(ODDetections * detections); + void append(shared_ptr<ODDetections> detections); - ODDetection * operator[](unsigned int i); - ODDetection * at(unsigned int i); + shared_ptr<ODDetection> operator[](unsigned int i); + shared_ptr<ODDetection> at(unsigned int i); const cv::Mat & getMetainfoImage() const; void setMetainfoImage(const cv::Mat & metainfo_image_); @@ -188,7 +193,7 @@ namespace od protected: - std::vector<ODDetection*> detections_; + std::vector<shared_ptr<ODDetection>> detections_; cv::Mat metainfo_image_; typename pcl::PointCloud<pcl::PointXYZ>::Ptr metainfo_cluster_; @@ -210,8 +215,8 @@ namespace od */ ODSceneImage renderMetainfo(ODSceneImage & input); - ODDetection2D * operator[](unsigned int i); - ODDetection2D * at(unsigned int i); + shared_ptr<ODDetection2D> operator[](unsigned int i); + shared_ptr<ODDetection2D> at(unsigned int i); }; @@ -244,8 +249,8 @@ namespace od }*/ - ODDetection3D * operator[](unsigned int i); - ODDetection3D * at(unsigned int i); + shared_ptr<ODDetection3D> operator[](unsigned int i); + shared_ptr<ODDetection3D> at(unsigned int i); }; /** \brief The container class for ODDetectionComplete returned by ODDetector2DComplete @@ -257,8 +262,8 @@ namespace od { public: - ODDetectionComplete * operator[](unsigned int i); - ODDetectionComplete * at(unsigned int i); + shared_ptr<ODDetectionComplete> operator[](unsigned int i); + shared_ptr<ODDetectionComplete> at(unsigned int i); }; } diff --git a/common/include/od/common/pipeline/ODDetector.h b/common/include/od/common/pipeline/ODDetector.h index f3fb6c9d..c5eb53bd 100644 --- a/common/include/od/common/pipeline/ODDetector.h +++ b/common/include/od/common/pipeline/ODDetector.h @@ -28,11 +28,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 08.06.15. // #pragma once - -#include "od/common/pipeline/ODObjectDetector.h" #include "od/common/pipeline/ODScene.h" -#include "od/common/pipeline/ODDetection.h" -#include "od/common/pipeline/ODAlgorithmBase.h" +#include "od/common/pipeline/ODObjectDetector.h" +#include "od/common/utils/ODShared_pointers.h" namespace od { @@ -51,8 +49,8 @@ namespace od ODDetector(const std::string & training_data_location) : ODDetectorCommon(training_data_location) {} - virtual ODDetections * detect(ODScene *scene){} - virtual ODDetections * detectOmni(ODScene *scene){} + virtual shared_ptr<ODDetections> detect(shared_ptr<ODScene> scene){} + virtual shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene){} bool meta_info_; @@ -71,9 +69,9 @@ namespace od ODDetector2D(const std::string & trained_data_location) : ODDetector(trained_data_location) { } - ODDetections * detect(ODScene *scene) + shared_ptr<ODDetections> detect(shared_ptr<ODScene> scene) { - return detect(dynamic_cast<ODSceneImage *>(scene)); + return detect(dynamic_pointer_cast<ODSceneImage>(scene)); } /** \brief Function for performing detection on a segmented scene. @@ -81,14 +79,14 @@ namespace od * \param[in] scene An instance of 2D scene * \return [out] detections A number of detections as an ODDetections instance. */ - virtual ODDetections * detect(ODSceneImage * scene) = 0; + virtual shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene) = 0; /** \brief Function for performing detection on an entire scene. * The purpose of this function is to detect an object in an entire scene. Thus, other than the type of detection we also have information about the location of the detection w.r.t. the scene. * \param[in] scene An instance of 2D scene * \return [out] detections A number of detections as an ODDetections2D instance containing information about the detection and its 2D location. */ - virtual ODDetections2D * detectOmni(ODSceneImage * scene) = 0; + virtual shared_ptr<ODDetections2D> detectOmni(shared_ptr<ODSceneImage> scene) = 0; }; /** \brief The detector of 3D scene. @@ -102,22 +100,21 @@ namespace od class ODDetector3D: public ODDetector { public: - ODDetector3D(const std::string & trained_data_location) : ODDetector(trained_data_location) - {} + ODDetector3D(const std::string & trained_data_location) : ODDetector(trained_data_location) {} /** \brief Function for performing detection on a segmented scene. * The purpose of this function is to perform detection on a segmented scene or an 'object candidate'. i.e. the entire scene is considered as an 'object' or an detection. It is possible for a scene to trigger multiple detections. * \param[in] scene An instance of 3D scene * \return A number of detections as an ODDetections instance. */ - virtual ODDetections * detect(ODScenePointCloud<PointT> * scene) = 0; + virtual shared_ptr<ODDetections> detect(shared_ptr<ODScenePointCloud<PointT>> scene) = 0; /** \brief Function for performing detection on an entire scene. * The purpose of this function is to detect an object in an entire scene. Thus, other than the type of detection we also have information about the location of the detection w.r.t. the scene. * \param[in] scene An instance of 3D scene * \return A number of detections as an ODDetections3D instance containing information about the detection and its 3D pose. */ - virtual ODDetections3D * detectOmni(ODScenePointCloud<PointT> * scene) = 0; + virtual shared_ptr<ODDetections3D> detectOmni(shared_ptr<ODScenePointCloud<PointT>> scene) = 0; }; /** \brief The detector of 2D scene performing a 'complete detection'. @@ -131,22 +128,21 @@ namespace od class ODDetector2DComplete: public ODDetector { public: - ODDetector2DComplete(const std::string & trained_data_location) : ODDetector(trained_data_location) - {} + ODDetector2DComplete(const std::string & trained_data_location) : ODDetector(trained_data_location) {} /** \brief Function for performing detection on a segmented scene. * The purpose of this function is to perform detection on a segmented scene or an 'object candidate'. i.e. the entire scene is considered as an 'object' or an detection. It is possible for a scene to trigger multiple detections. * \param[in] scene An instance of 2D scene * \return A number of detections as an ODDetections instance. */ - virtual ODDetections * detect(ODSceneImage * scene) = 0; + virtual shared_ptr<ODDetections> detect(share_ptd<ODSceneImage> scene) = 0; /** \brief Function for performing detection on an entire scene. * The purpose of this function is to detect an object in an entire scene. Thus, other than the type of detection we also have information about the location of the detection w.r.t. the scene. * \param[in] scene An instance of 2D scene * \return A number of detections as an ODDetections3D instance containing information about the detection and its pose in 3D. */ - virtual ODDetections3D * detectOmni(ODSceneImage * scene) = 0; + virtual shared_ptr<ODDetections3D> detectOmni(share_ptd<ODSceneImage> scene) = 0; }; } diff --git a/common/include/od/common/pipeline/ODObjectDetector.h b/common/include/od/common/pipeline/ODObjectDetector.h index 0b8bd87f..8849569a 100644 --- a/common/include/od/common/pipeline/ODObjectDetector.h +++ b/common/include/od/common/pipeline/ODObjectDetector.h @@ -131,10 +131,10 @@ namespace od virtual int train() = 0; - virtual int detect(ODScene * scene, const std::vector<ODDetection *> & detections) {} + virtual int detect(shared_ptr<ODScene> scene, const std::vector<shared_ptr<ODDetection> > & detections) {} - virtual ODDetection * detect(ODScene * scene) {} - virtual ODDetections * detectOmni(ODScene * scene) {} + virtual shared_ptr<ODDetection> detect(shared_ptr<ODScene> scene) {} + virtual shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene) {} protected: diff --git a/common/include/od/common/pipeline/ODScene.h b/common/include/od/common/pipeline/ODScene.h index e1d732d0..afaf3459 100644 --- a/common/include/od/common/pipeline/ODScene.h +++ b/common/include/od/common/pipeline/ODScene.h @@ -121,7 +121,8 @@ namespace od } template <typename PointType> - ODScenePointCloud<PointType>::ODScenePointCloud(const std::string & point_cloud_file): point_cloud_(new pcl::PointCloud<PointType>()) + ODScenePointCloud<PointType>::ODScenePointCloud(const std::string & point_cloud_file): + point_cloud_(new pcl::PointCloud<PointType>()) { if(pcl::io::loadPCDFile<PointType> (point_cloud_file, *point_cloud_ ) == -1) { diff --git a/common/include/od/common/pipeline/ODTrainer.h b/common/include/od/common/pipeline/ODTrainer.h index fd4274dc..71a511c3 100644 --- a/common/include/od/common/pipeline/ODTrainer.h +++ b/common/include/od/common/pipeline/ODTrainer.h @@ -29,7 +29,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // #pragma once #include <boost/filesystem.hpp> -#include "od/common/pipeline/ODAlgorithmBase.h" #include "od/common/pipeline/ODObjectDetector.h" namespace od diff --git a/common/include/od/common/utils/ODFeatureDetector2D.h b/common/include/od/common/utils/ODFeatureDetector2D.h index 0aa051b1..4b4e86ea 100644 --- a/common/include/od/common/utils/ODFeatureDetector2D.h +++ b/common/include/od/common/utils/ODFeatureDetector2D.h @@ -30,7 +30,7 @@ namespace od void computeKeypointsAndDescriptors(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints); - void findSiftGPUDescriptors(const char * image_name, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints); + void findSiftGPUDescriptors(const std::string & image_name, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints); void findSiftGPUDescriptors(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints); diff --git a/common/include/od/common/utils/ODFrameGenerator.h b/common/include/od/common/utils/ODFrameGenerator.h index 18661357..0ece0100 100644 --- a/common/include/od/common/utils/ODFrameGenerator.h +++ b/common/include/od/common/utils/ODFrameGenerator.h @@ -1,6 +1,3 @@ -// -// Created by sarkar on 19.06.15. -// #pragma once #include "od/common/pipeline/ODDetection.h" #include "od/common/pipeline/ODScene.h" @@ -176,26 +173,28 @@ namespace od grabber_.stop (); } - ODScenePointCloud<PointT> * getNextFrame() + shared_ptr<ODScenePointCloud<PointT>> getNextFrame() { OpenNIFrameSource::PointCloudPtr frame = snap(); - return new ODScenePointCloud<PointT>(frame); + return make_shared<ODScenePointCloud<PointT>>(frame); } bool isValid() - {return isActive();} + { + return isActive(); + } const PointCloudPtr snap () { - return (most_recent_frame_); + return most_recent_frame_; } bool isActive () { return active_; } - void onKeyboardEvent (const pcl::visualization::KeyboardEvent & event) + void onKeyboardEvent(const pcl::visualization::KeyboardEvent & event) { // When the spacebar is pressed, trigger a frame capture mutex_.lock (); @@ -207,11 +206,11 @@ namespace od } protected: - void onNewFrame (const PointCloudConstPtr &cloud) + void onNewFrame(const PointCloudConstPtr & cloud) { mutex_.lock (); ++frame_counter_; - most_recent_frame_ = boost::make_shared<PointCloud>(*cloud); // Make a copy of the frame + most_recent_frame_ = make_shared<PointCloud>(*cloud); // Make a copy of the frame mutex_.unlock (); } diff --git a/common/include/od/common/utils/ODShared_pointers.h b/common/include/od/common/utils/ODShared_pointers.h new file mode 100644 index 00000000..696274fb --- /dev/null +++ b/common/include/od/common/utils/ODShared_pointers.h @@ -0,0 +1,16 @@ +#pragma once +#ifdef WITH_BOOST_SHARED_PTR + #include <boost/shared_ptr.hpp> + #include <boost/make_shared.hpp> + #include <boost/pointer_cast.hpp> + using boost::shared_ptr; + using boost::make_shared; + using boost::static_pointer_cast; + using boost::dynamic_pointer_cast; +#else + #include <memory> + using std::shared_ptr; + using std::make_shared; + using std::static_pointer_cast; + using std::dynamic_pointer_cast; +#endif \ No newline at end of file diff --git a/common/include/od/common/utils/shared_pointers.h b/common/include/od/common/utils/shared_pointers.h deleted file mode 100644 index 1e943f27..00000000 --- a/common/include/od/common/utils/shared_pointers.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifdef WITH_BOOST_SHARED_PTR - #define shared_ptr boost:shared_ptr - #define make_shared boost::make_shared - #include <boost/shared_ptr.hpp> - #include <boost/make_shared.hpp> -#else - #define shared_ptr std::shared_ptr - #define make_shared std::make_shared - #include <memory> -#endif \ No newline at end of file diff --git a/common/include/od/common/utils/utils.h b/common/include/od/common/utils/utils.h index 91516336..3f694f97 100644 --- a/common/include/od/common/utils/utils.h +++ b/common/include/od/common/utils/utils.h @@ -52,7 +52,7 @@ namespace od */ cv::Mat makeCanvasMultiImages(const std::vector<cv::Mat> & imgs, const cv::Size & cellSize, const std::vector<std::string> & messages); - cv::Scalar getHashedColor(const std::string & name, int offset); + cv::Scalar getHashedColor(const std::string & name, int offset = 100); std::string getTexfileinObj(const std::string & objfilename); diff --git a/common/src/pipeline/ODDetection.cpp b/common/src/pipeline/ODDetection.cpp index d3e114fa..f103a933 100644 --- a/common/src/pipeline/ODDetection.cpp +++ b/common/src/pipeline/ODDetection.cpp @@ -164,8 +164,6 @@ namespace od ODDetections::~ODDetections() { - for(size_t i = 0; i < size(); ++i) - delete detections_[i]; detections_.resize(0); } @@ -174,24 +172,24 @@ namespace od return detections_.size(); } - void ODDetections::push_back(ODDetection * detection) + void ODDetections::push_back(shared_ptr<ODDetection> detection) { detections_.push_back(detection); } - void ODDetections::append(ODDetections * detections) + void ODDetections::append(shared_ptr<ODDetections> detections) { detections_.insert(detections_.end(), detections->detections_.begin(), detections->detections_.end()); } - ODDetection * ODDetections::operator[](unsigned int i) + shared_ptr<ODDetection> ODDetections::operator[](unsigned int i) { return detections_[i]; } - ODDetection * ODDetections::at(unsigned int i) + shared_ptr<ODDetection> ODDetections::at(unsigned int i) { - return (*this)[i]; + return detections_[i]; } const cv::Mat & ODDetections::getMetainfoImage() const @@ -229,7 +227,7 @@ namespace od cv::Mat image = input.getCVImage().clone(); for(size_t i = 0; i < detections_.size(); ++i) { - ODDetection2D * detection = dynamic_cast<ODDetection2D *>(detections_[i]); + shared_ptr<ODDetection2D> detection = dynamic_pointer_cast<ODDetection2D>(detections_[i]); if(detection) cv::rectangle(image, detection->bounding_box_2d_, getHashedColor(detections_[i]->getId(), 100), 2); } @@ -237,36 +235,36 @@ namespace od } - ODDetection2D * ODDetections2D::operator[](unsigned int i) + shared_ptr<ODDetection2D> ODDetections2D::operator[](unsigned int i) { - return dynamic_cast<ODDetection2D *>(detections_[i]); + return dynamic_pointer_cast<ODDetection2D>(detections_[i]); } - ODDetection2D * ODDetections2D::at(unsigned int i) + shared_ptr<ODDetection2D> ODDetections2D::at(unsigned int i) { - return dynamic_cast<ODDetection2D *>(detections_[i]); + return dynamic_pointer_cast<ODDetection2D>(detections_[i]); } - ODDetection3D * ODDetections3D::operator[](unsigned int i) + shared_ptr<ODDetection3D> ODDetections3D::operator[](unsigned int i) { - return dynamic_cast<ODDetection3D *>(detections_[i]); + return dynamic_pointer_cast<ODDetection3D>(detections_[i]); } - ODDetection3D * ODDetections3D::at(unsigned int i) + shared_ptr<ODDetection3D> ODDetections3D::at(unsigned int i) { - return dynamic_cast<ODDetection3D *>(detections_[i]); + return dynamic_pointer_cast<ODDetection3D>(detections_[i]); } - ODDetectionComplete * ODDetectionsComplete::operator[](unsigned int i) + shared_ptr<ODDetectionComplete> ODDetectionsComplete::operator[](unsigned int i) { - return dynamic_cast<ODDetectionComplete *>(detections_[i]); + return dynamic_pointer_cast<ODDetectionComplete>(detections_[i]); } - ODDetectionComplete * ODDetectionsComplete::at(unsigned int i) + shared_ptr<ODDetectionComplete> ODDetectionsComplete::at(unsigned int i) { - return dynamic_cast <ODDetectionComplete *>(detections_[i]); + return dynamic_pointer_cast<ODDetectionComplete>(detections_[i]); } diff --git a/common/src/utils/ODDetector.cpp b/common/src/utils/ODDetector.cpp deleted file mode 100644 index 6aaa81d2..00000000 --- a/common/src/utils/ODDetector.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* -Copyright (c) 2015, Kripasindhu Sarkar -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions 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. - * Neither the name of the copyright holder(s) nor the - names of its contributors may 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 COPYRIGHT HOLDER 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. -*/ -// -// Created by sarkar on 13.08.15. -// - diff --git a/common/src/utils/ODFeatureDetector2D.cpp b/common/src/utils/ODFeatureDetector2D.cpp index 15beb754..2b886298 100644 --- a/common/src/utils/ODFeatureDetector2D.cpp +++ b/common/src/utils/ODFeatureDetector2D.cpp @@ -92,9 +92,9 @@ namespace od memcpy(siftImage, grey.data, image.rows * image.cols); } - void ODFeatureDetector2D::findSiftGPUDescriptors_(cv::Mat const &image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) + void ODFeatureDetector2D::findSiftGPUDescriptors_(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) { - unsigned char *data = image.data; + unsigned char * data = image.data; cv::Mat greyimage; if(image.type() != CV_8U) { cv::cvtColor(image, greyimage, cv::COLOR_BGR2GRAY); @@ -129,7 +129,7 @@ namespace od } - void ODFeatureDetector2D::findSiftGPUDescriptors(cv::Mat const &image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) + void ODFeatureDetector2D::findSiftGPUDescriptors(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) { unsigned char *data = image.data; cv::Mat greyimage; @@ -165,9 +165,9 @@ namespace od } - void ODFeatureDetector2D::findSiftGPUDescriptors(const char * image_name, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) + void ODFeatureDetector2D::findSiftGPUDescriptors(const std::string & image_name, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) { - sift_gpu_->RunSIFT(image_name); + sift_gpu_->RunSIFT(image_name.c_str()); int nFeat = sift_gpu_->GetFeatureNum();//get feature count //allocate memory for readback @@ -193,7 +193,7 @@ namespace od cv::Mat image = cv::imread(image_name); } - void ODFeatureDetector2D::computeAndSave(const cv::Mat & image, const std::string &path) + void ODFeatureDetector2D::computeAndSave(const cv::Mat & image, const std::string & path) { cv::Mat descriptors; std::vector<cv::KeyPoint> keypoints; diff --git a/common/src/utils/utils.cpp b/common/src/utils/utils.cpp index 18de04df..564d5799 100644 --- a/common/src/utils/utils.cpp +++ b/common/src/utils/utils.cpp @@ -24,7 +24,7 @@ namespace od { for (int r = 0; r < descriptors.rows; r++) { - float norm=0; + float norm = 0; for (size_t c = 0; c < descriptors.cols; c++) norm += (descriptors.at<float>(r, c)*descriptors.at<float>(r, c)); @@ -118,7 +118,7 @@ namespace od return ""; } - cv::Scalar getHashedColor(const std::string & name, int offset = 100) + cv::Scalar getHashedColor(const std::string & name, int offset) { boost::hash<std::string> string_hash; const int hashed = string_hash(name); diff --git a/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp b/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp index f68fe09e..58bbb040 100644 --- a/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp +++ b/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp @@ -33,9 +33,9 @@ namespace od void init(); - ODDetections * detect(ODScenePointCloud<PointT> * scene); + shared_ptr<ODDetections> detect(shared_ptr<ODScenePointCloud<PointT> > scene); - ODDetections3D * detectOmni(ODScenePointCloud<PointT> * scene); + shared_ptr<ODDetections3D> detectOmni(shared_ptr<ODScenePointCloud<PointT> > scene); int getNN() const { @@ -58,9 +58,10 @@ namespace od } protected: + int NN; std::string desc_name_; - boost::shared_ptr<pcl::rec_3d_framework::GlobalClassifier<pcl::PointXYZ> > global_; + shared_ptr<pcl::rec_3d_framework::GlobalClassifier<pcl::PointXYZ> > global_; }; @@ -68,16 +69,16 @@ namespace od void ODCADDetector3DGlobal<PointT>::init() { - boost::shared_ptr<pcl::rec_3d_framework::MeshSource<pcl::PointXYZ> > mesh_source(new pcl::rec_3d_framework::MeshSource<pcl::PointXYZ>); + shared_ptr<pcl::rec_3d_framework::MeshSource<pcl::PointXYZ> > mesh_source(new pcl::rec_3d_framework::MeshSource<pcl::PointXYZ>); mesh_source->setPath(this->training_input_location_); std::string training_dir_specific = this->getSpecificTrainingDataLocation(); mesh_source->setModelScale(1.f); mesh_source->generate(training_dir_specific); - boost::shared_ptr<pcl::rec_3d_framework::Source<pcl::PointXYZ> > cast_source; - cast_source = boost::static_pointer_cast<pcl::rec_3d_framework::MeshSource<pcl::PointXYZ> >(mesh_source); + shared_ptr<pcl::rec_3d_framework::Source<pcl::PointXYZ> > cast_source; + cast_source = dynamic_pointer_cast<pcl::rec_3d_framework::MeshSource<pcl::PointXYZ> >(mesh_source); - boost::shared_ptr<pcl::rec_3d_framework::PreProcessorAndNormalEstimator<pcl::PointXYZ, pcl::Normal> > normal_estimator; + shared_ptr<pcl::rec_3d_framework::PreProcessorAndNormalEstimator<pcl::PointXYZ, pcl::Normal> > normal_estimator; normal_estimator.reset(new pcl::rec_3d_framework::PreProcessorAndNormalEstimator<pcl::PointXYZ, pcl::Normal>); normal_estimator->setCMR(true); normal_estimator->setDoVoxelGrid(true); @@ -86,15 +87,15 @@ namespace od if(desc_name_.compare("vfh") == 0) { - boost::shared_ptr<pcl::rec_3d_framework::VFHEstimation<pcl::PointXYZ, pcl::VFHSignature308> > vfh_estimator; + shared_ptr<pcl::rec_3d_framework::VFHEstimation<pcl::PointXYZ, pcl::VFHSignature308> > vfh_estimator; vfh_estimator.reset(new pcl::rec_3d_framework::VFHEstimation<pcl::PointXYZ, pcl::VFHSignature308>); vfh_estimator->setNormalEstimator(normal_estimator); - boost::shared_ptr<pcl::rec_3d_framework::GlobalEstimator<pcl::PointXYZ, pcl::VFHSignature308> > cast_estimator; - cast_estimator = boost::dynamic_pointer_cast<pcl::rec_3d_framework::VFHEstimation<pcl::PointXYZ, pcl::VFHSignature308> >( + shared_ptr<pcl::rec_3d_framework::GlobalEstimator<pcl::PointXYZ, pcl::VFHSignature308> > cast_estimator; + cast_estimator = dynamic_pointer_cast<pcl::rec_3d_framework::VFHEstimation<pcl::PointXYZ, pcl::VFHSignature308> >( vfh_estimator); - boost::shared_ptr<pcl::rec_3d_framework::GlobalNNPipeline<flann::L1, pcl::PointXYZ, pcl::VFHSignature308> > global( + shared_ptr<pcl::rec_3d_framework::GlobalNNPipeline<flann::L1, pcl::PointXYZ, pcl::VFHSignature308> > global( new pcl::rec_3d_framework::GlobalNNPipeline<flann::L1, pcl::PointXYZ, pcl::VFHSignature308>()); global->setDataSource(cast_source); global->setTrainingDir(training_dir_specific); @@ -106,15 +107,15 @@ namespace od } else if(desc_name_.compare("cvfh") == 0) { - boost::shared_ptr<pcl::rec_3d_framework::CVFHEstimation<pcl::PointXYZ, pcl::VFHSignature308> > vfh_estimator; + shared_ptr<pcl::rec_3d_framework::CVFHEstimation<pcl::PointXYZ, pcl::VFHSignature308> > vfh_estimator; vfh_estimator.reset(new pcl::rec_3d_framework::CVFHEstimation<pcl::PointXYZ, pcl::VFHSignature308>); vfh_estimator->setNormalEstimator(normal_estimator); - boost::shared_ptr<pcl::rec_3d_framework::GlobalEstimator<pcl::PointXYZ, pcl::VFHSignature308> > cast_estimator; - cast_estimator = boost::dynamic_pointer_cast<pcl::rec_3d_framework::CVFHEstimation<pcl::PointXYZ, pcl::VFHSignature308> >( + shared_ptr<pcl::rec_3d_framework::GlobalEstimator<pcl::PointXYZ, pcl::VFHSignature308> > cast_estimator; + cast_estimator = dynamic_pointer_cast<pcl::rec_3d_framework::CVFHEstimation<pcl::PointXYZ, pcl::VFHSignature308> >( vfh_estimator); - boost::shared_ptr<pcl::rec_3d_framework::GlobalNNPipeline<Metrics::HistIntersectionUnionDistance, pcl::PointXYZ, pcl::VFHSignature308> > global( + shared_ptr<pcl::rec_3d_framework::GlobalNNPipeline<Metrics::HistIntersectionUnionDistance, pcl::PointXYZ, pcl::VFHSignature308> > global( new pcl::rec_3d_framework::GlobalNNPipeline<Metrics::HistIntersectionUnionDistance, pcl::PointXYZ, pcl::VFHSignature308>()); global->setDataSource(cast_source); global->setTrainingDir(training_dir_specific); @@ -125,14 +126,14 @@ namespace od global_ = global; } else if(desc_name_.compare("esf") == 0) { - boost::shared_ptr<pcl::rec_3d_framework::ESFEstimation<pcl::PointXYZ, pcl::ESFSignature640> > estimator; + shared_ptr<pcl::rec_3d_framework::ESFEstimation<pcl::PointXYZ, pcl::ESFSignature640> > estimator; estimator.reset(new pcl::rec_3d_framework::ESFEstimation<pcl::PointXYZ, pcl::ESFSignature640>); - boost::shared_ptr<pcl::rec_3d_framework::GlobalEstimator<pcl::PointXYZ, pcl::ESFSignature640> > cast_estimator; - cast_estimator = boost::dynamic_pointer_cast<pcl::rec_3d_framework::ESFEstimation<pcl::PointXYZ, pcl::ESFSignature640> >( + shared_ptr<pcl::rec_3d_framework::GlobalEstimator<pcl::PointXYZ, pcl::ESFSignature640> > cast_estimator; + cast_estimator = dynamic_pointer_cast<pcl::rec_3d_framework::ESFEstimation<pcl::PointXYZ, pcl::ESFSignature640> >( estimator); - boost::shared_ptr<pcl::rec_3d_framework::GlobalNNPipeline<flann::L1, pcl::PointXYZ, pcl::ESFSignature640> > global( + shared_ptr<pcl::rec_3d_framework::GlobalNNPipeline<flann::L1, pcl::PointXYZ, pcl::ESFSignature640> > global( new pcl::rec_3d_framework::GlobalNNPipeline<flann::L1, pcl::PointXYZ, pcl::ESFSignature640>()); global->setDataSource(cast_source); global->setTrainingDir(training_dir_specific); @@ -149,9 +150,9 @@ namespace od } template<typename PointT> - ODDetections3D * ODCADDetector3DGlobal<PointT>::detectOmni(ODScenePointCloud<PointT> * scene) + shared_ptr<ODDetections3D> ODCADDetector3DGlobal<PointT>::detectOmni(shared_ptr<ODScenePointCloud<PointT> > scene) { - ODDetections3D * detections = new ODDetections3D; + shared_ptr<ODDetections3D> detections = make_shared<ODDetections3D>(); typename pcl::PointCloud<PointT>::Ptr frame; @@ -201,7 +202,7 @@ namespace od //position at 3D identified! //now fill up the detection: - ODDetection3D *detection = new ODDetection3D; + shared_ptr<ODDetection3D> detection = make_shared<ODDetection3D>(); detection->setType(ODDetection::OD_DETECTION_CLASS); detection->setId(categories[0]); detection->setLocation(centroid); @@ -213,9 +214,9 @@ namespace od } template<typename PointT> - ODDetections * ODCADDetector3DGlobal<PointT>::detect(ODScenePointCloud<PointT> * scene) + shared_ptr<ODDetections> ODCADDetector3DGlobal<PointT>::detect(shared_ptr<ODScenePointCloud<PointT> > scene) { - ODDetections * detections = new ODDetections; + shared_ptr<ODDetections> detections = make_shared<ODDetections>(); typename pcl::PointCloud<PointT>::Ptr frame; float Z_DIST_ = 1.25f; @@ -238,7 +239,7 @@ namespace od std::string category = categories[0]; //now fill up the detection: - ODDetection * detection = new ODDetection3D(ODDetection::OD_DETECTION_CLASS, categories[0], conf[0]); + shared_ptr<ODDetection> detection = make_shared<ODDetection3D>(ODDetection::OD_DETECTION_CLASS, categories[0], conf[0]); detections->push_back(detection); return detections; diff --git a/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp b/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp index d90b0f3d..09604709 100644 --- a/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp +++ b/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp @@ -44,19 +44,20 @@ namespace od public: ODDetectorMultiAlgo2D(const std::string & training_data_location_) : ODDetector2D(training_data_location_){} - ODDetections * detect(ODSceneImage * scene) ; - ODDetections2D * detectOmni(ODSceneImage * scene); + shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene) ; + shared_ptr<ODDetections2D> detectOmni(shared_ptr<ODSceneImage> scene); - ODDetections * detect(ODScenePointCloud<PointT> * scene); - ODDetections3D * detectOmni(ODScenePointCloud<PointT> * scene); + shared_ptr<ODDetections> detect(shared_ptr<ODScenePointCloud<PointT> > scene); + shared_ptr<ODDetections3D> detectOmni(shared_ptr<ODScenePointCloud<PointT> > scene); void init(); private: - std::vector<ODDetector2D *> detectors_2d_; - std::vector<ODDetector3D<PointT> *> detectors_3d_; + std::vector<shared_ptr<ODDetector2D> > detectors_2d_; + std::vector<shared_ptr<ODDetector3D<PointT> > > detectors_3d_; + }; template<typename PointT> class ODDetectorMultiAlgo : public ODDetector @@ -65,28 +66,28 @@ namespace od public: ODDetectorMultiAlgo(const std::string & training_data_location_) : ODDetector(training_data_location_){} - ODDetections * detect(ODSceneImage * scene) ; - ODDetections2D * detectOmni(ODSceneImage * scene); + shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage > scene) ; + shared_ptr<ODDetections2D> detectOmni(shared_ptr<ODSceneImage > scene); - ODDetections * detect(ODScenePointCloud<PointT> * scene); - ODDetections3D * detectOmni(ODScenePointCloud<PointT> * scene); + shared_ptr<ODDetections> detect(shared_ptr<ODScenePointCloud<PointT> > scene); + shared_ptr<ODDetections3D> detectOmni(shared_ptr<ODScenePointCloud<PointT> > scene); void init(); private: - std::vector<ODDetector2D *> detectors_2d_; - std::vector<ODDetector3D<PointT> *> detectors_3d_; + std::vector<od::shared_ptr<ODDetector2D> > detectors_2d_; + std::vector<shared_ptr<ODDetector3D<PointT> > > detectors_3d_; }; //BASED ON 2D SCENE template<typename PointT> - ODDetections * ODDetectorMultiAlgo2D<PointT>::detect(ODSceneImage * scene) + shared_ptr<ODDetections> ODDetectorMultiAlgo2D<PointT>::detect(shared_ptr<ODSceneImage > scene) { - ODDetections * detections_all = new ODDetections; + shared_ptr<ODDetections> detections_all = make_shared<ODDetections>(); for (size_t i = 0; i < detectors_2d_.size(); ++i) { - ODDetections * detections_individual = detectors_2d_[i]->detect(scene); + shared_ptr<ODDetections> detections_individual = detectors_2d_[i]->detect(scene); detections_all->append(detections_individual); } @@ -94,12 +95,12 @@ namespace od } template<typename PointT> - ODDetections2D * ODDetectorMultiAlgo2D<PointT>::detectOmni(ODSceneImage * scene) + shared_ptr<ODDetections2D> ODDetectorMultiAlgo2D<PointT>::detectOmni(shared_ptr<ODSceneImage > scene) { - ODDetections2D * detections_all = new ODDetections2D; + shared_ptr<ODDetections2D> detections_all = make_shared<ODDetections2D>(); for (size_t i = 0; i < detectors_2d_.size(); ++i) { - ODDetections2D * detections_individual = detectors_2d_[i]->detectOmni(scene); + shared_ptr<ODDetections2D> detections_individual = detectors_2d_[i]->detectOmni(scene); detections_all->append(detections_individual); } @@ -134,12 +135,12 @@ namespace od } template<typename PointT> - ODDetections * ODDetectorMultiAlgo<PointT>::detect(ODScenePointCloud<PointT> * scene) + shared_ptr<ODDetections> ODDetectorMultiAlgo<PointT>::detect(shared_ptr<ODScenePointCloud<PointT> > scene) { - ODDetections * detections_all = new ODDetections; + shared_ptr<ODDetections> detections_all = make_shared<ODDetections>(); for(size_t i = 0; i < detectors_3d_.size(); ++i) { - ODDetections * detections_individual = detectors_3d_[i]->detect(scene); + shared_ptr<ODDetections> detections_individual = detectors_3d_[i]->detect(scene); detections_all->append(detections_individual); } @@ -147,12 +148,12 @@ namespace od } template<typename PointT> - ODDetections3D * ODDetectorMultiAlgo<PointT>::detectOmni(ODScenePointCloud<PointT> * scene) + shared_ptr<ODDetections3D> ODDetectorMultiAlgo<PointT>::detectOmni(shared_ptr<ODScenePointCloud<PointT> > scene) { - ODDetections3D * detections_all = new ODDetections3D; + shared_ptr<ODDetections3D> detections_all = make_shared<ODDetections3D>; for(size_t i = 0; i < detectors_3d_.size(); i++) { - ODDetections3D * detections_individual = detectors_3d_[i]->detectOmni(scene); + shared_ptr<ODDetections3D> detections_individual = detectors_3d_[i]->detectOmni(scene); detections_all->append(detections_individual); } diff --git a/detectors/include/od/detectors/global2D/ODFaceRecognizer.h b/detectors/include/od/detectors/global2D/ODFaceRecognizer.h index dcea7e59..9c3330ea 100644 --- a/detectors/include/od/detectors/global2D/ODFaceRecognizer.h +++ b/detectors/include/od/detectors/global2D/ODFaceRecognizer.h @@ -71,7 +71,7 @@ namespace od int train(); - ODDetections * detect(ODSceneImage * scene); + shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene); const FaceRecogType & getRecogtype() const @@ -105,6 +105,7 @@ namespace od } protected: + cv::Ptr<cv::face::FaceRecognizer> cv_recognizer_; FaceRecogType recog_type_; @@ -115,7 +116,8 @@ namespace od private: - void read_csv(const std::string & file_name, std::vector<cv::Mat> & images, std::vector<int> & labels, char separator = ';'); + + void read_csv(const std::string & file_name, std::vector<cv::Mat> & images, std::vector<int> & labels, const std::string & separator = ';'); }; /** \example objectdetector/od_image_facerecog.cpp diff --git a/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h b/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h index e3e6b892..172b654d 100644 --- a/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h +++ b/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h @@ -56,11 +56,11 @@ namespace od void init(); - ODDetections2D * detectOmni(ODSceneImage *scene); - ODDetections * detect(ODSceneImage *scene); + shared_ptr<ODDetections2D> detectOmni(shared_ptr<ODSceneImage> scene); + shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene); private: - std::shared_ptr<cv::CascadeClassifier> haar_cascade_; + shared_ptr<cv::CascadeClassifier> haar_cascade_; double scale_factor_; int min_neighbors_; diff --git a/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h b/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h index 93f1cdbd..6be1f141 100644 --- a/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h +++ b/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h @@ -76,10 +76,10 @@ namespace od void setSVMDetector(std::vector<float> svm_detector); - ODDetections2D * detectOmni(ODSceneImage * scene); - ODDetections * detect(ODSceneImage * scene); + shared_ptr<ODDetections2D> detectOmni(shared_ptr<ODSceneImage> scene); + shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene); - int detect(ODScene * scene, std::vector<ODDetection *> & detections); + int detect(shared_ptr<ODScene> scene, std::vector<shared_ptr<ODDetection> > & detections); void setTrainedDataLocation(const std::string & trained_data_location); diff --git a/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h b/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h index f92e26d4..90e345c0 100644 --- a/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h +++ b/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h @@ -27,9 +27,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 13.08.15. // #pragma once -#include <opencv2/objdetect.hpp> +//#include <opencv2/objdetect.hpp> #include "od/common/pipeline/ODTrainer.h" -#include "od/common/utils/utils.h" +//#include "od/common/utils/utils.h" namespace od { @@ -48,6 +48,7 @@ namespace od { public: + ODHOGTrainer(const std::string & training_input_location_ = "", const std::string & trained_data_location_ = "", const cv::Size & win_size = cv::Size(64,128), const cv::Size & block_size = cv::Size(16,16), const cv::Size & block_stride = cv::Size(8,8), const cv::Size & cell_size = cv::Size(8,8), float hits_threshold = 0.0); @@ -219,6 +220,7 @@ namespace od std::string svm_model_hard_; std::string descriptor_vector_file_; std::string descriptor_vector_hard_; + }; /** \example objectdetector/od_hog_train.cpp */ diff --git a/detectors/include/od/detectors/global3D/ODPointCloudGlobalMatching.h b/detectors/include/od/detectors/global3D/ODPointCloudGlobalMatching.h index b8ea2fc1..a7755902 100644 --- a/detectors/include/od/detectors/global3D/ODPointCloudGlobalMatching.h +++ b/detectors/include/od/detectors/global3D/ODPointCloudGlobalMatching.h @@ -27,9 +27,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 16.06.15. // #pragma once - -#include "od/common/pipeline/ODDetector.h" -#include "od/common/pipeline/ODTrainer.h" #include "od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h" #include "od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp" @@ -49,13 +46,13 @@ namespace od void init(){} int train(); - int detect(ODScene * scene, std::vector<ODDetection *> detections); + int detect(shared_ptr<ODScene> scene, std::vector<shared_ptr<ODDetection>> & detections); protected: std::string desc_name_; - ODCADDetectTrainer3DGlobal * trainer_; - ODCADDetector3DGlobal<pcl::PointXYZ> * detector_; + shared_ptr<ODCADDetectTrainer3DGlobal> trainer_; + shared_ptr<ODCADDetector3DGlobal<pcl::PointXYZ> > detector_; }; } } diff --git a/detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h b/detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h index b410424b..5189aad7 100644 --- a/detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h +++ b/detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h @@ -27,8 +27,18 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 16.06.15. // #pragma once -#include "od/common/pipeline/ODTrainer.h" #include <iostream> +#include <pcl/pcl_macros.h> +#include <pcl/apps/3d_rec_framework/pipeline/global_nn_classifier.h> +#include <pcl/apps/3d_rec_framework/pc_source/mesh_source.h> +#include <pcl/apps/3d_rec_framework/feature_wrapper/global/vfh_estimator.h> +#include <pcl/apps/3d_rec_framework/feature_wrapper/global/esf_estimator.h> +#include <pcl/apps/3d_rec_framework/feature_wrapper/global/cvfh_estimator.h> +#include <pcl/apps/3d_rec_framework/utils/metrics.h> +#include <pcl/visualization/pcl_visualizer.h> +#include <pcl/apps/dominant_plane_segmentation.h> +#include <pcl/console/parse.h> +#include "od/common/pipeline/ODTrainer.h" namespace od { diff --git a/detectors/include/od/detectors/local2D/ODImageLocalMatching.h b/detectors/include/od/detectors/local2D/ODImageLocalMatching.h index b5526ba6..21a0d0a1 100644 --- a/detectors/include/od/detectors/local2D/ODImageLocalMatching.h +++ b/detectors/include/od/detectors/local2D/ODImageLocalMatching.h @@ -47,11 +47,7 @@ namespace od { public: - ODImageLocalMatchingTrainer(std::string const &training_input_location_, std::string const &training_data_location_) : ODTrainer(training_input_location_, training_data_location_) - { - TRAINED_LOCATION_DENTIFIER_ = "FEATCORR"; - TRAINED_DATA_ID_ = "corr.xml"; - } + ODImageLocalMatchingTrainer(const std::string & training_input_location_, const std::string & training_data_location_); }; /** \brief ODImageLocalMatchingDetector @@ -63,11 +59,8 @@ namespace od { public: - ODImageLocalMatchingDetector(std::string const &training_data_location_) : ODDetector2DComplete(training_data_location_) - { - TRAINED_LOCATION_DENTIFIER_ = "FEATCORR"; - TRAINED_DATA_ID_ = ".xml"; - } + ODImageLocalMatchingDetector(const std::string & training_data_location_); + }; /** \brief ODImageLocalMatching @@ -80,52 +73,28 @@ namespace od public: - ODImageLocalMatchingTrainer *getTrainer() const - { - return trainer_; - } - - void setTrainer(ODImageLocalMatchingTrainer *trainer_) - { - ODImageLocalMatching::trainer_ = trainer_; - } + shared_ptr<ODImageLocalMatchingTrainer> getTrainer() const; - ODImageLocalMatchingDetector *getDetector() const - { - return detector_; - } + void setTrainer(shared_ptr<ODImageLocalMatchingTrainer> trainer_); - void setDetector(ODImageLocalMatchingDetector *detector_) - { - ODImageLocalMatching::detector_ = detector_; - } + shared_ptr<ODImageLocalMatchingDetector> getDetector() const; + void setDetector(shared_ptr<ODImageLocalMatchingDetector> detector_); - ODImageLocalMatching() - { - TRAINED_DATA_EXT_ = "corr.xml"; - } - void init() - { } + ODImageLocalMatching(); + void init() {} - int train() - { - return trainer_->train(); - } - int detect(ODScene *scene, std::vector<ODDetection *> detections) - { - //detector_->detect(scene, detections); - return 1; - } + int train(); + int detect(shared_ptr<ODScene> scene, std::vector<shared_ptr<ODDetection> > detections); protected: - ODImageLocalMatchingTrainer * trainer_; - ODImageLocalMatchingDetector * detector_; + shared_ptr<ODImageLocalMatchingTrainer> trainer_; + shared_ptr<ODImageLocalMatchingDetector> detector_; }; diff --git a/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h b/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h index 15c980c4..4b946f77 100644 --- a/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h +++ b/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h @@ -34,13 +34,25 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/common/pipeline/ODScene.h" #include "od/common/utils/utils.h" #include "od/common/utils/ODFeatureDetector2D.h" +#include "od/detectors/local2D/simple_ransac_detection/Mesh.h" +#include "od/detectors/local2D/simple_ransac_detection/Model.h" +#include "od/detectors/local2D/simple_ransac_detection/PnPProblem.h" +#include "od/detectors/local2D/simple_ransac_detection/RobustMatcher.h" +#include "od/detectors/local2D/simple_ransac_detection/ModelRegistration.h" +#include "od/detectors/local2D/simple_ransac_detection/Utils.h" +#include "od/detectors/local2D/simple_ransac_detection/Utils.h" #include <iostream> + #include <opencv2/core/core.hpp> #include <opencv2/core/utility.hpp> -#include <opencv2/calib3d.hpp> +#include <opencv2/highgui/highgui.hpp> +#include <opencv2/imgproc/imgproc.hpp> +#include <opencv2/calib3d/calib3d.hpp> +#include <opencv2/video/tracking.hpp> +#include <opencv2/xfeatures2d.hpp> +#include <opencv2/viz.hpp> -#include "od/detectors/local2D/simple_ransac_detection/Utils.h" namespace od { @@ -104,12 +116,12 @@ namespace od void init(); - ODDetections * detect(ODSceneImage * scene); - ODDetections3D * detectOmni(ODSceneImage * scene); + shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene); + shared_ptr<ODDetections3D> detectOmni(shared_ptr<ODSceneImage> scene); protected: - bool detectSingleModel(ODSceneImage * scene, const Model & model, ODDetection3D * detection3D, const cv::Mat & frame_viz); + bool detectSingleModel(shared_ptr<ODSceneImage> scene, const Model & model, shared_ptr<ODDetection3D> & detection3D, const cv::Mat & frame_viz); std::string camera_intrinsic_file_; @@ -141,7 +153,7 @@ namespace od std::vector<Model> models_; PnPProblem pnp_detection_; std::string f_type_default_; - std::shared_ptr<ODFeatureDetector2D> feature_detector_; + shared_ptr<ODFeatureDetector2D> feature_detector_; }; /** \example objectdetector/od_image_cadrecog_camera.cpp diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvReader.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvReader.h index 01741c44..1f8cf31e 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvReader.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvReader.h @@ -21,7 +21,7 @@ namespace od { * @param separator - The separator character between words per line * @return */ - CsvReader(const std::string & path, const char & separator = ' '); + CsvReader(const std::string & path, const std::string & separator = ' '); /** * Read a plane text file with .ply format @@ -34,9 +34,9 @@ namespace od { private: /** The current stream file for the reader */ - std::ifstream _file_; + std::ifstream file_; /** The separator character between words for each line */ - char _separator_; + std::string separator_; }; } diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvWriter.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvWriter.h index 707c371f..3d249ea6 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvWriter.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvWriter.h @@ -17,9 +17,11 @@ namespace od { void writeUVXYZ(const std::vector<cv::Point3f> & list_points3d, const std::vector<cv::Point2f> & list_points2d, const cv::Mat & descriptors); private: - std::ofstream _file_; - std::string _separator_; - bool _is_first_term_; + + std::ofstream file_; + std::string separator_; + bool is_first_term_; + }; } diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/PnPProblem.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/PnPProblem.h index 6295b387..2a03bae8 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/PnPProblem.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/PnPProblem.h @@ -35,8 +35,8 @@ namespace od { cv::Point3f getNearest3DPoint(std::vector<cv::Point3f> & points_list, const cv::Point3f & origin); bool backproject2DPoint(const Mesh * mesh, const cv::Point2f & point2d, cv::Point3f & point3d); - bool intersectMollerTrumbore(Ray & ray, Triangle & Triangle, double * out); - std::vector<cv::Point2f> verifyPoints(Mesh * mesh); + bool intersectMollerTrumbore(Ray & ray, Triangle & Triangle, shared_ptr<double> out); + std::vector<cv::Point2f> verifyPoints(shared_ptr<Mesh> mesh); cv::Point2f backproject3DPoint(const cv::Point3f & point3d); bool estimatePose(const std::vector<cv::Point3f> & list_points3d, const std::vector<cv::Point2f> & list_points2d, int flags); void estimatePoseRANSAC(const std::vector<cv::Point3f> & list_points3d, const std::vector<cv::Point2f> & list_points2d, diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/RobustMatcher.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/RobustMatcher.h index 1723fd38..97b17b00 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/RobustMatcher.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/RobustMatcher.h @@ -39,7 +39,7 @@ namespace od { void setDescriptorMatcher(const cv::Ptr<cv::DescriptorMatcher> & match) { matcher_ = match; } // Compute the keypoints of an image - void computeKeyPoints( const cv::Mat& image, std::vector<cv::KeyPoint> & keypoints); + void computeKeyPoints( const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints); // Compute the descriptors of an image given its keypoints void computeDescriptors( const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints, cv::Mat & descriptors); @@ -68,8 +68,8 @@ namespace od { std::vector<cv::KeyPoint> & keypoints_frame, const cv::Mat & descriptors_model ); - void findFeatureAndMatch(cv::Mat const & frame, std::vector<cv::DMatch> & good_matches, std::vector<cv::KeyPoint> & keypoints_frame, - cv::Mat const & descriptors_model); + void findFeatureAndMatch(const cv::Mat & frame, std::vector<cv::DMatch> & good_matches, std::vector<cv::KeyPoint> & keypoints_frame, + const cv::Mat & descriptors_model); void match(const cv::Mat & descriptors_frame, const cv::Mat & descriptors_model, std::vector<cv::DMatch> & good_matches); @@ -80,8 +80,8 @@ namespace od { private: - void instantiateMatcher(Model const & feature_type, bool use_gpu); - void instantiateMatcher1(Model const & model, bool use_gpu); + void instantiateMatcher(const Model & feature_type, bool use_gpu); + void instantiateMatcher1(const Model & model, bool use_gpu); // pointer to the feature point detector object cv::Ptr<od::ODFeatureDetector2D> featureDetector_; diff --git a/detectors/src/global2D/ODFaceRecognizer.cpp b/detectors/src/global2D/ODFaceRecognizer.cpp index a5cec425..9ed58df7 100644 --- a/detectors/src/global2D/ODFaceRecognizer.cpp +++ b/detectors/src/global2D/ODFaceRecognizer.cpp @@ -105,7 +105,7 @@ namespace od im_height_ = images[0].rows; } - ODDetections * ODFaceRecognizer::detect(ODSceneImage * scene) + shared_ptr<ODDetections> ODFaceRecognizer::detect(ODSceneImage * scene) { cv::Mat face_edited; cv::cvtColor(scene->getCVImage(), face_edited, CV_BGR2GRAY); @@ -120,14 +120,14 @@ namespace od cv_recognizer_->predict(face_edited, label, confidence); //fill in the detection - ODDetection2D * detection = new ODDetection2D(ODDetection::OD_DETECTION_CLASS, std::to_string(label), confidence); - ODDetections2D * detections = new ODDetections2D; + shared_ptr<ODDetection2D> detection = make_shared<ODDetection2D>(ODDetection::OD_DETECTION_CLASS, std::to_string(label), confidence); + shared_ptr<ODDetections2D> detections = make_shared<ODDetections2D>(); detections->push_back(detection); return detections; } - void ODFaceRecognizer::read_csv(const std::string & filename, std::vector<cv::Mat> & images, std::vector<int> & labels, char separator) + void ODFaceRecognizer::read_csv(const std::string & filename, std::vector<cv::Mat> & images, std::vector<int> & labels, const std::string & separator) { std::ifstream file(filename.c_str(), std::ifstream::in); if(!file) @@ -139,7 +139,7 @@ namespace od while(getline(file, line)) { std::stringstream liness(line); - getline(liness, path, separator); + getline(liness, path, separator.c_str()); getline(liness, classlabel); if(!path.empty() && !classlabel.empty()) { diff --git a/detectors/src/global2D/detection/ODCascadeDetector.cpp b/detectors/src/global2D/detection/ODCascadeDetector.cpp index 7f09fc59..21361293 100644 --- a/detectors/src/global2D/detection/ODCascadeDetector.cpp +++ b/detectors/src/global2D/detection/ODCascadeDetector.cpp @@ -47,11 +47,10 @@ namespace od void ODCascadeDetector::init() { - haar_cascade_ = std::make_shared<cv::CascadeClassifier>(fileutils::getFirstFile(getSpecificTrainingDataLocation(), - TRAINED_DATA_ID_)); + haar_cascade_ = make_shared<cv::CascadeClassifier>(fileutils::getFirstFile(getSpecificTrainingDataLocation(), TRAINED_DATA_ID_)); } - ODDetections2D * ODCascadeDetector::detectOmni(ODSceneImage * scene) + shared_ptr<ODDetections2D> ODCascadeDetector::detectOmni(shared_ptr<ODSceneImage> scene) { cv::Mat gray; cv::cvtColor(scene->getCVImage(), gray, CV_BGR2GRAY); @@ -60,7 +59,7 @@ namespace od haar_cascade_->detectMultiScale(gray, faces, scale_factor_, min_neighbors_, 0, min_size_, max_size_); //always create detections - ODDetections2D * detections = new ODDetections2D; + shared_ptr<ODDetections2D> detections = make_shared<ODDetections2D>(); cv::Mat viz = scene->getCVImage().clone(); cv::Rect face_i; for(size_t i = 0; i < faces.size(); ++i) @@ -68,7 +67,7 @@ namespace od // Process face by face: face_i = faces[i]; - ODDetection2D * detection2D = new ODDetection2D(ODDetection::OD_DETECTION_CLASS, "FACE", 1); + shared_ptr<ODDetection2D> detection2D = make_shared<ODDetection2D>(ODDetection::OD_DETECTION_CLASS, "FACE", 1); detection2D->setBoundingBox(face_i); detections->push_back(detection2D); @@ -82,10 +81,10 @@ namespace od return detections; } - ODDetections * ODCascadeDetector::detect(ODSceneImage * scene) + shared_ptr<ODDetections> ODCascadeDetector::detect(shared_ptr<ODSceneImage> scene) { //always create detections - ODDetections * detections = new ODDetections; + shared_ptr<ODDetections> detections = make_shared<ODDetections>(); cv::Mat gray; cv::cvtColor(scene->getCVImage(), gray, CV_BGR2GRAY); @@ -98,7 +97,7 @@ namespace od haar_cascade_->detectMultiScale(gray, faces, 5, min_neighbors_, 0, gray.size(), gray.size()); if(faces.size() > 0) { - detections->push_back(new ODDetection(ODDetection::OD_DETECTION_CLASS, "FACE", 1)); + detections->push_back(make_shared<ODDetection>(ODDetection::OD_DETECTION_CLASS, "FACE", 1)); } return detections; } diff --git a/detectors/src/global2D/detection/ODHOGDetector.cpp b/detectors/src/global2D/detection/ODHOGDetector.cpp index 8c276afe..34efe227 100644 --- a/detectors/src/global2D/detection/ODHOGDetector.cpp +++ b/detectors/src/global2D/detection/ODHOGDetector.cpp @@ -37,8 +37,8 @@ namespace od { ODHOGDetector::ODHOGDetector(const std::string & trained_data_location_ = "", const cv::Size & win_size = cv::Size(64,128), - const cv::Size & block_size = cv::Size(16,16), const cv::Size & block_stride = cv::Size(8,8), const cv::Size & cell_size = cv::Size(8,8), - float hit_threshold = 0.0) + const cv::Size & block_size = cv::Size(16,16), const cv::Size & block_stride = cv::Size(8,8), + const cv::Size & cell_size = cv::Size(8,8), float hit_threshold = 0.0) { TRAINED_LOCATION_DENTIFIER_ = "HOG"; TRAINED_DATA_ID_ = "hog.xml"; @@ -90,10 +90,10 @@ namespace od hog_.setSVMDetector(svm_detector); } - ODDetections2D * ODHOGDetector::detectOmni(ODSceneImage * scene) + shared_ptr<ODDetections2D> ODHOGDetector::detectOmni(ODSceneImage * scene) { //always create a detection - ODDetections2D * detections = new ODDetections2D; + shared_ptr<ODDetections2D> detections = make_shared<ODDetections2D>(); vector<cv::Rect> found, found_filtered; hog_.detectMultiScale(scene->getCVImage(), found, hit_threshold, cv::Size(8, 8), cv::Size(32, 32), 1.05, 2); @@ -101,7 +101,7 @@ namespace od cv::Mat viz = scene->getCVImage().clone(); for(int i = 0; i < found.size(); i++) { - ODDetection2D * detection2D = new ODDetection2D; + shared_ptr<ODDetection2D> detection2D = make_shared<ODDetection2D>; detection2D->setBoundingBox(found[i]); detection2D->setId("PEOPLE"); detection2D->setType(ODDetection::OD_DETECTION_CLASS); @@ -122,10 +122,10 @@ namespace od return detections; } - ODDetections * ODHOGDetector::detect(ODSceneImage * scene) + shared_ptr<ODDetections> ODHOGDetector::detect(shared_ptr<ODSceneImage> scene) { //always create a detection - ODDetections * detections = new ODDetections; + shared_ptr<ODDetections> detections = make_shared<ODDetections>; cv::Mat scaled_window; cv::resize(scene->getCVImage(), scaled_window, hog_.win_size); @@ -133,9 +133,9 @@ namespace od std::vector<cv::Point> found_locations; hog_.detect(scene->getCVImage(), found_locations, hitThreshold); - if (!found_locations.empty()) + if(!found_locations.empty()) { - ODDetection2D * detection2D = new ODDetection2D; + shared_ptr<ODDetection2D> detection2D = make_shared<ODDetection2D>; detection2D->setId("PEOPLE"); detection2D->setType(ODDetection::OD_DETECTION_CLASS); detections->push_back(detection2D); @@ -162,7 +162,7 @@ namespace od const cv::Size & ODHOGDetector::getWinSize() const { - return win_size; + return win_size_; } void ODHOGDetector::setWinSize(const cv::Size & win_size) @@ -182,27 +182,27 @@ namespace od const cv::Size & getBlockStride() const { - return block_stride; + return block_stride_; } void ODHOGDetector::setBlockStride(const cv::Size & block_stride) { - ODHOGDetector::blockStride = blockStride; + block_stride_ = block_stride; } const cv::Size & ODHOGDetector::getCellSize() const { - return cell_size; + return cell_size_; } void ODHOGDetector::setCellSize(const cv::Size & cell_size) { - ODHOGDetector::cellSize = cellSize; + cell_size_ = cell_size; } float ODHOGDetector::getHitThreshold() const { - return hit_threshold; + return hit_threshold_; } void ODHOGDetector::setHitThreshold(float hit_threshold) diff --git a/detectors/src/global3D/CMakeLists.txt b/detectors/src/global3D/CMakeLists.txt index e4c8be8c..afa86fb1 100644 --- a/detectors/src/global3D/CMakeLists.txt +++ b/detectors/src/global3D/CMakeLists.txt @@ -9,7 +9,6 @@ if(build) set(SOURCES "ODPointCloudGlobalMatching.cpp" - "detection/ODCADDetector3DGlobal.cpp" "training/ODCADDetectTrainer3DGlobal.cpp" ) diff --git a/detectors/src/global3D/ODPointCloudGlobalMatching.cpp b/detectors/src/global3D/ODPointCloudGlobalMatching.cpp index b90737cd..aa796769 100644 --- a/detectors/src/global3D/ODPointCloudGlobalMatching.cpp +++ b/detectors/src/global3D/ODPointCloudGlobalMatching.cpp @@ -29,17 +29,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/global3D/ODPointCloudGlobalMatching.h" - namespace od { namespace g3d { - int ODPointCloudGlobalMatching ::train() + int ODPointCloudGlobalMatching::train() { return trainer_->train(); } - int ODPointCloudGlobalMatching::detect(ODScene * scene, std::vector<ODDetection *> detections) + int ODPointCloudGlobalMatching::detect(shared_ptr<ODScene> scene, std::vector<shared_ptr<ODDetection>> & detections) { //detector_->detect(scene, detections); return 0; diff --git a/detectors/src/global3D/detection/ODCADDetector3DGlobal.cpp b/detectors/src/global3D/detection/ODCADDetector3DGlobal.cpp deleted file mode 100644 index aa20819c..00000000 --- a/detectors/src/global3D/detection/ODCADDetector3DGlobal.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* -Copyright (c) 2015, Kripasindhu Sarkar -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions 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. - * Neither the name of the copyright holder(s) nor the - names of its contributors may 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 COPYRIGHT HOLDER 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. -*/// -// Created by sarkar on 16.06.15. -// - -#include "od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp" diff --git a/detectors/src/global3D/training/ODCADDetectTrainer3DGlobal.cpp b/detectors/src/global3D/training/ODCADDetectTrainer3DGlobal.cpp index cc8f2241..48da14ef 100644 --- a/detectors/src/global3D/training/ODCADDetectTrainer3DGlobal.cpp +++ b/detectors/src/global3D/training/ODCADDetectTrainer3DGlobal.cpp @@ -29,23 +29,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h" -#include <pcl/pcl_macros.h> -#include <pcl/apps/3d_rec_framework/pipeline/global_nn_classifier.h> -#include <pcl/apps/3d_rec_framework/pc_source/mesh_source.h> -#include <pcl/apps/3d_rec_framework/feature_wrapper/global/vfh_estimator.h> -#include <pcl/apps/3d_rec_framework/feature_wrapper/global/esf_estimator.h> -#include <pcl/apps/3d_rec_framework/feature_wrapper/global/cvfh_estimator.h> -#include <pcl/apps/3d_rec_framework/utils/metrics.h> -#include <pcl/visualization/pcl_visualizer.h> -#include <pcl/apps/dominant_plane_segmentation.h> -#include <pcl/console/parse.h> - namespace od { namespace g3d { - ODCADDetectTrainer3DGlobal::ODCADDetectTrainer3DGlobal(const std::string & training_input_location_, const std::string & training_data_location_) : - ODTrainer(training_input_location_, training_data_location_) + ODCADDetectTrainer3DGlobal::ODCADDetectTrainer3DGlobal(const std::string & training_input_location_, const std::string & training_data_location_): + ODTrainer(training_input_location_, training_data_location_) { desc_name_ = "esf"; TRAINED_LOCATION_DENTIFIER_ = "GLOBAL3DVFH"; @@ -54,7 +43,8 @@ namespace od { int ODCADDetectTrainer3DGlobal::train() { - boost::shared_ptr<pcl::rec_3d_framework::MeshSource<pcl::PointXYZ> > mesh_source(new pcl::rec_3d_framework::MeshSource<pcl::PointXYZ>); + shared_ptr<pcl::rec_3d_framework::MeshSource<pcl::PointXYZ> > mesh_source(new pcl::rec_3d_framework::MeshSource<pcl::PointXYZ>()); + mesh_source->setPath(training_input_location_); mesh_source->setResolution(150); mesh_source->setTesselationLevel(1); @@ -63,6 +53,7 @@ namespace od { mesh_source->setModelScale(1.f); std::string location = getSpecificTrainingDataLocation(); mesh_source->generate(location); + return 1; } diff --git a/detectors/src/local2D/ODImageLocalMatching.cpp b/detectors/src/local2D/ODImageLocalMatching.cpp index b09fe256..60660086 100644 --- a/detectors/src/local2D/ODImageLocalMatching.cpp +++ b/detectors/src/local2D/ODImageLocalMatching.cpp @@ -29,3 +29,63 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // #include "od/detectors/local2D/ODImageLocalMatching.h" + +namespace od +{ + + namespace l2d + { + + ODImageLocalMatchingTrainer::ODImageLocalMatchingTrainer(const std::string & training_input_location_, + const std::string & training_data_location_) : + ODTrainer(training_input_location_, training_data_location_) + { + TRAINED_LOCATION_DENTIFIER_ = "FEATCORR"; + TRAINED_DATA_ID_ = "corr.xml"; + } + + ODImageLocalMatchingDetector::ODImageLocalMatchingDetector(const std::string & training_data_location_) : + ODDetector2DComplete(training_data_location_) + { + TRAINED_LOCATION_DENTIFIER_ = "FEATCORR"; + TRAINED_DATA_ID_ = ".xml"; + } + + + shared_ptr<ODImageLocalMatchingTrainer> ODImageLocalMatching::getTrainer() const + { + return trainer_; + } + + void ODImageLocalMatching::setTrainer(shared_ptr<ODImageLocalMatchingTrainer> trainer_) + { + ODImageLocalMatching::trainer_ = trainer_; + } + + shared_ptr<ODImageLocalMatchingDetector> ODImageLocalMatching::getDetector() const + { + return detector_; + } + + void ODImageLocalMatching:: setDetector(shared_ptr<ODImageLocalMatchingDetector> detector_) + { + ODImageLocalMatching::detector_ = detector_; + } + + ODImageLocalMatching::ODImageLocalMatching() + { + TRAINED_DATA_EXT_ = "corr.xml"; + } + + int ODImageLocalMatching::train() + { + return trainer_->train(); + } + + int ODImageLocalMatching::detect(shared_ptr<ODScene> scene, std::vector<shared_ptr<ODDetection> > detections) + { + //detector_->detect(scene, detections); + return 1; + } + } +} \ No newline at end of file diff --git a/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp b/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp index 2f33376b..8887a735 100644 --- a/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp +++ b/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp @@ -30,25 +30,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/local2D/detection/ODCADRecognizer2DLocal.h" -#include <opencv2/highgui/highgui.hpp> -#include <opencv2/imgproc/imgproc.hpp> -#include <opencv2/calib3d/calib3d.hpp> -#include <opencv2/video/tracking.hpp> -#include <opencv2/xfeatures2d.hpp> -#include <opencv2/viz.hpp> -#include <sys/time.h> -#include "od/common/utils/utils.h" - -// PnP Tutorial -#include "od/detectors/local2D/simple_ransac_detection/Mesh.h" -#include "od/detectors/local2D/simple_ransac_detection/Model.h" -#include "od/detectors/local2D/simple_ransac_detection/PnPProblem.h" -#include "od/detectors/local2D/simple_ransac_detection/RobustMatcher.h" -#include "od/detectors/local2D/simple_ransac_detection/ModelRegistration.h" -#include "od/detectors/local2D/simple_ransac_detection/Utils.h" - -//using namespace cv::xfeatures2d; - namespace od { namespace l2d @@ -80,7 +61,7 @@ namespace od pnp_method_ = cv::SOLVEPNP_EPNP; f_type_default_ = "SIFT"; - feature_detector_ = std::make_shared<ODFeatureDetector2D>(f_type_default_, use_gpu_); + feature_detector_ = make_shared<ODFeatureDetector2D>(f_type_default_, use_gpu_); } const std::string & ODCADRecognizer2DLocal::getCameraIntrinsicFile() const @@ -262,7 +243,7 @@ namespace od // get all trained models fileutils::getFilesInDirectoryRec(getSpecificTrainingDataLocation(), TRAINED_DATA_ID_, model_names_); - for(int i = 0; i < model_names_.size(); i++) + for(size_t i = 0; i < model_names_.size(); ++i) { Model model; model.loadNewXml(model_names_[i]); @@ -271,19 +252,20 @@ namespace od if(models_.size() > 0) f_type_default_ = models_[0].f_type_; - feature_detector_ = std::make_shared<ODFeatureDetector2D>(f_type_default_, use_gpu_); + feature_detector_ = make_shared<ODFeatureDetector2D>(f_type_default_, use_gpu_); } - ODDetections * ODCADRecognizer2DLocal::detect(ODSceneImage * scene) + shared_ptr<ODDetections> ODCADRecognizer2DLocal::detect(shared_ptr<ODSceneImage> scene) { - ODDetections3D * detections = detectOmni(scene); - return detections; + shared_ptr<ODDetections3D> detections = detectOmni(scene); + return dynamic_pointer_cast<ODDetections>(detections); } - bool ODCADRecognizer2DLocal::detectSingleModel(ODSceneImage * scene, const Model & model, ODDetection3D * detection3D, const cv::Mat & frame_vis) + bool ODCADRecognizer2DLocal::detectSingleModel(shared_ptr<ODSceneImage> scene, const Model & model, shared_ptr<ODDetection3D> & detection3D, + const cv::Mat & frame_vis) { //reset @@ -339,7 +321,8 @@ namespace od std::cout << "RECOGNIZED: " << model.id_ << std::endl; //else everything is fine; report the detection - detection3D = new ODDetection3D(); + if(!detection3D) + detection3D = make_shared<ODDetection3D>(); detection3D->setLocation(pnp_detection_.getTMatrix()); detection3D->setPose(pnp_detection_.getRMatrix()); detection3D->setType(ODDetection::OD_DETECTION_RECOG); @@ -348,7 +331,7 @@ namespace od return true; } - ODDetections3D * ODCADRecognizer2DLocal::detectOmni(ODSceneImage * scene) + shared_ptr<ODDetections3D> ODCADRecognizer2DLocal::detectOmni(shared_ptr<ODSceneImage> scene) { std::vector<cv::KeyPoint> keypoints_scene; @@ -357,13 +340,13 @@ namespace od scene->setDescriptors(descriptor_scene); scene->setKeypoints(keypoints_scene); - ODDetections3D * detections = new ODDetections3D; + shared_ptr<ODDetections3D> detections = make_shared<ODDetections3D>(); cv::Mat viz = scene->getCVImage().clone(); for(size_t i = 0; i < models_.size(); ++i) { - ODDetection3D * detection; + shared_ptr<ODDetection3D> detection = make_shared<ODDetection3D>(); if(detectSingleModel(scene, models_[i], detection, viz)) { detections->push_back(detection); diff --git a/detectors/src/local2D/simple_ransac_detection/CsvReader.cpp b/detectors/src/local2D/simple_ransac_detection/CsvReader.cpp index f92e2e67..e7f3eeca 100644 --- a/detectors/src/local2D/simple_ransac_detection/CsvReader.cpp +++ b/detectors/src/local2D/simple_ransac_detection/CsvReader.cpp @@ -5,9 +5,9 @@ namespace od { namespace l2d { /** The default constructor of the CSV reader Class */ - CsvReader::CsvReader(const std::string & path, const char & separator){ - _file_.open(path.c_str(), std::ifstream::in); - _separator_ = separator; + CsvReader::CsvReader(const std::string & path, const std::string & separator){ + file_.open(path.c_str(), std::ifstream::in); + separator_ = separator; } /* Read a plane text file with .ply format */ @@ -20,16 +20,16 @@ namespace od { bool end_vertex = false; // Read the whole *.ply file - while(getline(_file_, line)) { + while(getline(file_, line)) { std::stringstream liness(line); // read header if(!end_header) { - getline(liness, tmp_str, _separator_); + getline(liness, tmp_str, separator_.c_str()); if(tmp_str == "element") { - getline(liness, tmp_str, _separator_); + getline(liness, tmp_str, separator_.c_str()); getline(liness, n); if(tmp_str == "vertex") num_vertex = std::stoi(n); @@ -47,7 +47,7 @@ namespace od { if(!end_vertex && count < num_vertex) { std::string x, y, z; - getline(liness, x, _separator_); + getline(liness, x, separator_.c_str()); getline(liness, y, _separator_); getline(liness, z); diff --git a/detectors/src/local2D/simple_ransac_detection/CsvWriter.cpp b/detectors/src/local2D/simple_ransac_detection/CsvWriter.cpp index b1855c91..6737bd7d 100644 --- a/detectors/src/local2D/simple_ransac_detection/CsvWriter.cpp +++ b/detectors/src/local2D/simple_ransac_detection/CsvWriter.cpp @@ -5,14 +5,14 @@ namespace od { namespace l2d { CsvWriter::CsvWriter(const std::string & path, const std::string & separator){ - _file_.open(path.c_str(), std::ofstream::out); - _is_first_term_ = true; - _separator_ = separator; + file_.open(path.c_str(), std::ofstream::out); + is_first_term_ = true; + separator_ = separator; } CsvWriter::~CsvWriter() { - _file_.flush(); - _file_.close(); + file_.flush(); + file_.close(); } void CsvWriter::writeXYZ(const std::vector<cv::Point3f> & list_points3d) @@ -24,7 +24,7 @@ namespace od { y = std::to_string(list_points3d[i].y); z = std::to_string(list_points3d[i].z); - _file_ << x << _separator_ << y << _separator_ << z << std::endl; + file_ << x << separator_ << y << separator_ << z << std::endl; } } @@ -40,14 +40,14 @@ namespace od { y = std::to_string(list_points3d[i].y); z = std::to_string(list_points3d[i].z); - _file_ << u << _separator_ << v << _separator_ << x << _separator_ << y << _separator_ << z; + file_ << u << separator_ << v << separator_ << x << separator_ << y << separator_ << z; for(size_t j = 0; j < 32; ++j) { descriptor_str = std::to_string(descriptors.at<float>(i,j)); - _file_ << _separator_ << descriptor_str; + file_ << separator_ << descriptor_str; } - _file_ << std::endl; + file_ << std::endl; } } diff --git a/detectors/src/local2D/simple_ransac_detection/ModelRegistration.cpp b/detectors/src/local2D/simple_ransac_detection/ModelRegistration.cpp index cb710f4e..7e04ed72 100644 --- a/detectors/src/local2D/simple_ransac_detection/ModelRegistration.cpp +++ b/detectors/src/local2D/simple_ransac_detection/ModelRegistration.cpp @@ -38,7 +38,7 @@ namespace od { bool ModelRegistration::isRegistrable() const { - return (n_registrations_ < max_registrations_); + return n_registrations_ < max_registrations_; } ModelRegistration::ModelRegistration() diff --git a/detectors/src/local2D/simple_ransac_detection/PnPProblem.cpp b/detectors/src/local2D/simple_ransac_detection/PnPProblem.cpp index ba7d5181..0429bb20 100644 --- a/detectors/src/local2D/simple_ransac_detection/PnPProblem.cpp +++ b/detectors/src/local2D/simple_ransac_detection/PnPProblem.cpp @@ -203,7 +203,7 @@ namespace od { } // Given the mesh, backproject the 3D points to 2D to verify the pose estimation - std::vector<cv::Point2f> PnPProblem::verifyPoints(Mesh * mesh) + std::vector<cv::Point2f> PnPProblem::verifyPoints(shared_ptr<Mesh> mesh) { std::vector<cv::Point2f> verified_points_2d; for(size_t i = 0; i < mesh->getNumVertices(); ++i) @@ -305,7 +305,7 @@ namespace od { } // Möller–Trumbore intersection algorithm - bool PnPProblem::intersectMollerTrumbore(Ray & ray, Triangle & triangle, double * out) + bool PnPProblem::intersectMollerTrumbore(Ray & ray, Triangle & triangle, shared_ptr<double> out) { const double EPSILON = 0.000001; diff --git a/detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp b/detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp index 55ed79c9..33bf7df2 100644 --- a/detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp +++ b/detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp @@ -34,6 +34,7 @@ namespace od class vtkTimerCallbackSnapshot : public vtkCommand { public: + static vtkTimerCallbackSnapshot * New() { vtkTimerCallbackSnapshot * cb = new vtkTimerCallbackSnapshot; @@ -84,7 +85,9 @@ namespace od } private: + int snap_count; + public: std::string takeSnapshot(vtkRenderWindow *renderWindow, int snap_no); @@ -112,10 +115,10 @@ namespace od std::string feature_type; std::string input_file, input_dir, output_dir, output_extension; - vtkActor * actor; vtkRenderer * renderer; bool snap_mode; + }; int ODCADRecogTrainerSnapshotBased::train() @@ -159,7 +162,7 @@ namespace od // Read texture file std::string texfilename = getTexfileinObj(objname); vtkSmartPointer<vtkImageReader2Factory> readerFactory = vtkSmartPointer<vtkImageReader2Factory>::New(); - vtkImageReader2 *imageReader = readerFactory->CreateImageReader2(texfilename.c_str()); + vtkImageReader2 * imageReader = readerFactory->CreateImageReader2(texfilename.c_str()); imageReader->SetFileName(texfilename.c_str()); imageReader->Update(); @@ -190,7 +193,7 @@ namespace od //Set up the camera at a very proper location and angle (about 30) renderer->ResetCamera(); - vtkCamera *camera = renderer->GetActiveCamera(); + vtkCamera * camera = renderer->GetActiveCamera(); camera->Elevation(VIEW_ANGLE); //////////////////// @@ -254,7 +257,7 @@ namespace od { fout << pairs[i].first.x << " " << pairs[i].first.y << " " << pairs[i].first.z << endl; fout << pairs[i].second.pt.x << " " << pairs[i].second.pt.y << " " << pairs[i].second.octave << " " << pairs[i].second.angle << " " << pairs[i].second.response << " " << pairs[i].second.size << endl; - for(int j = 0; j < descriptors.cols; j++) + for(size_t j = 0; j < descriptors.cols; ++j) { fout << descriptors.at<float>(i, j) << " "; } @@ -332,12 +335,12 @@ namespace od picker->Pick(kpts[i].pt.x, img.rows - kpts[i].pt.y, 0, ren); //IMPORTANT: note that the y coordinate is converted from pixel coordinates to picking coordinates //find transformed 3d - double *pos = picker->GetPickPosition(); + double * pos = picker->GetPickPosition(); //3 find original 3d - vtkMatrix4x4 *curr_transform_inv = vtkMatrix4x4::New(); + vtkMatrix4x4 * curr_transform_inv = vtkMatrix4x4::New(); vtkMatrix4x4::Invert(actor->GetMatrix(), curr_transform_inv); - double *pos_model = curr_transform_inv->MultiplyDoublePoint(pos); + double * pos_model = curr_transform_inv->MultiplyDoublePoint(pos); //std::cout << "2D position is: " << kpts[i].pt.x << " " << kpts[i].pt.x << std::endl; //std::cout << "3d position (world coordinates) is: " << pos[0] << " " << pos[1] << " " << pos[2] << std::endl << endl; @@ -377,13 +380,14 @@ namespace od class MouseInteractorStyle2 : public vtkInteractorStyleTrackballCamera { public: - static MouseInteractorStyle2 *New(); + + static MouseInteractorStyle2 * New(); vtkTypeMacro(MouseInteractorStyle2, vtkInteractorStyleTrackballCamera); virtual void OnLeftButtonDown() { - int *clickPos = this->GetInteractor()->GetEventPosition(); + int * clickPos = this->GetInteractor()->GetEventPosition(); // Pick from this location. /*vtkSmartPointer<vtkPointPicker> picker = @@ -417,8 +421,6 @@ namespace od vtkInteractorStyleTrackballCamera::OnLeftButtonDown(); } - private: - }; vtkStandardNewMacro(MouseInteractorStyle2) diff --git a/examples/apps/cadrecog2D/od_test_single_db_single_model.cpp b/examples/apps/cadrecog2D/od_test_single_db_single_model.cpp index 6dac963d..542392ee 100644 --- a/examples/apps/cadrecog2D/od_test_single_db_single_model.cpp +++ b/examples/apps/cadrecog2D/od_test_single_db_single_model.cpp @@ -27,6 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h" #include "od/detectors/local2D/detection/ODCADRecognizer2DLocal.h" #include "od/common/utils/ODFrameGenerator.h" +#include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) { @@ -53,13 +54,14 @@ int main(int argc, char *argv[]) od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(imagespath); //GUI //cv::namedWindow("Overlay", cv::WINDOW_NORMAL); - while(frameGenerator.isValid() && cv::waitKey(1000) != 27) + od::ODSceneImage * scene; + while(frameGenerator.isValid() && cv::waitKey(10) != 27) { - od::ODSceneImage * scene = frameGenerator.getNextFrame(); + scene = frameGenerator.getNextFrame(); //cv::imshow("Overlay", scene->getCVImage()); //Detect - od::ODDetections3D *detections = detector.detectOmni(scene); + boost::shared_ptr<od::ODDetections3D> detections = detector.detectOmni(scene); if(detections->size() > 0) { @@ -68,20 +70,17 @@ int main(int argc, char *argv[]) for (int i = 0; i < detections->size(); i++) { - od::ODDetection3D *detection = detections->at(i); + boost::shared_ptr<od::ODDetection3D> detection = detections->at(i); detection->printSelf(); logfile << detection->getId() << endl; } //cv::imshow("Overlay", detections->getMetainfoImage()); } - else + //else //cv::imshow("Overlay", scene->getCVImage()); - - delete scene; - delete detections; } diff --git a/examples/apps/global2D/od_multihog_app.cpp b/examples/apps/global2D/od_multihog_app.cpp index cc6967fe..a51b3281 100644 --- a/examples/apps/global2D/od_multihog_app.cpp +++ b/examples/apps/global2D/od_multihog_app.cpp @@ -32,7 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/global2D/detection/ODHOGDetector.h" #include "od/common/utils/ODFrameGenerator.h" - +#include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) { @@ -60,7 +60,7 @@ int main(int argc, char *argv[]) detectors.push_back(detector3); //init all detectors - for (size_t i = 0; i < detectors.size(); i++) + for(size_t i = 0; i < detectors.size(); ++i) detectors[i].init(); //get scenes @@ -81,10 +81,11 @@ int main(int argc, char *argv[]) //detect 3 times for (size_t i = 0; i < detectors.size(); i++) { - od::ODDetections2D *detections = detectors[i].detectOmni(scene); + boost::shared_ptr<od::ODDetections2D> detections = detectors[i].detectOmni(scene); if(detections->size() > 0) images_to_show.push_back(detections->renderMetainfo(*scene).getCVImage()); - else images_to_show.push_back(scene->getCVImage()); + else + images_to_show.push_back(scene->getCVImage()); } cv::Mat multiimage = makeCanvasMultiImages(images_to_show, sizesingle, messages); diff --git a/examples/objectdetector/od_cascade_cam.cpp b/examples/objectdetector/od_cascade_cam.cpp index dc50d16d..87b2d303 100644 --- a/examples/objectdetector/od_cascade_cam.cpp +++ b/examples/objectdetector/od_cascade_cam.cpp @@ -31,6 +31,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/global2D/detection/ODCascadeDetector.h" #include "od/common/utils/ODFrameGenerator.h" +#include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) { @@ -56,7 +57,7 @@ int main(int argc, char *argv[]) od::ODSceneImage * scene = frameGenerator.getNextFrame(); //Detect - od::ODDetections2D *detections = detector.detectOmni(scene); + boost::shared_ptr<od::ODDetections2D> detections = detector.detectOmni(scene); if(detections->size() > 0) cv::imshow("Overlay", detections->getMetainfoImage()); diff --git a/examples/objectdetector/od_cascade_files.cpp b/examples/objectdetector/od_cascade_files.cpp index 962ca61e..18591a3c 100644 --- a/examples/objectdetector/od_cascade_files.cpp +++ b/examples/objectdetector/od_cascade_files.cpp @@ -31,6 +31,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/global2D/detection/ODCascadeDetector.h" #include "od/common/utils/ODFrameGenerator.h" +#include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) { @@ -57,7 +58,7 @@ int main(int argc, char *argv[]) od::ODSceneImage * scene = frameGenerator.getNextFrame(); //Detect - od::ODDetections2D *detections = detector.detectOmni(scene); + boost::shared_ptr<od::ODDetections2D> detections = detector.detectOmni(scene); if(detections->size() > 0) { diff --git a/examples/objectdetector/od_example_framegenerator.cpp b/examples/objectdetector/od_example_framegenerator.cpp index 16eb1a4d..ef99e696 100644 --- a/examples/objectdetector/od_example_framegenerator.cpp +++ b/examples/objectdetector/od_example_framegenerator.cpp @@ -40,7 +40,7 @@ int main(int argc, char *argv[]) //GUI and feedback pcl::visualization::PCLVisualizer vis ("kinect"); - od::ODScenePointCloud<pcl::PointXYZRGBA> *frame; + od::ODScenePointCloud<pcl::PointXYZRGBA> * frame; od::ODFrameGenerator<od::ODScenePointCloud<pcl::PointXYZRGBA>, od::GENERATOR_TYPE_DEVICE> frameGenerator(""); while(frameGenerator.isValid()) { diff --git a/examples/objectdetector/od_hog_train.cpp b/examples/objectdetector/od_hog_train.cpp index a356003a..b65c5154 100644 --- a/examples/objectdetector/od_hog_train.cpp +++ b/examples/objectdetector/od_hog_train.cpp @@ -34,6 +34,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/global2D/training/ODHOGTrainer.h" #include "od/detectors/global2D/detection/ODHOGDetector.h" #include "od/common/utils/ODFrameGenerator.h" +#include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) { @@ -69,7 +70,7 @@ int main(int argc, char *argv[]) od::ODSceneImage * scene = frameGenerator.getNextFrame(); //Detect - ODDetections2D *detections = detector.detectOmni(scene); + boost::shared_ptr<ODDetections2D> detections = detector.detectOmni(scene); if(detections->size() > 0) cv::imshow("Overlay", detections->renderMetainfo(*scene).getCVImage()); diff --git a/examples/objectdetector/od_image_cadrecog_camera.cpp b/examples/objectdetector/od_image_cadrecog_camera.cpp index 1e3fa549..f2429c30 100644 --- a/examples/objectdetector/od_image_cadrecog_camera.cpp +++ b/examples/objectdetector/od_image_cadrecog_camera.cpp @@ -33,7 +33,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h" #include "od/detectors/local2D/detection/ODCADRecognizer2DLocal.h" #include "od/common/utils/ODFrameGenerator.h" - +#include <boost/shared_ptr.hpp> + int main(int argc, char *argv[]) { @@ -66,7 +67,7 @@ int main(int argc, char *argv[]) od::ODSceneImage * scene = frameGenerator.getNextFrame(); //Detect - ODDetections3D *detections = detector.detectOmni(scene); + boost::shared_ptr<ODDetections3D> detections = detector.detectOmni(scene); if(detections->size() > 0) cv::imshow("Overlay", detections->getMetainfoImage()); diff --git a/examples/objectdetector/od_image_cadrecog_files.cpp b/examples/objectdetector/od_image_cadrecog_files.cpp index 3b129785..b94017b7 100644 --- a/examples/objectdetector/od_image_cadrecog_files.cpp +++ b/examples/objectdetector/od_image_cadrecog_files.cpp @@ -28,6 +28,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h" #include "od/detectors/local2D/detection/ODCADRecognizer2DLocal.h" #include "od/common/utils/ODFrameGenerator.h" +#include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) { @@ -62,7 +63,7 @@ int main(int argc, char *argv[]) cv::imshow("Overlay", scene->getCVImage()); //Detect - od::ODDetections3D *detections = detector.detectOmni(scene); + boost::shared_ptr<od::ODDetections3D> detections = detector.detectOmni(scene); if(detections->size() > 0) cv::imshow("Overlay", detections->getMetainfoImage()); diff --git a/examples/objectdetector/od_image_customhog.cpp b/examples/objectdetector/od_image_customhog.cpp index 90d14423..3aabf8d7 100644 --- a/examples/objectdetector/od_image_customhog.cpp +++ b/examples/objectdetector/od_image_customhog.cpp @@ -33,6 +33,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/global2D/detection/ODHOGDetector.h" #include "od/common/utils/ODFrameGenerator.h" +#include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) { @@ -57,7 +58,7 @@ int main(int argc, char *argv[]) od::ODSceneImage * scene = frameGenerator.getNextFrame(); //Detect - ODDetections2D *detections = detector.detectOmni(scene); + boost::shared_ptr<ODDetections2D> detections = detector.detectOmni(scene); if(detections->size() > 0) cv::imshow("Overlay", detections->renderMetainfo(*scene).getCVImage()); diff --git a/examples/objectdetector/od_image_facerecog.cpp b/examples/objectdetector/od_image_facerecog.cpp index 23fee756..a3aabb5e 100644 --- a/examples/objectdetector/od_image_facerecog.cpp +++ b/examples/objectdetector/od_image_facerecog.cpp @@ -32,6 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/global2D/ODFaceRecognizer.h" #include "od/common/utils/ODFrameGenerator.h" +#include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) { @@ -62,7 +63,7 @@ int main(int argc, char *argv[]) od::ODSceneImage * scene = frameGenerator.getNextFrame(); //Detect - od::ODDetections *detections = objdetector.detect(scene); + boost::shared_ptr<od::ODDetections> detections = objdetector.detect(scene); (*detections)[0]->printSelf(); cv::imshow("Overlay", scene->getCVImage()); diff --git a/examples/objectdetector/od_image_hog_files.cpp b/examples/objectdetector/od_image_hog_files.cpp index 26f441a2..604bcbce 100644 --- a/examples/objectdetector/od_image_hog_files.cpp +++ b/examples/objectdetector/od_image_hog_files.cpp @@ -32,6 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/global2D/detection/ODCascadeDetector.h" #include "od/detectors/global2D/detection/ODHOGDetector.h" #include "od/common/utils/ODFrameGenerator.h" +#include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) { @@ -59,7 +60,7 @@ int main(int argc, char *argv[]) od::ODSceneImage * scene = frameGenerator.getNextFrame(); //Detect - ODDetections2D *detections = detector.detectOmni(scene); + boost::shared_ptr<ODDetections2D> detections = detector.detectOmni(scene); if(detections->size() > 0) cv::imshow("Overlay", detections->renderMetainfo(*scene).getCVImage()); diff --git a/examples/objectdetector/od_multialgo_files.cpp b/examples/objectdetector/od_multialgo_files.cpp index a6c42798..e6da9564 100644 --- a/examples/objectdetector/od_multialgo_files.cpp +++ b/examples/objectdetector/od_multialgo_files.cpp @@ -28,6 +28,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <opencv2/highgui.hpp> #include "detectors/misc/detection/ODDetectorMultiAlgo.hpp" #include "od/common/utils/ODFrameGenerator.h" +#include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) { @@ -53,7 +54,7 @@ int main(int argc, char *argv[]) od::ODSceneImage * scene = frameGenerator.getNextFrame(); //Detect - ODDetections2D *detections = detector.detectOmni(scene); + boost::shared_ptr<ODDetections2D> detections = detector.detectOmni(scene); if(detections->size() > 0) cv::imshow("Overlay", detections->renderMetainfo(*scene).getCVImage()); diff --git a/examples/objectdetector/od_multialgo_pc.cpp b/examples/objectdetector/od_multialgo_pc.cpp index 88ed5d52..b12fb4c4 100644 --- a/examples/objectdetector/od_multialgo_pc.cpp +++ b/examples/objectdetector/od_multialgo_pc.cpp @@ -35,6 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/global3D/ODPointCloudGlobalMatching.h" #include "common/utils/ODFrameGenerator.h" #include <string> +#include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) { @@ -53,7 +54,7 @@ int main(int argc, char *argv[]) detector.init(); //GUI and feedback - od::ODScenePointCloud<pcl::PointXYZRGBA> *frame; + od::ODScenePointCloud<pcl::PointXYZRGBA> * frame; pcl::visualization::PCLVisualizer vis ("kinect"); size_t previous_cluster_size = 0; @@ -69,7 +70,7 @@ int main(int argc, char *argv[]) //remove previous point clouds and text and add new ones in the visualizer vis.removePointCloud ("frame"); vis.addPointCloud<pcl::PointXYZRGBA> (frame->getPointCloud(), "frame"); - for (size_t i = 0; i < previous_cluster_size; i++) + for(size_t i = 0; i < previous_cluster_size; ++i) { cluster_name << "cluster_" << i; vis.removePointCloud (cluster_name.str ()); @@ -83,10 +84,10 @@ int main(int argc, char *argv[]) //Detect - od::ODDetections3D * detections = detector->detectOmni(frame); + boost::shared_ptr<od::ODDetections3D> detections = detector->detectOmni(frame); //add all the detections in the visualizer with its id as text - for (size_t i = 0; i < detections->size (); i++) + for(size_t i = 0; i < detections->size (); ++i) { cluster_name << "cluster_" << i; pcl::visualization::PointCloudColorHandlerRandom<pcl::PointXYZ> random_handler (detections->at(i)->getMetainfoCluster()); @@ -99,6 +100,7 @@ int main(int argc, char *argv[]) vis.addText3D (detections->at(i)->getId(), pos, 0.015f, 1, 0, 1, cluster_name.str() + "_txt", 0); cluster_name.str(std::string()); } + previous_cluster_size = detections->size (); vis.spinOnce (); diff --git a/examples/objectdetector/od_pc_global.cpp b/examples/objectdetector/od_pc_global.cpp index 33777f50..135acf00 100644 --- a/examples/objectdetector/od_pc_global.cpp +++ b/examples/objectdetector/od_pc_global.cpp @@ -35,6 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <iostream> #include "od/detectors/global3D/ODPointCloudGlobalMatching.h" +#include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) { @@ -61,10 +62,10 @@ int main(int argc, char *argv[]) //Get a scene od::ODScenePointCloud<pcl::PointXYZRGBA> scene(pointcloud_file); - od::ODDetections3D * detections = detector.detectOmni(&scene); + boost::shared_ptr<od::ODDetections3D> detections = detector.detectOmni(&scene); //feedback - for(size_t i = 0; i < detections->size(); i++) + for(size_t i = 0; i < detections->size(); ++i) { detections->at(i)->printSelf(); } diff --git a/examples/objectdetector/od_pc_global_files.cpp b/examples/objectdetector/od_pc_global_files.cpp index d8986480..84b5dacd 100644 --- a/examples/objectdetector/od_pc_global_files.cpp +++ b/examples/objectdetector/od_pc_global_files.cpp @@ -36,6 +36,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <vector> #include "od/common/utils/ODFrameGenerator.h" #include "od/detectors/global3D/ODPointCloudGlobalMatching.h" +#include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) { @@ -60,7 +61,7 @@ int main(int argc, char *argv[]) detector.init(); //Get a scene - od::ODScenePointCloud<pcl::PointXYZRGBA> *frame; + od::ODScenePointCloud<pcl::PointXYZRGBA> * frame; od::ODFrameGenerator<od::ODScenePointCloud<pcl::PointXYZRGBA>, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(pointcloud_file); while(frameGenerator.isValid()) @@ -68,9 +69,9 @@ int main(int argc, char *argv[]) //get frame frame = frameGenerator.getNextFrame(); - od::ODDetections3D * detections = detector.detectOmni(frame); + boost::shared_ptr<od::ODDetections3D> detections = detector.detectOmni(frame); - for(size_t i = 0; i < detections->size(); i++) + for(size_t i = 0; i < detections->size(); ++i) { detections->at(i)->printSelf(); } diff --git a/examples/objectdetector/od_pc_global_real_time.cpp b/examples/objectdetector/od_pc_global_real_time.cpp index 107c0f6b..9c5883e5 100644 --- a/examples/objectdetector/od_pc_global_real_time.cpp +++ b/examples/objectdetector/od_pc_global_real_time.cpp @@ -37,6 +37,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/global3D/ODPointCloudGlobalMatching.h" #include "od/common/utils/ODFrameGenerator.h" #include <string> +#include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) { @@ -60,7 +61,7 @@ int main(int argc, char *argv[]) detector.init(); //GUI and feedback - od::ODScenePointCloud<pcl::PointXYZRGBA> *frame; + od::ODScenePointCloud<pcl::PointXYZRGBA> * frame; pcl::visualization::PCLVisualizer vis ("kinect"); od::ODFrameGenerator<od::ODScenePointCloud<pcl::PointXYZRGBA>, od::GENERATOR_TYPE_DEVICE> frameGenerator; @@ -78,10 +79,10 @@ int main(int argc, char *argv[]) vis.addPointCloud<pcl::PointXYZRGBA> (frame->getPointCloud(), "frame"); //Detect - od::ODDetections3D * detections = detector.detectOmni(frame); + boost::shared_ptr<od::ODDetections3D> detections = detector.detectOmni(frame); //add all the detections in the visualizer with its id as text - for (size_t i = 0; i < detections->size (); i++) + for(size_t i = 0; i < detections->size(); ++i) { pcl::visualization::PointCloudColorHandlerRandom<pcl::PointXYZ> random_handler (detections->at(i)->getMetainfoCluster()); vis.addPointCloud<pcl::PointXYZ> (detections->at(i)->getMetainfoCluster(), random_handler, "cluster_" + i); From a273e49247a98aed29ea658e1a28e6467cc92d74 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Mon, 13 Jun 2016 12:37:19 +0200 Subject: [PATCH 20/79] fixes svmlight build; only linking problem missing --- common/CMakeLists.txt | 3 +- common/include/od/common/bindings/svmlight.h | 140 +++------------ .../include/od/common/pipeline/ODDetection.h | 2 +- .../include/od/common/pipeline/ODDetector.h | 4 +- .../od/common/utils/ODFeatureDetector2D.h | 2 +- .../od/common/utils/ODFrameGenerator.h | 34 ++-- .../od/common/utils/{utils.h => ODUtils.h} | 2 + common/src/bindings/svmlight.cpp | 161 ++++++++++++++++++ common/src/utils/ODFeatureDetector2D.cpp | 1 - common/src/utils/{utils.cpp => ODUtils.cpp} | 13 +- .../detection/ODCADDetector3DGlobal.hpp | 2 +- .../misc/detection/ODDetectorMultiAlgo.hpp | 10 +- .../od/detectors/global2D/ODFaceRecognizer.h | 16 +- .../global2D/detection/ODCascadeDetector.h | 2 +- .../global2D/detection/ODHOGDetector.h | 16 +- .../global2D/training/ODHOGTrainer.h | 29 ++-- .../training/ODCADDetectTrainer3DGlobal.h | 9 - .../detectors/local2D/ODImageLocalMatching.h | 4 +- .../detection/ODCADRecognizer2DLocal.h | 19 --- .../simple_ransac_detection/CsvReader.h | 2 +- .../simple_ransac_detection/PnPProblem.h | 3 +- .../simple_ransac_detection/RobustMatcher.h | 2 +- .../local2D/simple_ransac_detection/Utils.h | 9 - .../training/ODCADRecogTrainerSnapshotBased.h | 2 +- detectors/src/global2D/ODFaceRecognizer.cpp | 7 +- .../src/global2D/detection/ODHOGDetector.cpp | 76 ++++----- .../src/global2D/training/ODHOGTrainer.cpp | 104 +++++------ .../detection/ODCADRecognizer2DLocal.cpp | 2 +- .../simple_ransac_detection/CsvReader.cpp | 14 +- .../simple_ransac_detection/PnPProblem.cpp | 6 +- .../gsoc2016_blog_giacomo.md | 15 +- .../od_test_single_db_single_model.cpp | 5 +- examples/apps/global2D/od_multihog_app.cpp | 28 +-- examples/objectdetector/od_cascade_cam.cpp | 3 +- examples/objectdetector/od_cascade_files.cpp | 3 +- .../od_example_framegenerator.cpp | 4 +- examples/objectdetector/od_hog_train.cpp | 7 +- .../od_image_cadrecog_camera.cpp | 3 +- .../od_image_cadrecog_files.cpp | 4 +- .../objectdetector/od_image_customhog.cpp | 12 +- .../objectdetector/od_image_facerecog.cpp | 5 +- .../objectdetector/od_image_hog_files.cpp | 6 +- .../objectdetector/od_multialgo_files.cpp | 10 +- examples/objectdetector/od_multialgo_pc.cpp | 9 +- examples/objectdetector/od_pc_global.cpp | 5 +- .../objectdetector/od_pc_global_files.cpp | 5 +- .../objectdetector/od_pc_global_real_time.cpp | 5 +- 47 files changed, 422 insertions(+), 403 deletions(-) rename common/include/od/common/utils/{utils.h => ODUtils.h} (98%) create mode 100644 common/src/bindings/svmlight.cpp rename common/src/utils/{utils.cpp => ODUtils.cpp} (97%) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 7994efeb..24dac071 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -20,8 +20,9 @@ if(build) "src/pipeline/ODObjectDetector.cpp" "src/pipeline/ODScene.cpp" "src/pipeline/ODDetection.cpp" - "src/utils/utils.cpp" + "src/utils/ODUtils.cpp" "src/utils/ODFeatureDetector2D.cpp" + "src/bindings/svmlight.cpp" ) include_directories(${COMMON_INCLUDE_DIR}) diff --git a/common/include/od/common/bindings/svmlight.h b/common/include/od/common/bindings/svmlight.h index 83e8ee7b..ee325651 100644 --- a/common/include/od/common/bindings/svmlight.h +++ b/common/include/od/common/bindings/svmlight.h @@ -48,110 +48,48 @@ Unless required by applicable law or agreed to in writing, software distributed * @see http://www.cs.cornell.edu/people/tj/svm_light/ for SVMlight details and terms of use * */ - -#ifndef SVMLIGHT_H -#define SVMLIGHT_H - +#pragma once #include <stdio.h> #include <vector> +#include <string> // svmlight related // namespace required for avoiding collisions of declarations (e.g. LINEAR being declared in flann, svmlight and libsvm) namespace svmlight { - extern "C" { - #include "3rdparty/svmlight/svm_common.h" - #include "3rdparty/svmlight/svm_learn.h" - } + #include "svm_common.h" + #include "svm_learn.h" } using namespace svmlight; class SVMlight { private: - DOC** docs; // training examples + DOC ** docs; // training examples long totwords, totdoc, i; // support vector stuff - double* target; - double* alpha_in; - KERNEL_CACHE* kernel_cache; - MODEL* model; // SVM model - - SVMlight() { - // Init variables - alpha_in = NULL; - kernel_cache = NULL; // Cache not needed with linear kernel - model = (MODEL *) my_malloc(sizeof (MODEL)); - learn_parm = new LEARN_PARM; - kernel_parm = new KERNEL_PARM; - // Init parameters - verbosity = 1; // Show some messages -v 1 - learn_parm->alphafile[0] = ' '; // NULL; // Important, otherwise files with strange/invalid names appear in the working directory - // learn_parm->alphafile = NULL; // Important, otherwise files with strange/invalid names appear in the working directory - learn_parm->biased_hyperplane = 1; - learn_parm->sharedslack = 0; // 1 - learn_parm->skip_final_opt_check = 0; - learn_parm->svm_maxqpsize = 10; - learn_parm->svm_newvarsinqp = 0; - learn_parm->svm_iter_to_shrink = 2; // 2 is for linear; - learn_parm->kernel_cache_size = 40; - learn_parm->maxiter = 100000; - learn_parm->svm_costratio = 1.0; - learn_parm->svm_costratio_unlab = 1.0; - learn_parm->svm_unlabbound = 1E-5; - learn_parm->eps = 0.1; - learn_parm->transduction_posratio = -1.0; - learn_parm->epsilon_crit = 0.001; - learn_parm->epsilon_a = 1E-15; - learn_parm->compute_loo = 0; - learn_parm->rho = 1.0; - learn_parm->xa_depth = 0; - // The HOG paper uses a soft classifier (C = 0.01), set to 0.0 to get the default calculation - learn_parm->svm_c = 0.01; // -c 0.01 - learn_parm->type = REGRESSION; - learn_parm->remove_inconsistent = 0; // -i 0 - Important - kernel_parm->rbf_gamma = 1.0; - kernel_parm->coef_lin = 1; - kernel_parm->coef_const = 1; - kernel_parm->kernel_type = LINEAR; // -t 0 - kernel_parm->poly_degree = 3; - } - - virtual ~SVMlight() { - // Cleanup area - // Free the memory used for the cache - if (kernel_cache) - kernel_cache_cleanup(kernel_cache); - free(alpha_in); - free_model(model, 0); - for (i = 0; i < totdoc; i++) - free_example(docs[i], 1); - free(docs); - free(target); - } + double * target; + double * alpha_in; + KERNEL_CACHE * kernel_cache; + MODEL * model; // SVM model + + SVMlight(); + virtual ~SVMlight(); public: - LEARN_PARM* learn_parm; - KERNEL_PARM* kernel_parm; - static SVMlight* getInstance(); + LEARN_PARM * learn_parm; + KERNEL_PARM * kernel_parm; - inline void saveModelToFile(const std::string _modelFileName) { - write_model(const_cast<char*>(_modelFileName.c_str()), model); - } + static SVMlight * getInstance(); - void loadModelFromFile(const std::string _modelFileName) { - this->model = read_model(const_cast<char*>(_modelFileName.c_str())); - } + void saveModelToFile(const std::string _modelFileName); + + void loadModelFromFile(const std::string _modelFileName); // read in a problem (in svmlight format) - void read_problem(char* filename) { - // Reads and parses the specified file - read_documents(filename, &docs, &target, &totwords, &totdoc); - } + void read_problem(char* filename); // Calls the actual machine learning algorithm - void train() { - svm_learn_regression(docs, target, totdoc, totwords, learn_parm, kernel_parm, &kernel_cache, model); - } + void train(); /** * Generates a single detecting feature vector (vec1) from the trained support vectors, for use e.g. with the HOG algorithm @@ -159,47 +97,15 @@ class SVMlight { * @param singleDetectorVector resulting single detector vector for use in openCV HOG * @param singleDetectorVectorIndices dummy vector for this implementation */ - void getSingleDetectingVector(std::vector<float>& singleDetectorVector, std::vector<unsigned int>& singleDetectorVectorIndices) { - // Now we use the trained svm to retrieve the single detector vector - DOC** supveclist = model->supvec; - printf("Calculating single descriptor vector out of support vectors (may take some time)\n"); - // Retrieve single detecting vector (v1) from returned ones by calculating vec1 = sum_1_n (alpha_y*x_i). (vec1 is a n x1 column vector. n = feature vector length) - singleDetectorVector.clear(); - singleDetectorVector.resize(model->totwords, 0.); - printf("Resulting vector size %lu\n", singleDetectorVector.size()); - - // Walk over every support vector - for (long ssv = 1; ssv < model->sv_num; ++ssv) { // Don't know what's inside model->supvec[0] ?! - // Get a single support vector - DOC* singleSupportVector = supveclist[ssv]; // Get next support vector - SVECTOR* singleSupportVectorValues = singleSupportVector->fvec; - WORD singleSupportVectorComponent; - // Walk through components of the support vector and populate our detector vector - for (unsigned long singleFeature = 0; singleFeature < model->totwords; ++singleFeature) { - singleSupportVectorComponent = singleSupportVectorValues->words[singleFeature]; - singleDetectorVector.at(singleSupportVectorComponent.wnum-1) += (singleSupportVectorComponent.weight * model->alpha[ssv]); - } - } - } + void getSingleDetectingVector(std::vector<float>& singleDetectorVector, std::vector<unsigned int>& singleDetectorVectorIndices); /** * Return model detection threshold / bias * @return detection threshold / bias */ - float getThreshold() const { - return model->b; - } + float getThreshold() const; - const char* getSVMName() const { - return "SVMlight"; - } + const char* getSVMName() const; }; -/// Singleton -SVMlight* SVMlight::getInstance() { - static SVMlight theInstance; - return &theInstance; -} - -#endif /* SVMLIGHT_H */ diff --git a/common/include/od/common/pipeline/ODDetection.h b/common/include/od/common/pipeline/ODDetection.h index 8053d6fe..ce4e3b44 100644 --- a/common/include/od/common/pipeline/ODDetection.h +++ b/common/include/od/common/pipeline/ODDetection.h @@ -28,7 +28,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 12.06.15. // #pragma once -#include "od/common/utils/utils.h" +#include "od/common/utils/ODUtils.h" #include "od/common/pipeline/ODScene.h" #include "od/common/utils/ODShared_pointers.h" diff --git a/common/include/od/common/pipeline/ODDetector.h b/common/include/od/common/pipeline/ODDetector.h index c5eb53bd..df787d39 100644 --- a/common/include/od/common/pipeline/ODDetector.h +++ b/common/include/od/common/pipeline/ODDetector.h @@ -135,14 +135,14 @@ namespace od * \param[in] scene An instance of 2D scene * \return A number of detections as an ODDetections instance. */ - virtual shared_ptr<ODDetections> detect(share_ptd<ODSceneImage> scene) = 0; + virtual shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene) = 0; /** \brief Function for performing detection on an entire scene. * The purpose of this function is to detect an object in an entire scene. Thus, other than the type of detection we also have information about the location of the detection w.r.t. the scene. * \param[in] scene An instance of 2D scene * \return A number of detections as an ODDetections3D instance containing information about the detection and its pose in 3D. */ - virtual shared_ptr<ODDetections3D> detectOmni(share_ptd<ODSceneImage> scene) = 0; + virtual shared_ptr<ODDetections3D> detectOmni(shared_ptr<ODSceneImage> scene) = 0; }; } diff --git a/common/include/od/common/utils/ODFeatureDetector2D.h b/common/include/od/common/utils/ODFeatureDetector2D.h index 4b4e86ea..24e3dfdc 100644 --- a/common/include/od/common/utils/ODFeatureDetector2D.h +++ b/common/include/od/common/utils/ODFeatureDetector2D.h @@ -8,7 +8,7 @@ #include <opencv2/features2d/features2d.hpp> #include <opencv2/xfeatures2d.hpp> #include <opencv2/cudafeatures2d.hpp> - +#include <GL/gl.h> #include "SiftGPU.h" diff --git a/common/include/od/common/utils/ODFrameGenerator.h b/common/include/od/common/utils/ODFrameGenerator.h index 0ece0100..99c1e53a 100644 --- a/common/include/od/common/utils/ODFrameGenerator.h +++ b/common/include/od/common/utils/ODFrameGenerator.h @@ -29,20 +29,22 @@ namespace od { public: - ODFrameGenerator(const std::string & input = ""); + ODFrameGenerator(const std::string & input = std::string("")); ODFrameGenerator(int input = 0){} - SceneT * getNextFrame(); + shared_ptr<SceneT> getNextFrame(); bool isValid(); protected: + int cameraID_; std::vector<std::string> file_list_; std::string video_read_path_; unsigned int curr_image_; cv::VideoCapture inputCapture_; bool exhausted_; + }; @@ -50,14 +52,14 @@ namespace od class ODFrameGenerator<SceneT, GENERATOR_TYPE_FILE_LIST> { public: - ODFrameGenerator(const std::string & input = "") + ODFrameGenerator(const std::string & input = std::string("")) { file_list_ = myglob(input); curr_image_ = -1; exhausted_ = false; } - SceneT * getNextFrame() + shared_ptr<SceneT> getNextFrame() { if(exhausted_) { @@ -70,10 +72,13 @@ namespace od exhausted_ = true; cout << "Frame: " << file_list_[curr_image_] << endl; - return new SceneT(file_list_[curr_image_]); + return make_shared<SceneT>(file_list_[curr_image_]); } + bool isValid() - {return !exhausted_;} + { + return !exhausted_; + } std::string currentFile() { @@ -81,16 +86,19 @@ namespace od } private: + std::vector<std::string> file_list_; bool exhausted_; int curr_image_; + }; template<> class ODFrameGenerator<ODSceneImage, GENERATOR_TYPE_DEVICE> { public: - ODFrameGenerator(const std::string & input = "") + + ODFrameGenerator(const std::string & input = std::string("")) { input_capture_.open(input); if(!input_capture_.isOpened()) @@ -104,15 +112,17 @@ namespace od {std::cout << "FATAL: Cannot open video capture!" << std::endl;} } - ODSceneImage * getNextFrame() + shared_ptr<ODSceneImage> getNextFrame() { cv::Mat frame; input_capture_.read(frame); - return new ODSceneImage(frame); + return make_shared<ODSceneImage>(frame); } bool isValid() - {return input_capture_.isOpened();} + { + return input_capture_.isOpened(); + } cv::VideoCapture input_capture_; }; @@ -155,12 +165,13 @@ namespace od class ODFrameGenerator<ODScenePointCloud<PointT>, GENERATOR_TYPE_DEVICE> { public: + typedef pcl::PointCloud<PointT> PointCloud; typedef typename pcl::PointCloud<PointT>::Ptr PointCloudPtr; typedef typename pcl::PointCloud<PointT>::ConstPtr PointCloudConstPtr; /* A simple class for capturing data from an OpenNI camera */ - ODFrameGenerator(const std::string & input = "") : grabber_(input), most_recent_frame_(), frame_counter_(0), active_(true) + ODFrameGenerator(const std::string & input = std::string("")) : grabber_(input), most_recent_frame_(), frame_counter_(0), active_(true) { boost::function<void(const PointCloudConstPtr&)> frame_cb = boost::bind(&ODFrameGenerator<ODScenePointCloud<PointT> , GENERATOR_TYPE_DEVICE>::onNewFrame, this, _1); grabber_.registerCallback(frame_cb); @@ -206,6 +217,7 @@ namespace od } protected: + void onNewFrame(const PointCloudConstPtr & cloud) { mutex_.lock (); diff --git a/common/include/od/common/utils/utils.h b/common/include/od/common/utils/ODUtils.h similarity index 98% rename from common/include/od/common/utils/utils.h rename to common/include/od/common/utils/ODUtils.h index 3f694f97..5b496e5a 100644 --- a/common/include/od/common/utils/utils.h +++ b/common/include/od/common/utils/ODUtils.h @@ -7,6 +7,8 @@ #include <boost/filesystem.hpp> #include <boost/algorithm/string.hpp> #include <opencv2/core/core.hpp> +#include <opencv2/imgproc/imgproc.hpp> +#include <boost/functional/hash.hpp> #include <fstream> #include <iostream> diff --git a/common/src/bindings/svmlight.cpp b/common/src/bindings/svmlight.cpp new file mode 100644 index 00000000..516e2fe6 --- /dev/null +++ b/common/src/bindings/svmlight.cpp @@ -0,0 +1,161 @@ +/* +Copyright (c) 2015, Kripasindhu Sarkar +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions 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. + * Neither the name of the copyright holder(s) nor the + names of its contributors may 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 COPYRIGHT HOLDER 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. + + + Original code under the following Apache License 2.0: + Changes - None + + ---------- + This software is licensed under the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0.html) Please find the Apache license 2.0 statement in the respective file alongside this software. + +Copyright 2015 Jan Hendriks + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + ---------- + */ + +/** + * @file: svmlight.h + * @author: Jan Hendriks (dahoc3150 [at] gmail.com) + * @date: Created on 11. Mai 2011 + * @brief: Wrapper interface for SVMlight, + * @see http://www.cs.cornell.edu/people/tj/svm_light/ for SVMlight details and terms of use + * + */ + +#include "od/common/bindings/svmlight.h" + + + SVMlight::SVMlight(){ + // Init variables + alpha_in = NULL; + kernel_cache = NULL; // Cache not needed with linear kernel + model = (MODEL *) my_malloc(sizeof (MODEL)); + learn_parm = new LEARN_PARM; + kernel_parm = new KERNEL_PARM; + // Init parameters + verbosity = 1; // Show some messages -v 1 + learn_parm->alphafile[0] = ' '; // NULL; // Important, otherwise files with strange/invalid names appear in the working directory + // learn_parm->alphafile = NULL; // Important, otherwise files with strange/invalid names appear in the working directory + learn_parm->biased_hyperplane = 1; + learn_parm->sharedslack = 0; // 1 + learn_parm->skip_final_opt_check = 0; + learn_parm->svm_maxqpsize = 10; + learn_parm->svm_newvarsinqp = 0; + learn_parm->svm_iter_to_shrink = 2; // 2 is for linear; + learn_parm->kernel_cache_size = 40; + learn_parm->maxiter = 100000; + learn_parm->svm_costratio = 1.0; + learn_parm->svm_costratio_unlab = 1.0; + learn_parm->svm_unlabbound = 1E-5; + learn_parm->eps = 0.1; + learn_parm->transduction_posratio = -1.0; + learn_parm->epsilon_crit = 0.001; + learn_parm->epsilon_a = 1E-15; + learn_parm->compute_loo = 0; + learn_parm->rho = 1.0; + learn_parm->xa_depth = 0; + // The HOG paper uses a soft classifier (C = 0.01), set to 0.0 to get the default calculation + learn_parm->svm_c = 0.01; // -c 0.01 + learn_parm->type = REGRESSION; + learn_parm->remove_inconsistent = 0; // -i 0 - Important + kernel_parm->rbf_gamma = 1.0; + kernel_parm->coef_lin = 1; + kernel_parm->coef_const = 1; + kernel_parm->kernel_type = LINEAR; // -t 0 + kernel_parm->poly_degree = 3; + } + + SVMlight::~SVMlight() { + // Cleanup area + // Free the memory used for the cache + if (kernel_cache) + kernel_cache_cleanup(kernel_cache); + free(alpha_in); + free_model(model, 0); + for (i = 0; i < totdoc; i++) + free_example(docs[i], 1); + free(docs); + free(target); + } + + void SVMlight::saveModelToFile(const std::string _modelFileName) { + write_model(const_cast<char*>(_modelFileName.c_str()), model); + } + + void SVMlight::loadModelFromFile(const std::string _modelFileName) { + model = read_model(const_cast<char*>(_modelFileName.c_str())); + } + + void SVMlight::read_problem(char* filename) { + read_documents(filename, &docs, &target, &totwords, &totdoc); + } + + void SVMlight::train() { + svm_learn_regression(docs, target, totdoc, totwords, learn_parm, kernel_parm, &kernel_cache, model); + } + + + void SVMlight::getSingleDetectingVector(std::vector<float>& singleDetectorVector, std::vector<unsigned int>& singleDetectorVectorIndices) { + // Now we use the trained svm to retrieve the single detector vector + DOC** supveclist = model->supvec; + printf("Calculating single descriptor vector out of support vectors (may take some time)\n"); + // Retrieve single detecting vector (v1) from returned ones by calculating vec1 = sum_1_n (alpha_y*x_i). (vec1 is a n x1 column vector. n = feature vector length) + singleDetectorVector.clear(); + singleDetectorVector.resize(model->totwords, 0.); + printf("Resulting vector size %lu\n", singleDetectorVector.size()); + + // Walk over every support vector + for (long ssv = 1; ssv < model->sv_num; ++ssv) { // Don't know what's inside model->supvec[0] ?! + // Get a single support vector + DOC* singleSupportVector = supveclist[ssv]; // Get next support vector + SVECTOR* singleSupportVectorValues = singleSupportVector->fvec; + WORD singleSupportVectorComponent; + // Walk through components of the support vector and populate our detector vector + for (unsigned long singleFeature = 0; singleFeature < model->totwords; ++singleFeature) { + singleSupportVectorComponent = singleSupportVectorValues->words[singleFeature]; + singleDetectorVector.at(singleSupportVectorComponent.wnum-1) += (singleSupportVectorComponent.weight * model->alpha[ssv]); + } + } + } + + float SVMlight::getThreshold() const { + return model->b; + } + + const char * SVMlight::getSVMName() const { + return "SVMlight"; + } + + + /// Singleton + SVMlight * SVMlight::getInstance() { + static SVMlight theInstance; + return &theInstance; + } diff --git a/common/src/utils/ODFeatureDetector2D.cpp b/common/src/utils/ODFeatureDetector2D.cpp index 2b886298..c91912e7 100644 --- a/common/src/utils/ODFeatureDetector2D.cpp +++ b/common/src/utils/ODFeatureDetector2D.cpp @@ -2,7 +2,6 @@ // Created by sarkar on 20.04.15. // -#include <GL/gl.h> #include "od/common/utils/ODFeatureDetector2D.h" namespace od diff --git a/common/src/utils/utils.cpp b/common/src/utils/ODUtils.cpp similarity index 97% rename from common/src/utils/utils.cpp rename to common/src/utils/ODUtils.cpp index 564d5799..f5a96fdc 100644 --- a/common/src/utils/utils.cpp +++ b/common/src/utils/ODUtils.cpp @@ -1,9 +1,8 @@ // // Created by sarkar on 19.06.15. // -#include "od/common/utils/utils.h" -#include <opencv2/imgproc/imgproc.hpp> -#include <boost/functional/hash.hpp> +#include "od/common/utils/ODUtils.h" + namespace od { @@ -179,11 +178,13 @@ namespace od std::string extension = strs[strs.size() - 1]; bool flagfound = false; - for (int exti = 0; exti < exts.size(); exti ++) + for (size_t exti = 0; exti < exts.size(); ++exti) if(file.rfind(exts[exti]) != std::string::npos) - { flagfound = true; break; } + { flagfound = true; + break; + } - if( flagfound == true ) + if(flagfound == true) { #if BOOST_FILESYSTEM_VERSION == 3 std::string path = rel_path_so_far + (itr->path().filename()).string(); diff --git a/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp b/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp index 58bbb040..ba0b865e 100644 --- a/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp +++ b/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp @@ -127,7 +127,7 @@ namespace od } else if(desc_name_.compare("esf") == 0) { shared_ptr<pcl::rec_3d_framework::ESFEstimation<pcl::PointXYZ, pcl::ESFSignature640> > estimator; - estimator.reset(new pcl::rec_3d_framework::ESFEstimation<pcl::PointXYZ, pcl::ESFSignature640>); + estimator = make_shared<pcl::rec_3d_framework::ESFEstimation<pcl::PointXYZ, pcl::ESFSignature640> >(); shared_ptr<pcl::rec_3d_framework::GlobalEstimator<pcl::PointXYZ, pcl::ESFSignature640> > cast_estimator; cast_estimator = dynamic_pointer_cast<pcl::rec_3d_framework::ESFEstimation<pcl::PointXYZ, pcl::ESFSignature640> >( diff --git a/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp b/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp index 09604709..e86a292e 100644 --- a/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp +++ b/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp @@ -75,7 +75,7 @@ namespace od void init(); private: - std::vector<od::shared_ptr<ODDetector2D> > detectors_2d_; + std::vector<shared_ptr<ODDetector2D> > detectors_2d_; std::vector<shared_ptr<ODDetector3D<PointT> > > detectors_3d_; }; @@ -112,8 +112,8 @@ namespace od { //make a list of different algorithms //vector<ODDetector *> detectors = {new ODCascadeDetector(trained_data_location_), new ODHOGDetector(trained_data_location_), new ODCADRecognizer2DLocal(trained_data_location_)}; - detectors_2d_.push_back(new g2d::ODCascadeDetector(trained_data_location_)); - detectors_2d_.push_back(new g2d::ODHOGDetector(trained_data_location_)); + detectors_2d_.push_back(make_shared<g2d::ODCascadeDetector>(trained_data_location_)); + detectors_2d_.push_back(make_shared<g2d::ODHOGDetector>(trained_data_location_)); // detectors.push_back(new ODCADRecognizer2DLocal(trained_data_location_)); for(size_t i = 0; i < detectors_2d_.size(); ++i) @@ -127,7 +127,7 @@ namespace od void ODDetectorMultiAlgo<PointT>::init() { //3D - detectors_3d_.push_back(new g3d::ODCADDetector3DGlobal<PointT>(trained_data_location_, training_input_location_)); + detectors_3d_.push_back( make_shared<g3d::ODCADDetector3DGlobal<PointT> >(trained_data_location_, training_input_location_)); for(size_t i = 0; i < detectors_3d_.size(); ++i) { detectors_3d_[i]->init(); @@ -150,7 +150,7 @@ namespace od template<typename PointT> shared_ptr<ODDetections3D> ODDetectorMultiAlgo<PointT>::detectOmni(shared_ptr<ODScenePointCloud<PointT> > scene) { - shared_ptr<ODDetections3D> detections_all = make_shared<ODDetections3D>; + shared_ptr<ODDetections3D> detections_all = make_shared<ODDetections3D>(); for(size_t i = 0; i < detectors_3d_.size(); i++) { shared_ptr<ODDetections3D> detections_individual = detectors_3d_[i]->detectOmni(scene); diff --git a/detectors/include/od/detectors/global2D/ODFaceRecognizer.h b/detectors/include/od/detectors/global2D/ODFaceRecognizer.h index 9c3330ea..7d690d23 100644 --- a/detectors/include/od/detectors/global2D/ODFaceRecognizer.h +++ b/detectors/include/od/detectors/global2D/ODFaceRecognizer.h @@ -28,16 +28,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // #pragma once #include "od/common/pipeline/ODDetector.h" -#include "od/common/pipeline/ODTrainer.h" -#include "od/common/utils/utils.h" - -#include <opencv2/core.hpp> #include <opencv2/face.hpp> -#include <opencv2/highgui.hpp> -#include <opencv2/imgproc.hpp> -#include <opencv2/objdetect.hpp> - -#include <string> namespace od { @@ -73,7 +64,6 @@ namespace od shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene); - const FaceRecogType & getRecogtype() const { return recog_type_; @@ -109,15 +99,15 @@ namespace od cv::Ptr<cv::face::FaceRecognizer> cv_recognizer_; FaceRecogType recog_type_; - int im_width_; - int im_height_; + unsigned int im_width_; + unsigned int im_height_; int num_components_; double threshold_; private: - void read_csv(const std::string & file_name, std::vector<cv::Mat> & images, std::vector<int> & labels, const std::string & separator = ';'); + void read_csv(const std::string & file_name, std::vector<cv::Mat> & images, std::vector<int> & labels, const std::string & separator = std::string(";")); }; /** \example objectdetector/od_image_facerecog.cpp diff --git a/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h b/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h index 172b654d..c286a8e5 100644 --- a/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h +++ b/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h @@ -29,7 +29,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #pragma once #include "od/common/pipeline/ODDetector.h" #include "od/common/pipeline/ODScene.h" -#include "od/common/utils/utils.h" +#include "od/common/utils/ODUtils.h" #include "od/common/utils/ODFeatureDetector2D.h" #include <opencv2/opencv.hpp> diff --git a/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h b/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h index 6be1f141..4d221b87 100644 --- a/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h +++ b/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h @@ -29,11 +29,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #pragma once #include "od/common/pipeline/ODDetector.h" #include "od/common/pipeline/ODScene.h" -#include "od/common/utils/utils.h" +#include "od/common/utils/ODUtils.h" #include "od/common/utils/ODFeatureDetector2D.h" +#include "od/common/utils/ODShared_pointers.h" +#include "od/common/bindings/svmlight.h" #include <iostream> -#include <opencv2/opencv.hpp> +#include <opencv2/objdetect.hpp> namespace od { @@ -62,12 +64,8 @@ namespace od ODHOGDetector(const std::string & trained_data_location_ = "", const cv::Size & win_size = cv::Size(64,128), - const cv::Size & block_size = cv::Size(16,16), const cv::Size & block_stride = cv::Size(8,8), const cv::Size & cell_size = cv::Size(8,8), - float hit_threshold = 0.0): - ODDetector2D(trained_data_location_), win_size_(win_size), block_size_(block_size), block_stride_(block_stride), - cell_size_(cell_size), hit_threshold_(hit_threshold), hog_(win_size_, block_size, block_stride, cell_size, 9, 1, -1, - cv::HOGDescriptor::L2Hys, 0.2, false, cv::HOGDescriptor::DEFAULT_NLEVELS){} - + const cv::Size & block_size = cv::Size(16,16), const cv::Size & block_stride = cv::Size(8,8), + const cv::Size & cell_size = cv::Size(8,8), float hit_threshold = 0.0); void init(); void load(const std::string & file_name); @@ -113,7 +111,7 @@ namespace od float hit_threshold_; cv::HOGDescriptor hog_; - SVMType svmtype_; + SVMType svm_type_; }; diff --git a/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h b/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h index 90e345c0..0143b30e 100644 --- a/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h +++ b/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h @@ -27,9 +27,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 13.08.15. // #pragma once -//#include <opencv2/objdetect.hpp> +#include <opencv2/objdetect.hpp> +#include <opencv2/opencv.hpp> + #include "od/common/pipeline/ODTrainer.h" -//#include "od/common/utils/utils.h" +//#include "od/common/utils/ODUtils.h" +#include <fstream> + +//binding class for svmlight +#include "od/common/bindings/svmlight.h" namespace od { @@ -79,12 +85,12 @@ namespace od int getNOFeaturesNeg() const { - return nofeatures_neg_; + return no_features_neg_; } void setNOFeaturesNeg(int featno) { - no_features_neg_ = featno; + no_features_neg_ = featno; } const cv::Point & getStartHogPos() const @@ -149,12 +155,12 @@ namespace od bool isTrainHardNegetive() const { - return train_hard_negetive_; + return train_hard_negative_; } - void setTrainHardNegetive(bool train_hard_negetive) + void setTrainHardNegetive(bool train_hard_negative) { - train_hard_negetive_ = train_hard_negetive; + train_hard_negative_ = train_hard_negative; } double getHitThreshold() const @@ -174,13 +180,13 @@ namespace od //algo specific cv::Size training_padding_; cv::Point start_hog_pos_; - int no_features_neg__; + int no_features_neg_; cv::Size win_stride_; - bool train_hard_negetive_; + bool train_hard_negative_; //directories std::string pos_samples_dir_; - std::string neg_samples_dir__; + std::string neg_samples_dir_; //properties retained double hit_threshold_; @@ -208,8 +214,7 @@ namespace od - void saveDescriptorVectorToFile(std::vector<float> & descriptor_vector, std::vector<unsigned int> & vector_indices, - std::string file_name); + void saveDescriptorVectorToFile(const std::vector<float> & descriptor_vector, const std::string & file_name); void handleNegetivefile(const std::string & image_filename, cv::HOGDescriptor & hog, std::fstream & file); diff --git a/detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h b/detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h index 5189aad7..8d60a948 100644 --- a/detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h +++ b/detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h @@ -27,16 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 16.06.15. // #pragma once -#include <iostream> -#include <pcl/pcl_macros.h> -#include <pcl/apps/3d_rec_framework/pipeline/global_nn_classifier.h> #include <pcl/apps/3d_rec_framework/pc_source/mesh_source.h> -#include <pcl/apps/3d_rec_framework/feature_wrapper/global/vfh_estimator.h> -#include <pcl/apps/3d_rec_framework/feature_wrapper/global/esf_estimator.h> -#include <pcl/apps/3d_rec_framework/feature_wrapper/global/cvfh_estimator.h> -#include <pcl/apps/3d_rec_framework/utils/metrics.h> -#include <pcl/visualization/pcl_visualizer.h> -#include <pcl/apps/dominant_plane_segmentation.h> #include <pcl/console/parse.h> #include "od/common/pipeline/ODTrainer.h" diff --git a/detectors/include/od/detectors/local2D/ODImageLocalMatching.h b/detectors/include/od/detectors/local2D/ODImageLocalMatching.h index 21a0d0a1..98658f4f 100644 --- a/detectors/include/od/detectors/local2D/ODImageLocalMatching.h +++ b/detectors/include/od/detectors/local2D/ODImageLocalMatching.h @@ -31,7 +31,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/common/pipeline/ODScene.h" #include "od/common/pipeline/ODTrainer.h" #include "od/common/pipeline/ODDetector.h" -#include "od/common/pipeline/ODObjectDetector.h" namespace od { @@ -47,7 +46,9 @@ namespace od { public: + ODImageLocalMatchingTrainer(const std::string & training_input_location_, const std::string & training_data_location_); + }; /** \brief ODImageLocalMatchingDetector @@ -59,6 +60,7 @@ namespace od { public: + ODImageLocalMatchingDetector(const std::string & training_data_location_); }; diff --git a/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h b/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h index 4b946f77..4e382e20 100644 --- a/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h +++ b/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h @@ -29,31 +29,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // #pragma once #include "od/detectors/local2D/ODImageLocalMatching.h" - -#include "od/common/pipeline/ODDetector.h" #include "od/common/pipeline/ODScene.h" -#include "od/common/utils/utils.h" -#include "od/common/utils/ODFeatureDetector2D.h" #include "od/detectors/local2D/simple_ransac_detection/Mesh.h" -#include "od/detectors/local2D/simple_ransac_detection/Model.h" -#include "od/detectors/local2D/simple_ransac_detection/PnPProblem.h" #include "od/detectors/local2D/simple_ransac_detection/RobustMatcher.h" -#include "od/detectors/local2D/simple_ransac_detection/ModelRegistration.h" -#include "od/detectors/local2D/simple_ransac_detection/Utils.h" -#include "od/detectors/local2D/simple_ransac_detection/Utils.h" #include <iostream> -#include <opencv2/core/core.hpp> -#include <opencv2/core/utility.hpp> -#include <opencv2/highgui/highgui.hpp> -#include <opencv2/imgproc/imgproc.hpp> -#include <opencv2/calib3d/calib3d.hpp> -#include <opencv2/video/tracking.hpp> -#include <opencv2/xfeatures2d.hpp> -#include <opencv2/viz.hpp> - - namespace od { diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvReader.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvReader.h index 1f8cf31e..cf530ce9 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvReader.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvReader.h @@ -21,7 +21,7 @@ namespace od { * @param separator - The separator character between words per line * @return */ - CsvReader(const std::string & path, const std::string & separator = ' '); + CsvReader(const std::string & path, const std::string & separator = std::string(" ")); /** * Read a plane text file with .ply format diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/PnPProblem.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/PnPProblem.h index 2a03bae8..71add135 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/PnPProblem.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/PnPProblem.h @@ -12,6 +12,7 @@ #include <sstream> #include "od/detectors/local2D/simple_ransac_detection/Mesh.h" #include "od/detectors/local2D/simple_ransac_detection/ModelRegistration.h" +#include "od/common/utils/ODShared_pointers.h" class Mesh; class Ray; @@ -35,7 +36,7 @@ namespace od { cv::Point3f getNearest3DPoint(std::vector<cv::Point3f> & points_list, const cv::Point3f & origin); bool backproject2DPoint(const Mesh * mesh, const cv::Point2f & point2d, cv::Point3f & point3d); - bool intersectMollerTrumbore(Ray & ray, Triangle & Triangle, shared_ptr<double> out); + bool intersectMollerTrumbore(Ray & ray, Triangle & Triangle, double & out); std::vector<cv::Point2f> verifyPoints(shared_ptr<Mesh> mesh); cv::Point2f backproject3DPoint(const cv::Point3f & point3d); bool estimatePose(const std::vector<cv::Point3f> & list_points3d, const std::vector<cv::Point2f> & list_points2d, int flags); diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/RobustMatcher.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/RobustMatcher.h index 97b17b00..6e1b9f61 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/RobustMatcher.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/RobustMatcher.h @@ -13,7 +13,7 @@ #include <opencv2/xfeatures2d.hpp> #include <opencv2/ml.hpp> #include "od/common/utils/ODFeatureDetector2D.h" -#include "od/common/utils/utils.h" +#include "od/common/utils/ODUtils.h" #include "od/detectors/local2D/simple_ransac_detection/Model.h" #include "od/detectors/local2D/simple_ransac_detection/Utils.h" diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/Utils.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/Utils.h index e6a9e714..04b3300d 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/Utils.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/Utils.h @@ -73,15 +73,6 @@ namespace od { // Converts a given Euler angles to Rotation Matrix cv::Mat euler2rot(const cv::Mat & euler); - // Converts a given string to an integer - int StringToInt (const std::string & Text ); - - // Converts a given float to a string - std::string FloatToString (float Number ); - - // Converts a given integer to a string - std::string IntToString (int Number ); - } } diff --git a/detectors/include/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h b/detectors/include/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h index a4f65b17..b29866d6 100644 --- a/detectors/include/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h +++ b/detectors/include/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h @@ -29,7 +29,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // #pragma once #include "od/common/pipeline/ODTrainer.h" -#include "od/common/utils/utils.h" +#include "od/common/utils/ODUtils.h" #include "od/detectors/local2D/ODImageLocalMatching.h" #include <string> diff --git a/detectors/src/global2D/ODFaceRecognizer.cpp b/detectors/src/global2D/ODFaceRecognizer.cpp index 9ed58df7..fbcafb81 100644 --- a/detectors/src/global2D/ODFaceRecognizer.cpp +++ b/detectors/src/global2D/ODFaceRecognizer.cpp @@ -105,7 +105,7 @@ namespace od im_height_ = images[0].rows; } - shared_ptr<ODDetections> ODFaceRecognizer::detect(ODSceneImage * scene) + shared_ptr<ODDetections> ODFaceRecognizer::detect(shared_ptr<ODSceneImage> scene) { cv::Mat face_edited; cv::cvtColor(scene->getCVImage(), face_edited, CV_BGR2GRAY); @@ -127,7 +127,8 @@ namespace od } - void ODFaceRecognizer::read_csv(const std::string & filename, std::vector<cv::Mat> & images, std::vector<int> & labels, const std::string & separator) + void ODFaceRecognizer::read_csv(const std::string & filename, std::vector<cv::Mat> & images, std::vector<int> & labels, + const std::string & separator) { std::ifstream file(filename.c_str(), std::ifstream::in); if(!file) @@ -139,7 +140,7 @@ namespace od while(getline(file, line)) { std::stringstream liness(line); - getline(liness, path, separator.c_str()); + getline(liness, path, *separator.c_str()); getline(liness, classlabel); if(!path.empty() && !classlabel.empty()) { diff --git a/detectors/src/global2D/detection/ODHOGDetector.cpp b/detectors/src/global2D/detection/ODHOGDetector.cpp index 34efe227..e3e9f017 100644 --- a/detectors/src/global2D/detection/ODHOGDetector.cpp +++ b/detectors/src/global2D/detection/ODHOGDetector.cpp @@ -29,20 +29,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/global2D/detection/ODHOGDetector.h" -using namespace std; - namespace od { namespace g2d { - ODHOGDetector::ODHOGDetector(const std::string & trained_data_location_ = "", const cv::Size & win_size = cv::Size(64,128), - const cv::Size & block_size = cv::Size(16,16), const cv::Size & block_stride = cv::Size(8,8), - const cv::Size & cell_size = cv::Size(8,8), float hit_threshold = 0.0) + ODHOGDetector::ODHOGDetector(const std::string & trained_data_location_, const cv::Size & win_size, const cv::Size & block_size, + const cv::Size & block_stride, const cv::Size & cell_size, float hit_threshold): + ODDetector2D(trained_data_location_), win_size_(win_size), block_size_(block_size), block_stride_(block_stride), + cell_size_(cell_size), hit_threshold_(hit_threshold), hog_(win_size, block_size, block_stride, cell_size, 9, 1, -1, + cv::HOGDescriptor::L2Hys, 0.2, false, cv::HOGDescriptor::DEFAULT_NLEVELS) { TRAINED_LOCATION_DENTIFIER_ = "HOG"; TRAINED_DATA_ID_ = "hog.xml"; - metainfo_ = true; + meta_info_ = true; svm_type_ = OD_DEFAULT_PEOPLE; if (trained_data_location_ != "") @@ -56,20 +56,20 @@ namespace od { case OD_DEFAULT_PEOPLE: hog_.setSVMDetector(cv::HOGDescriptor::getDefaultPeopleDetector()); - cout << "HOG TYPE: OpenCV Default People" << endl; + std::cout << "HOG TYPE: OpenCV Default People" << std::endl; //hog_.save(getSpecificTrainingDataLocation() + "/defaultpeople." + TRAINED_DATA_EXT_); break; case OD_DAIMLER_PEOPLE: - hog_.win_size = cv::Size(48, 96); - hit_threshold = 1.2; + hog_.winSize = cv::Size(48, 96); + hit_threshold_ = 1.2; hog_.setSVMDetector(cv::HOGDescriptor::getDaimlerPeopleDetector()); - cout << "HOG TYPE: OpenCV Daimler People" << endl; + std::cout << "HOG TYPE: OpenCV Daimler People" << std::endl; //hog_.save(getSpecificTrainingDataLocation() + "/daimlerpeople." + TRAINED_DATA_EXT_); break; case OD_FILE: - string hogfile = FileUtils::getFirstFile(getSpecificTrainingDataLocation(), TRAINED_DATA_ID_); + std::string hogfile = fileutils::getFirstFile(getSpecificTrainingDataLocation(), TRAINED_DATA_ID_); load(hogfile); - cout << "HOG TYPE: Custom HOG features loaded from: " << hogfile << endl; + std::cout << "HOG TYPE: Custom HOG features loaded from: " << hogfile << std::endl; break; //dont set anything for custom, it is to be set by the user by setSVMDetector } @@ -90,24 +90,24 @@ namespace od hog_.setSVMDetector(svm_detector); } - shared_ptr<ODDetections2D> ODHOGDetector::detectOmni(ODSceneImage * scene) + shared_ptr<ODDetections2D> ODHOGDetector::detectOmni(shared_ptr<ODSceneImage> scene) { //always create a detection - shared_ptr<ODDetections2D> detections = make_shared<ODDetections2D>(); + shared_ptr<ODDetections2D> detections = make_shared<ODDetections2D>(); - vector<cv::Rect> found, found_filtered; - hog_.detectMultiScale(scene->getCVImage(), found, hit_threshold, cv::Size(8, 8), cv::Size(32, 32), 1.05, 2); + std::vector<cv::Rect> found, found_filtered; + hog_.detectMultiScale(scene->getCVImage(), found, hit_threshold_, cv::Size(8, 8), cv::Size(32, 32), 1.05, 2); cv::Mat viz = scene->getCVImage().clone(); - for(int i = 0; i < found.size(); i++) + for(size_t i = 0; i < found.size(); ++i) { - shared_ptr<ODDetection2D> detection2D = make_shared<ODDetection2D>; + shared_ptr<ODDetection2D> detection2D = make_shared<ODDetection2D>(); detection2D->setBoundingBox(found[i]); detection2D->setId("PEOPLE"); detection2D->setType(ODDetection::OD_DETECTION_CLASS); detections->push_back(detection2D); - if(metainfo_) + if(meta_info_) { cv::Rect r = found[i]; r.x += cvRound(r.width * 0.1); @@ -125,17 +125,17 @@ namespace od shared_ptr<ODDetections> ODHOGDetector::detect(shared_ptr<ODSceneImage> scene) { //always create a detection - shared_ptr<ODDetections> detections = make_shared<ODDetections>; + shared_ptr<ODDetections> detections = make_shared<ODDetections>(); cv::Mat scaled_window; - cv::resize(scene->getCVImage(), scaled_window, hog_.win_size); + cv::resize(scene->getCVImage(), scaled_window, hog_.winSize); std::vector<cv::Point> found_locations; - hog_.detect(scene->getCVImage(), found_locations, hitThreshold); + hog_.detect(scene->getCVImage(), found_locations, hit_threshold_); if(!found_locations.empty()) { - shared_ptr<ODDetection2D> detection2D = make_shared<ODDetection2D>; + shared_ptr<ODDetection2D> detection2D = make_shared<ODDetection2D>(); detection2D->setId("PEOPLE"); detection2D->setType(ODDetection::OD_DETECTION_CLASS); detections->push_back(detection2D); @@ -147,15 +147,15 @@ namespace od void ODHOGDetector::setTrainedDataLocation(const std::string & trained_data_location) { trained_data_location_ = trained_data_location; - svmtype_ = OD_FILE; + svm_type_ = OD_FILE; } - const SVMType & ODHOGDetector::getSvmtype() const + const ODHOGDetector::SVMType & ODHOGDetector::getSvmtype() const { return svm_type_; } - void ODHOGDetector::setSvmtype(const SVMType & svm_type) + void ODHOGDetector::setSvmtype(const ODHOGDetector::SVMType & svm_type) { svm_type_ = svm_type; } @@ -172,7 +172,7 @@ namespace od const cv::Size & ODHOGDetector::getBlockSize() const { - return block_size; + return block_size_; } void ODHOGDetector::setBlockSize(const cv::Size & block_size) @@ -180,7 +180,7 @@ namespace od block_size_ = block_size; } - const cv::Size & getBlockStride() const + const cv::Size & ODHOGDetector::getBlockStride() const { return block_stride_; } @@ -212,13 +212,13 @@ namespace od void ODHOGDetector::setSVMFromFile(const std::string & file_name) { - vector<float> descriptor_vector; - printf("Reading descriptor vector from file '%s'\n", file_name.c_str()); + std::vector<float> descriptor_vector; + std::cout << "Reading descriptor vector from file " << file_name << std::endl;; - ifstream file; + std::ifstream file; float percent; - file.open(file_name.c_str(), ios::in); - if (file.good() && file.is_open()) { + file.open(file_name.c_str(), std::ios::in); + if(file.good() && file.is_open()) { double d; while(file >> d) @@ -234,11 +234,11 @@ namespace od void ODHOGDetector::printParameters() { - cout << "winSize: " << win_size_ << endl; - cout << "blockSize: " << block_size_ << endl; - cout << "blockStride: " << block_stride_ << endl; - cout << "cellSize: " << cell_size_ << endl; - cout << "hitThreshold: " << hit_threshold_ << endl; + std::cout << "winSize: " << win_size_ << std::endl; + std::cout << "blockSize: " << block_size_ << std::endl; + std::cout << "blockStride: " << block_stride_ << std::endl; + std::cout << "cellSize: " << cell_size_ << std::endl; + std::cout << "hitThreshold: " << hit_threshold_ << std::endl; } } } diff --git a/detectors/src/global2D/training/ODHOGTrainer.cpp b/detectors/src/global2D/training/ODHOGTrainer.cpp index 5f101859..3b0753fe 100644 --- a/detectors/src/global2D/training/ODHOGTrainer.cpp +++ b/detectors/src/global2D/training/ODHOGTrainer.cpp @@ -31,17 +31,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/global2D/training/ODHOGTrainer.h" -#include <fstream> -#include <algorithm> -#include <iterator> - -#include <opencv2/opencv.hpp> -#include <opencv2/highgui/highgui.hpp> -#include <opencv2/imgproc/imgproc.hpp> - -//binding class for svmlight -#include "od/common/bindings/svmlight.h" - #define TRAINHOG_SVM_TO_TRAIN SVMlight namespace od @@ -71,19 +60,19 @@ namespace od //algo parameter init training_padding_ = cv::Size(0, 0); start_hog_pos_ = cv::Point(15, 15); - nofeatures_neg_ = 10; + no_features_neg_ = 10; win_stride_ = cv::Size(); - train_hard_negetive_ = false; + train_hard_negative_ = false; if(trained_data_location_ != "") { - pos_samples_dir = training_input_location_ + "/pos"; - neg_samples_dir = training_input_location_ + "/neg"; + pos_samples_dir_ = training_input_location_ + "/pos"; + neg_samples_dir_ = training_input_location_ + "/neg"; } //internal data - FileUtils::createTrainingDir(getSpecificTrainingDataLocation()); + fileutils::createTrainingDir(getSpecificTrainingDataLocation()); features_file_ = getSpecificTrainingDataLocation() + "/features.dat"; svm_model_file_ = getSpecificTrainingDataLocation() + "/svmlightmodel.dat"; svm_model_hard_ = getSpecificTrainingDataLocation() + "/svmlightmodelhard.dat"; @@ -93,14 +82,12 @@ namespace od } - void ODHOGTrainer::saveDescriptorVectorToFile(std::vector<float> & descriptor_vector, std::vector<unsigned int> & vector_indices, - const std::string & file_name) + void ODHOGTrainer::saveDescriptorVectorToFile(const std::vector<float> & descriptor_vector, const std::string & file_name) { std::cout << "Saving descriptor vector to file " << file_name << std::endl; - std::string separator = " "; // Use blank as default separator between single features - fstream file; + std::fstream file; float percent; - file.open(file_name.c_str(), ios::out); + file.open(file_name.c_str(), std::ios::out); const unsigned int descriptors_num = descriptor_vector.size(); if(file.good() && file.is_open()) @@ -112,11 +99,9 @@ namespace od if((feature % 10 == 0) || (feature == (descriptors_num - 1))) { percent = ((1 + feature) * 100 / descriptors_num); - printf("%4u (%3.0f%%)", feature, percent); - fflush(stdout); - resetCursor(); + std::cout << feature << "(" <<percent << "%)" << "\xd"; } - file << descriptor_vector[feature] << separator; + file << descriptor_vector[feature] << " "; } printf("\n"); file << std::endl; @@ -125,16 +110,16 @@ namespace od } } - void ODHOGTrainer::calculateFeaturesFromInput(const std::string & image_file_name, std::vector<float> & feature_vector, HOGDescriptor & hog) + void ODHOGTrainer::calculateFeaturesFromInput(const std::string & image_file_name, std::vector<float> & feature_vector, cv::HOGDescriptor & hog) { cv::Mat image_data_orig, image_data; - image_data_orig = imread(image_file_name, 0); + image_data_orig = cv::imread(image_file_name, 0); if(image_data_orig.empty()) { - featureVector.clear(); + feature_vector.clear(); std::cout << "Error: HOG image " << image_file_name << " is empty, features calculation skipped!" << std::endl; return; } @@ -147,18 +132,19 @@ namespace od image_data_orig.release(); // Release the image again after features are extracted } - void ODHOGTrainer::detectTrainingSetTest(const HOGDescriptor & hog, double hit_threshold, const std::vector<std::string> & pos_file_names, - const vector<string> & neg_file_names) + void ODHOGTrainer::detectTrainingSetTest(const cv::HOGDescriptor & hog, double hit_threshold, const std::vector<std::string> & pos_file_names, + const std::vector<std::string> & neg_file_names) { unsigned int true_positives = 0; unsigned int true_negatives = 0; unsigned int false_positives = 0; unsigned int false_negatives = 0; std::vector<cv::Point> found_detection; + cv::Mat image_data; // Walk over positive training samples, generate images and detect - for(auto & pf : pos_file_names.begin()) + for(auto & pf : pos_file_names) { - const cv::Mat image_data = cv::imread(pf, 0); + image_data = cv::imread(pf, 0); hog.detect(image_data, found_detection, hit_threshold, win_stride_, training_padding_); if(found_detection.size() > 0) { @@ -170,10 +156,10 @@ namespace od } } // Walk over negative training samples, generate images and detect - for(auto & nf : negFileNames) + for(auto & nf : neg_file_names) { - const cv::Mat imageData = cv::imread(nf, 0); - hog.detect(image_data, found_detection, hit_threshold, win_stride, training_padding); + image_data = cv::imread(nf, 0); + hog.detect(image_data, found_detection, hit_threshold, win_stride_, training_padding_); if(found_detection.size() > 0) { false_positives += found_detection.size(); @@ -183,12 +169,12 @@ namespace od } } - std::cout << "Results: " << std::endl; << "\tTrue Positives: " << true_positives << std::endl << "\tTrue Negatives: " + std::cout << "Results: " << std::endl << "\tTrue Positives: " << true_positives << std::endl << "\tTrue Negatives: " << true_negatives << "\tFalse Positives: " << false_positives << std::endl << " \tFalse Negatives:" << false_negatives << std::endl; } - void ODHOGTrainer::calculateFeaturesFromImageLoc(const Mat & image_data, std::vector<float> & feature_vector, const HOGDescriptor & hog, + void ODHOGTrainer::calculateFeaturesFromImageLoc(const cv::Mat & image_data, std::vector<float> & feature_vector, const cv::HOGDescriptor & hog, const cv::Point & start_pos) { @@ -197,14 +183,14 @@ namespace od hog.compute(image_data, feature_vector, win_stride_, training_padding_, locations); } - void ODHOGTrainer::handleNegetivefile(const std::string & image_file_name, HOGDescriptor & hog, std::fstream & file) + void ODHOGTrainer::handleNegetivefile(const std::string & image_file_name, cv::HOGDescriptor & hog, std::fstream & file) { cv::Mat image_data = cv::imread(image_file_name, 0); //cout << "Image size : " << imageData.size() << "random points : " << endl; //get hog at random 10 image location - for(size_t i = 0; i < nofeatures_neg; ++i) + for(size_t i = 0; i < no_features_neg_; ++i) { cv::Point feat_loc(rand() % (image_data.cols - win_size_.width), rand() % (image_data.rows - win_size_.height)); //cout << feat_loc << endl; @@ -213,7 +199,7 @@ namespace od std::vector<float> feature_vector; calculateFeaturesFromImageLoc(image_data, feature_vector, hog, feat_loc); - if(!featureVector.empty()) + if(!feature_vector.empty()) { /* Put positive or negative sample class to file, * true=positive, false=negative, @@ -221,7 +207,7 @@ namespace od */ file << "-1"; // Save feature vector components - for(size_t feature = 0; feature < featureVector.size(); ++feature) + for(size_t feature = 0; feature < feature_vector.size(); ++feature) { file << " " << (feature + 1) << ":" << feature_vector[feature]; } @@ -231,7 +217,7 @@ namespace od } - void ODHOGTrainer::createHardTrainingData(const HOGDescriptor & hog, double hit_threshold, const std::vector<std::string> & neg_file_names) + void ODHOGTrainer::createHardTrainingData(const cv::HOGDescriptor & hog, double hit_threshold, const std::vector<std::string> & neg_file_names) { std::fstream file; file.open(features_file_.c_str(), std::fstream::app); @@ -266,7 +252,7 @@ namespace od resize(neg_img, resized_neg, win_size_); // imshow("hardneg", resized_neg); waitKey(4000); - cv::vector<float> feature_vector; + std::vector<float> feature_vector; calculateFeaturesFromImageLoc(resized_neg, feature_vector, hog, cv::Point(0, 0)); if(!feature_vector.empty()) @@ -274,7 +260,7 @@ namespace od file << "-1"; for(size_t feature = 0; feature < feature_vector.size(); ++feature) { - file << " " << (feature + 1) << ":" << featureVector.at(feature); + file << " " << (feature + 1) << ":" << feature_vector.at(feature); } file << std::endl; } @@ -286,15 +272,15 @@ namespace od file.close(); } - double ODHOGTrainer::trainWithSVMLight(const std::string & svm_model_file, const string & svm_descriptor_file, + double ODHOGTrainer::trainWithSVMLight(const std::string & svm_model_file, const std::string & svm_descriptor_file, std::vector<float> & descriptor_vector) { //training takes featurefile as input, produces hitthreshold and vector as output - std:: << "Calling " << TRAINHOG_SVM_TO_TRAIN::getInstance()->getSVMName() << std::endl; + std::cout << "Calling " << TRAINHOG_SVM_TO_TRAIN::getInstance()->getSVMName() << std::endl; TRAINHOG_SVM_TO_TRAIN::getInstance()->read_problem(const_cast<char *> (features_file_.c_str())); TRAINHOG_SVM_TO_TRAIN::getInstance()->train(); // Call the core libsvm training procedure std::cout << "Training done, saving model file!"<< std::endl; - TRAINHOG_SVM_TO_TRAIN::getInstance()->saveModelToFile(svmModelFile); + TRAINHOG_SVM_TO_TRAIN::getInstance()->saveModelToFile(svm_model_file); std::cout << "Generating representative single HOG feature vector using svmlight!" << std::endl; @@ -303,10 +289,10 @@ namespace od // Generate a single detecting feature vector (v1 | b) from the trained support vectors, for use e.g. with the HOG algorithm TRAINHOG_SVM_TO_TRAIN::getInstance()->getSingleDetectingVector(descriptor_vector, descriptor_vector_indices); // And save the precious to file system - saveDescriptorVectorToFile(descriptor_vector, descriptor_vector_indices, svm_descriptor_file); + saveDescriptorVectorToFile(descriptor_vector, svm_descriptor_file); // Detector detection tolerance threshold - hit_threshold_ = TRAINHOG_SVM_TO_TRAIN::getInstance()->getThreshold() - return hit_threshold; + hit_threshold_ = TRAINHOG_SVM_TO_TRAIN::getInstance()->getThreshold(); + return hit_threshold_; } int ODHOGTrainer::train() @@ -319,8 +305,8 @@ namespace od valid_extensions.push_back(".png"); valid_extensions.push_back(".ppm"); - FileUtils::getFilesInDirectoryRec(pos_samples_dir_, valid_extensions, positive_training_images); - FileUtils::getFilesInDirectoryRec(neg_samples_dir_, valid_extensions, negative_training_images); + fileutils::getFilesInDirectoryRec(pos_samples_dir_, valid_extensions, positive_training_images); + fileutils::getFilesInDirectoryRec(neg_samples_dir_, valid_extensions, negative_training_images); unsigned int pos_size = positive_training_images.size(); unsigned int neg_size = negative_training_images.size(); @@ -374,7 +360,7 @@ namespace od // Iterate over NEG IMAGES for(auto & ni : negative_training_images) { - handleNegetivefile(ni, hog_, File); + handleNegetivefile(ni, hog_, file); } std::cout << std::endl; @@ -383,14 +369,14 @@ namespace od } else { - std::cout << "Error opening file " << features_file; + std::cout << "Error opening file " << features_file_; return EXIT_FAILURE; } //train them with SVM std::vector<float> descriptor_vector; - hit_threshold_ = trainWithSVMLight(svm_model_file, descriptor_vector_file, descriptor_vector); + hit_threshold_ = trainWithSVMLight(svm_model_file_, descriptor_vector_file_, descriptor_vector); // Pseudo test our custom detecting vector hog_.setSVMDetector(descriptor_vector); @@ -403,10 +389,10 @@ namespace od //create hard training examples createHardTrainingData(hog_, hit_threshold_, negative_training_images); //train again - hit_threshold_ = trainWithSVMLight(svm_model_hard, descriptor_vector_hard, descriptor_vector); + hit_threshold_ = trainWithSVMLight(svm_model_hard_, descriptor_vector_hard_, descriptor_vector); // Pseudo test our custom detecting vector - hog_.setSVMDetector(descriptorVector); + hog_.setSVMDetector(descriptor_vector); std::cout << "Testing training phase using training set as test set after HARD EXAMPLES (just to check if training is ok - no detection quality conclusion with this!)" << std::endl; detectTrainingSetTest(hog_, hit_threshold_, positive_training_images, negative_training_images); @@ -424,7 +410,7 @@ namespace od std::ifstream file; float percent; - file.open(fileName.c_str(), ios::in); + file.open(file_name.c_str(), std::ios::in); if(file.good() && file.is_open()) { @@ -442,7 +428,7 @@ namespace od cv::FileStorage fs(file_name, cv::FileStorage::WRITE); fs << "hitThreshold" << hit_threshold_; - hog_.write(fs, FileStorage::getDefaultObjectName(file_name)); + hog_.write(fs, cv::FileStorage::getDefaultObjectName(file_name)); } } diff --git a/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp b/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp index 8887a735..47cee9af 100644 --- a/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp +++ b/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp @@ -319,7 +319,7 @@ namespace od if(inliers_idx.rows < min_inliers_) return false; - std::cout << "RECOGNIZED: " << model.id_ << std::endl; + std::cout << "Recognized: " << model.id_ << std::endl; //else everything is fine; report the detection if(!detection3D) detection3D = make_shared<ODDetection3D>(); diff --git a/detectors/src/local2D/simple_ransac_detection/CsvReader.cpp b/detectors/src/local2D/simple_ransac_detection/CsvReader.cpp index e7f3eeca..887a5b52 100644 --- a/detectors/src/local2D/simple_ransac_detection/CsvReader.cpp +++ b/detectors/src/local2D/simple_ransac_detection/CsvReader.cpp @@ -26,10 +26,10 @@ namespace od { // read header if(!end_header) { - getline(liness, tmp_str, separator_.c_str()); + getline(liness, tmp_str, *separator_.c_str()); if(tmp_str == "element") { - getline(liness, tmp_str, separator_.c_str()); + getline(liness, tmp_str, *separator_.c_str()); getline(liness, n); if(tmp_str == "vertex") num_vertex = std::stoi(n); @@ -47,8 +47,8 @@ namespace od { if(!end_vertex && count < num_vertex) { std::string x, y, z; - getline(liness, x, separator_.c_str()); - getline(liness, y, _separator_); + getline(liness, x, *separator_.c_str()); + getline(liness, y, *separator_.c_str()); getline(liness, z); cv::Point3f tmp_p; @@ -68,9 +68,9 @@ namespace od { else if(end_vertex && count < num_triangles) { std::string num_pts_per_face, id0, id1, id2; - getline(liness, num_pts_per_face, _separator_); - getline(liness, id0, _separator_); - getline(liness, id1, _separator_); + getline(liness, num_pts_per_face, *separator_.c_str()); + getline(liness, id0, *separator_.c_str()); + getline(liness, id1, *separator_.c_str()); getline(liness, id2); std::vector<int> tmp_triangle(3); diff --git a/detectors/src/local2D/simple_ransac_detection/PnPProblem.cpp b/detectors/src/local2D/simple_ransac_detection/PnPProblem.cpp index 0429bb20..6cd060fc 100644 --- a/detectors/src/local2D/simple_ransac_detection/PnPProblem.cpp +++ b/detectors/src/local2D/simple_ransac_detection/PnPProblem.cpp @@ -285,7 +285,7 @@ namespace od { Triangle t(i, V0, V1, V2); double out; - if(intersectMollerTrumbore(r, t, &out)) + if(intersectMollerTrumbore(r, t, out)) { cv::Point3f tmp_pt = r.getP0() + out*r.getP1(); // P = O + t*D intersections_list.push_back(tmp_pt); @@ -305,7 +305,7 @@ namespace od { } // Möller–Trumbore intersection algorithm - bool PnPProblem::intersectMollerTrumbore(Ray & ray, Triangle & triangle, shared_ptr<double> out) + bool PnPProblem::intersectMollerTrumbore(Ray & ray, Triangle & triangle, double & out) { const double EPSILON = 0.000001; @@ -360,7 +360,7 @@ namespace od { t = DOT(e2, Q) * inv_det; if(t > EPSILON) { //ray intersection - *out = t; + out = t; return true; } diff --git a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md index 6a349810..25056e38 100644 --- a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md +++ b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md @@ -164,4 +164,17 @@ Missing parts and next steps: - I have to check that compilation works fine also with svmlight which was disabled till now - Use shared pointer where normal pointer are used. We have to decide if we want to use boost or stds shared pointers since pcl is using the first version. The second would be better so we would have everything which is possible using std and then when pcl moves toward std shared pointer we move also the rest of the code to that. -- Remove where possible the argc,argv parsing methods using standard ones. \ No newline at end of file +- Remove where possible the argc,argv parsing methods using standard ones. + +##Shared Pointers 10/06/15## + +The following step took quite a bit in order to have everything work well. Until now the library was using normal C++ pointer created with new and then deleted when not used anymore. This can lead to bad habits of forgetting to delete pointers, causing memory leaks. To avoid this its always good to use safe pointers which get deallocated as soon as they are not referenced my anyone anymore. Safe pointers are called **shared_ptr** in C++ and were intially implemented in the *Boost* library; then, after C++11, they where also inserted in the standard in the *std::* namespace. Substituting all pointers n the library was quite a complex task for different reasons: + +- Opendetection uses quite a lot the *Pcl* library. This library is still using the **boost::shared_ptr** implementation instead of the **std::shared_ptr** implementation. The two **shared_ptr** are not compatible and so a big issue arises. Should we use the first or second implementation? We should use the second solution since it relies on the standard, but we can't since *Pcl* still uses the first implementation. While *Pcl* moves to the *std::* solution we need to use the *boost* namespace, so in order to have the library compatible with both implementations, beeing able to switch from one to the other, I added a simple header file which switches between the two shared pointer types. This way as soon as *Pcl* moves to the *std::* we can also switch by just setting a variable in the cmake. The variable is called **WITH_BOOST_SHARED_PTR** ad is set to on by default so that we use *boost::shared_ptr* for now. + +- Substituting a pointer with a shared pointer requirest also to use another set of functions for the creation and the casting. to create a **shared_ptr** you either pass the pointer to the constructor or use the **make_shared** template passing to the function the constructor arguments and as template argument the pointer type. Also static and dynamic casting have specific template functions which are **dynamic_pointer_cast** and **static_pointer_casto**. + +- It is important to remove all delete which are around the code since **shared_ptr* automatically destroy the pointed objects in the destructor if not referenced anymore. + +While restructuring the code I also fixed again some coding style issues, but I still have to go through the code a few more times in order to finish fixing everything. The next step is as previously mentioned, to check the compilation with the svmlight option. + diff --git a/examples/apps/cadrecog2D/od_test_single_db_single_model.cpp b/examples/apps/cadrecog2D/od_test_single_db_single_model.cpp index 542392ee..1c2dbbf6 100644 --- a/examples/apps/cadrecog2D/od_test_single_db_single_model.cpp +++ b/examples/apps/cadrecog2D/od_test_single_db_single_model.cpp @@ -54,10 +54,9 @@ int main(int argc, char *argv[]) od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(imagespath); //GUI //cv::namedWindow("Overlay", cv::WINDOW_NORMAL); - od::ODSceneImage * scene; while(frameGenerator.isValid() && cv::waitKey(10) != 27) { - scene = frameGenerator.getNextFrame(); + boost::shared_ptr<od::ODSceneImage> scene = frameGenerator.getNextFrame(); //cv::imshow("Overlay", scene->getCVImage()); //Detect @@ -80,8 +79,6 @@ int main(int argc, char *argv[]) //else //cv::imshow("Overlay", scene->getCVImage()); - delete scene; - } return 0; diff --git a/examples/apps/global2D/od_multihog_app.cpp b/examples/apps/global2D/od_multihog_app.cpp index a51b3281..e1e19bb3 100644 --- a/examples/apps/global2D/od_multihog_app.cpp +++ b/examples/apps/global2D/od_multihog_app.cpp @@ -32,6 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/global2D/detection/ODHOGDetector.h" #include "od/common/utils/ODFrameGenerator.h" +#include "od/common/utils/ODUtils.h" #include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) @@ -39,25 +40,25 @@ int main(int argc, char *argv[]) std::string trained_data_dir(argv[1]), input_video(argv[2]), output_video = "output.avi"; if (argc > 3) output_video = argv[3]; - cv::Size sizesingle(640, 480); + cv::Size size_single(640, 480); //get 3 detectors of different types - std::vector<string> messages; + std::vector<std::string> messages; messages.push_back("Original"); - std::vector<g2d::ODHOGDetector> detectors; + std::vector<od::g2d::ODHOGDetector> detectors; od::g2d::ODHOGDetector detector1; // messages.push_back("OpenCV Default People"); detectors.push_back(detector1); od::g2d::ODHOGDetector detector2; - detector2->setSvmtype(g2d::ODHOGDetector::OD_DAIMLER_PEOPLE); + detector2.setSvmtype(od::g2d::ODHOGDetector::OD_DAIMLER_PEOPLE); messages.push_back("OpenCV Daimler People"); detectors.push_back(detector2); - od::g2d::ODHOGDetector detector3 (trained_data_dir); + od::g2d::ODHOGDetector detector(trained_data_dir); messages.push_back("Custom HOG from trained data"); - detectors.push_back(detector3); + detectors.push_back(detector); //init all detectors for(size_t i = 0; i < detectors.size(); ++i) @@ -66,20 +67,20 @@ int main(int argc, char *argv[]) //get scenes od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_DEVICE> frameGenerator(input_video); //od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(input_video); - cv::VideoWriter videoWriter(output_video, CV_FOURCC('M','J','P','G'), 25, sizesingle * 2, true); - + cv::VideoWriter videoWriter(output_video, CV_FOURCC('M','J','P','G'), 25, size_single * 2, true); //GUI cv::namedWindow("Overlay", cv::WINDOW_NORMAL); + cv::Mat multi_image; while(frameGenerator.isValid() && cv::waitKey(33) != 27) { - od::ODSceneImage * scene = frameGenerator.getNextFrame(); + boost::shared_ptr<od::ODSceneImage> scene = frameGenerator.getNextFrame(); std::vector<cv::Mat> images_to_show; images_to_show.push_back(scene->getCVImage()); //push the first image //detect 3 times - for (size_t i = 0; i < detectors.size(); i++) + for(size_t i = 0; i < detectors.size(); i++) { boost::shared_ptr<od::ODDetections2D> detections = detectors[i].detectOmni(scene); if(detections->size() > 0) @@ -88,11 +89,10 @@ int main(int argc, char *argv[]) images_to_show.push_back(scene->getCVImage()); } - cv::Mat multiimage = makeCanvasMultiImages(images_to_show, sizesingle, messages); - cv::imshow("Overlay", multiimage); - videoWriter << multiimage; + multi_image = od::makeCanvasMultiImages(images_to_show, size_single, messages); + cv::imshow("Overlay", multi_image); + videoWriter << multi_image; - delete scene; } return 0; diff --git a/examples/objectdetector/od_cascade_cam.cpp b/examples/objectdetector/od_cascade_cam.cpp index 87b2d303..6184e285 100644 --- a/examples/objectdetector/od_cascade_cam.cpp +++ b/examples/objectdetector/od_cascade_cam.cpp @@ -54,7 +54,7 @@ int main(int argc, char *argv[]) cv::namedWindow("Overlay", cv::WINDOW_NORMAL); while(frameGenerator.isValid() && cv::waitKey(10) != 27) { - od::ODSceneImage * scene = frameGenerator.getNextFrame(); + boost::shared_ptr<od::ODSceneImage> scene = frameGenerator.getNextFrame(); //Detect boost::shared_ptr<od::ODDetections2D> detections = detector.detectOmni(scene); @@ -64,7 +64,6 @@ int main(int argc, char *argv[]) else cv::imshow("Overlay", scene->getCVImage()); - delete scene; } return 0; diff --git a/examples/objectdetector/od_cascade_files.cpp b/examples/objectdetector/od_cascade_files.cpp index 18591a3c..2b0abec7 100644 --- a/examples/objectdetector/od_cascade_files.cpp +++ b/examples/objectdetector/od_cascade_files.cpp @@ -55,7 +55,7 @@ int main(int argc, char *argv[]) cv::namedWindow("Overlay", cv::WINDOW_NORMAL); while(frameGenerator.isValid() && cv::waitKey(2000) != 27) { - od::ODSceneImage * scene = frameGenerator.getNextFrame(); + boost::shared_ptr<od::ODSceneImage> scene = frameGenerator.getNextFrame(); //Detect boost::shared_ptr<od::ODDetections2D> detections = detector.detectOmni(scene); @@ -68,7 +68,6 @@ int main(int argc, char *argv[]) else cv::imshow("Overlay", scene->getCVImage()); - delete scene; } return 0; diff --git a/examples/objectdetector/od_example_framegenerator.cpp b/examples/objectdetector/od_example_framegenerator.cpp index ef99e696..5efd421d 100644 --- a/examples/objectdetector/od_example_framegenerator.cpp +++ b/examples/objectdetector/od_example_framegenerator.cpp @@ -40,19 +40,17 @@ int main(int argc, char *argv[]) //GUI and feedback pcl::visualization::PCLVisualizer vis ("kinect"); - od::ODScenePointCloud<pcl::PointXYZRGBA> * frame; od::ODFrameGenerator<od::ODScenePointCloud<pcl::PointXYZRGBA>, od::GENERATOR_TYPE_DEVICE> frameGenerator(""); while(frameGenerator.isValid()) { //get frame - frame = frameGenerator.getNextFrame(); + boost::shared_ptr<od::ODScenePointCloud<pcl::PointXYZRGBA>> frame = frameGenerator.getNextFrame(); vis.removePointCloud ("frame"); vis.addPointCloud<pcl::PointXYZRGBA> (frame->getPointCloud(), "frame"); vis.spinOnce (); //free frame - delete frame; } return 0; } diff --git a/examples/objectdetector/od_hog_train.cpp b/examples/objectdetector/od_hog_train.cpp index b65c5154..a0c82378 100644 --- a/examples/objectdetector/od_hog_train.cpp +++ b/examples/objectdetector/od_hog_train.cpp @@ -44,7 +44,7 @@ int main(int argc, char *argv[]) return -1; } - std::string pos_samples(argv[1]) + std::string pos_samples(argv[1]); std::string neg_samples(argv[2]); std::string trained_data_dir(argv[3]); std::string test_images(argv[4]); @@ -67,17 +67,16 @@ int main(int argc, char *argv[]) cv::namedWindow("Overlay", cv::WINDOW_NORMAL); while(frameGenerator.isValid() && cv::waitKey(10) != 27) { - od::ODSceneImage * scene = frameGenerator.getNextFrame(); + boost::shared_ptr<od::ODSceneImage> scene = frameGenerator.getNextFrame(); //Detect - boost::shared_ptr<ODDetections2D> detections = detector.detectOmni(scene); + boost::shared_ptr<od::ODDetections2D> detections = detector.detectOmni(scene); if(detections->size() > 0) cv::imshow("Overlay", detections->renderMetainfo(*scene).getCVImage()); else cv::imshow("Overlay", scene->getCVImage()); - delete scene; } return 0; diff --git a/examples/objectdetector/od_image_cadrecog_camera.cpp b/examples/objectdetector/od_image_cadrecog_camera.cpp index f2429c30..fc1cc442 100644 --- a/examples/objectdetector/od_image_cadrecog_camera.cpp +++ b/examples/objectdetector/od_image_cadrecog_camera.cpp @@ -64,7 +64,7 @@ int main(int argc, char *argv[]) cv::namedWindow("Overlay", cv::WINDOW_NORMAL); while(frameGenerator.isValid() && cv::waitKey(10) != 27) { - od::ODSceneImage * scene = frameGenerator.getNextFrame(); + boost::shared_ptr<od::ODSceneImage> scene = frameGenerator.getNextFrame(); //Detect boost::shared_ptr<ODDetections3D> detections = detector.detectOmni(scene); @@ -74,7 +74,6 @@ int main(int argc, char *argv[]) else cv::imshow("Overlay", scene->getCVImage()); - delete scene; } return 0; diff --git a/examples/objectdetector/od_image_cadrecog_files.cpp b/examples/objectdetector/od_image_cadrecog_files.cpp index b94017b7..65ce2285 100644 --- a/examples/objectdetector/od_image_cadrecog_files.cpp +++ b/examples/objectdetector/od_image_cadrecog_files.cpp @@ -59,7 +59,7 @@ int main(int argc, char *argv[]) cv::namedWindow("Overlay", cv::WINDOW_NORMAL); while(frameGenerator.isValid() && cv::waitKey(10) != 27) { - od::ODSceneImage * scene = frameGenerator.getNextFrame(); + boost::shared_ptr<od::ODSceneImage> scene = frameGenerator.getNextFrame(); cv::imshow("Overlay", scene->getCVImage()); //Detect @@ -70,8 +70,6 @@ int main(int argc, char *argv[]) else cv::imshow("Overlay", scene->getCVImage()); - delete scene; - } return 0; diff --git a/examples/objectdetector/od_image_customhog.cpp b/examples/objectdetector/od_image_customhog.cpp index 3aabf8d7..e7f0257a 100644 --- a/examples/objectdetector/od_image_customhog.cpp +++ b/examples/objectdetector/od_image_customhog.cpp @@ -35,7 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/common/utils/ODFrameGenerator.h" #include <boost/shared_ptr.hpp> -int main(int argc, char *argv[]) +int main(int argc, char * argv[]) { if(argc < 4){ @@ -45,8 +45,8 @@ int main(int argc, char *argv[]) std::string trained_data_dir(argv[1]); //detector - g2d::ODHOGDetector detector(trained_data_dir); - detector.setSvmtype(g2d::ODHOGDetector::OD_FILE); + od::g2d::ODHOGDetector detector(trained_data_dir); + detector.setSvmtype(od::g2d::ODHOGDetector::OD_FILE); detector.init(); //get scenes @@ -55,17 +55,15 @@ int main(int argc, char *argv[]) cv::namedWindow("Overlay", cv::WINDOW_NORMAL); while(frameGenerator.isValid() && cv::waitKey(10) != 27) { - od::ODSceneImage * scene = frameGenerator.getNextFrame(); + boost::shared_ptr<od::ODSceneImage> scene = frameGenerator.getNextFrame(); //Detect - boost::shared_ptr<ODDetections2D> detections = detector.detectOmni(scene); + boost::shared_ptr<od::ODDetections2D> detections = detector.detectOmni(scene); if(detections->size() > 0) cv::imshow("Overlay", detections->renderMetainfo(*scene).getCVImage()); else cv::imshow("Overlay", scene->getCVImage()); - - delete scene; } return 0; diff --git a/examples/objectdetector/od_image_facerecog.cpp b/examples/objectdetector/od_image_facerecog.cpp index a3aabb5e..08ba4af1 100644 --- a/examples/objectdetector/od_image_facerecog.cpp +++ b/examples/objectdetector/od_image_facerecog.cpp @@ -60,14 +60,13 @@ int main(int argc, char *argv[]) cv::namedWindow("Overlay", cv::WINDOW_NORMAL); while(frameGenerator.isValid() && cv::waitKey(10) != 27) { - od::ODSceneImage * scene = frameGenerator.getNextFrame(); + boost::shared_ptr<od::ODSceneImage> scene = frameGenerator.getNextFrame(); //Detect - boost::shared_ptr<od::ODDetections> detections = objdetector.detect(scene); + boost::shared_ptr<od::ODDetections> detections = objdetector.detect(scene); (*detections)[0]->printSelf(); cv::imshow("Overlay", scene->getCVImage()); - delete scene; } return 0; diff --git a/examples/objectdetector/od_image_hog_files.cpp b/examples/objectdetector/od_image_hog_files.cpp index 604bcbce..4feac2f4 100644 --- a/examples/objectdetector/od_image_hog_files.cpp +++ b/examples/objectdetector/od_image_hog_files.cpp @@ -57,17 +57,15 @@ int main(int argc, char *argv[]) cv::namedWindow("Overlay", cv::WINDOW_NORMAL); while(frameGenerator.isValid() && cv::waitKey(10) != 27) { - od::ODSceneImage * scene = frameGenerator.getNextFrame(); + boost::shared_ptr<od::ODSceneImage> scene = frameGenerator.getNextFrame(); //Detect - boost::shared_ptr<ODDetections2D> detections = detector.detectOmni(scene); + boost::shared_ptr<od::ODDetections2D> detections = detector.detectOmni(scene); if(detections->size() > 0) cv::imshow("Overlay", detections->renderMetainfo(*scene).getCVImage()); else cv::imshow("Overlay", scene->getCVImage()); - - delete scene; } return 0; diff --git a/examples/objectdetector/od_multialgo_files.cpp b/examples/objectdetector/od_multialgo_files.cpp index e6da9564..4d5a2a74 100644 --- a/examples/objectdetector/od_multialgo_files.cpp +++ b/examples/objectdetector/od_multialgo_files.cpp @@ -26,7 +26,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <opencv2/highgui.hpp> -#include "detectors/misc/detection/ODDetectorMultiAlgo.hpp" +#include "od/detectors/misc/detection/ODDetectorMultiAlgo.hpp" #include "od/common/utils/ODFrameGenerator.h" #include <boost/shared_ptr.hpp> @@ -42,7 +42,7 @@ int main(int argc, char *argv[]) std::string query_images(argv[2]); //detector - od::ODDetectorMultiAlgo2D detector(trained_data_dir); + od::ODDetectorMultiAlgo2D<pcl::PointXYZRGBA> detector(trained_data_dir); detector.init(); //get scenes @@ -51,18 +51,16 @@ int main(int argc, char *argv[]) cv::namedWindow("Overlay", cv::WINDOW_NORMAL); while(frameGenerator.isValid() && cv::waitKey(10) != 27) { - od::ODSceneImage * scene = frameGenerator.getNextFrame(); + boost::shared_ptr<od::ODSceneImage> scene = frameGenerator.getNextFrame(); //Detect - boost::shared_ptr<ODDetections2D> detections = detector.detectOmni(scene); + boost::shared_ptr<od::ODDetections2D> detections = detector.detectOmni(scene); if(detections->size() > 0) cv::imshow("Overlay", detections->renderMetainfo(*scene).getCVImage()); else cv::imshow("Overlay", scene->getCVImage()); - delete scene; - } return 0; diff --git a/examples/objectdetector/od_multialgo_pc.cpp b/examples/objectdetector/od_multialgo_pc.cpp index b12fb4c4..adad16aa 100644 --- a/examples/objectdetector/od_multialgo_pc.cpp +++ b/examples/objectdetector/od_multialgo_pc.cpp @@ -33,7 +33,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/misc/detection/ODDetectorMultiAlgo.hpp" #include "od/detectors/global3D/ODPointCloudGlobalMatching.h" -#include "common/utils/ODFrameGenerator.h" +#include "od/common/utils/ODFrameGenerator.h" #include <string> #include <boost/shared_ptr.hpp> @@ -49,12 +49,11 @@ int main(int argc, char *argv[]) std::string trained_data_dir(argv[2]); //detector - od::ODDetectorMultiAlgo detector(trained_data_dir); + od::ODDetectorMultiAlgo<pcl::PointXYZRGBA> detector(trained_data_dir); detector.setTrainingInputLocation(training_input_dir); detector.init(); //GUI and feedback - od::ODScenePointCloud<pcl::PointXYZRGBA> * frame; pcl::visualization::PCLVisualizer vis ("kinect"); size_t previous_cluster_size = 0; @@ -65,7 +64,7 @@ int main(int argc, char *argv[]) while(frameGenerator.isValid()) { - frame = frameGenerator.getNextFrame(); + boost::shared_ptr<od::ODScenePointCloud<pcl::PointXYZRGBA>> frame = frameGenerator.getNextFrame(); //remove previous point clouds and text and add new ones in the visualizer vis.removePointCloud ("frame"); @@ -84,7 +83,7 @@ int main(int argc, char *argv[]) //Detect - boost::shared_ptr<od::ODDetections3D> detections = detector->detectOmni(frame); + boost::shared_ptr<od::ODDetections3D> detections = detector.detectOmni(frame); //add all the detections in the visualizer with its id as text for(size_t i = 0; i < detections->size (); ++i) diff --git a/examples/objectdetector/od_pc_global.cpp b/examples/objectdetector/od_pc_global.cpp index 135acf00..b6a0209f 100644 --- a/examples/objectdetector/od_pc_global.cpp +++ b/examples/objectdetector/od_pc_global.cpp @@ -60,9 +60,10 @@ int main(int argc, char *argv[]) detector.init(); //Get a scene - od::ODScenePointCloud<pcl::PointXYZRGBA> scene(pointcloud_file); + boost::shared_ptr<od::ODScenePointCloud<pcl::PointXYZRGBA> > scene = boost::make_shared<od::ODScenePointCloud<pcl::PointXYZRGBA>>(pointcloud_file); - boost::shared_ptr<od::ODDetections3D> detections = detector.detectOmni(&scene); + + boost::shared_ptr<od::ODDetections3D> detections = detector.detectOmni(scene); //feedback for(size_t i = 0; i < detections->size(); ++i) diff --git a/examples/objectdetector/od_pc_global_files.cpp b/examples/objectdetector/od_pc_global_files.cpp index 84b5dacd..64e8445d 100644 --- a/examples/objectdetector/od_pc_global_files.cpp +++ b/examples/objectdetector/od_pc_global_files.cpp @@ -61,13 +61,12 @@ int main(int argc, char *argv[]) detector.init(); //Get a scene - od::ODScenePointCloud<pcl::PointXYZRGBA> * frame; od::ODFrameGenerator<od::ODScenePointCloud<pcl::PointXYZRGBA>, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(pointcloud_file); while(frameGenerator.isValid()) { //get frame - frame = frameGenerator.getNextFrame(); + boost::shared_ptr<od::ODScenePointCloud<pcl::PointXYZRGBA>> frame = frameGenerator.getNextFrame(); boost::shared_ptr<od::ODDetections3D> detections = detector.detectOmni(frame); @@ -81,8 +80,6 @@ int main(int argc, char *argv[]) //vis.spinOnce (5); - //free frame - delete frame; } return 0; diff --git a/examples/objectdetector/od_pc_global_real_time.cpp b/examples/objectdetector/od_pc_global_real_time.cpp index 9c5883e5..9b0bad45 100644 --- a/examples/objectdetector/od_pc_global_real_time.cpp +++ b/examples/objectdetector/od_pc_global_real_time.cpp @@ -61,7 +61,6 @@ int main(int argc, char *argv[]) detector.init(); //GUI and feedback - od::ODScenePointCloud<pcl::PointXYZRGBA> * frame; pcl::visualization::PCLVisualizer vis ("kinect"); od::ODFrameGenerator<od::ODScenePointCloud<pcl::PointXYZRGBA>, od::GENERATOR_TYPE_DEVICE> frameGenerator; @@ -71,7 +70,7 @@ int main(int argc, char *argv[]) while(frameGenerator.isValid()) { - frame = frameGenerator.getNextFrame(); + boost::shared_ptr<od::ODScenePointCloud<pcl::PointXYZRGBA>> frame = frameGenerator.getNextFrame(); //remove previous point clouds and text and add new ones in the visualizer vis.removeAllPointClouds(); @@ -91,7 +90,7 @@ int main(int argc, char *argv[]) pos.y = detections->at(i)->getLocation()[1]; pos.z = detections->at(i)->getLocation()[2]; ss << "cluster_" << i << "_txt"; - vis.addText3D (detections->at(i)->getId(), pos, 0.015f, 1, 0, 1, ss.str(), 0); + vis.addText3D(detections->at(i)->getId(), pos, 0.015f, 1, 0, 1, ss.str(), 0); ss.str(std::string()); } From ecf339a9d769c6690973ca050b17f970fb5d49e8 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Mon, 13 Jun 2016 14:48:05 +0200 Subject: [PATCH 21/79] sets svmlight as gitsubmodule --- .gitmodules | 3 + 3rdparty/CMakeLists.txt | 6 +- 3rdparty/svmlightlib | 1 + CMakeLists.txt | 1 - common/CMakeLists.txt | 9 ++- common/include/od/common/bindings/svmlight.h | 2 + .../misc/detection/ODDetectorMultiAlgo.hpp | 3 + .../global2D/detection/ODHOGDetector.h | 2 +- detectors/src/global2D/CMakeLists.txt | 35 +++--------- examples/apps/CMakeLists.txt | 6 +- examples/objectdetector/CMakeLists.txt | 57 +++++++++---------- .../objectdetector/od_multialgo_files.cpp | 2 +- 12 files changed, 58 insertions(+), 69 deletions(-) create mode 160000 3rdparty/svmlightlib diff --git a/.gitmodules b/.gitmodules index 18fa539a..68660bea 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "3rdparty/SiftGPU"] path = 3rdparty/SiftGPU url = https://github.com/pitzer/SiftGPU.git +[submodule "3rdparty/svmlightlib"] + path = 3rdparty/svmlightlib + url = https://github.com/giacomodabisias/svmlightlib.git diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 678f5bed..4bfcfad5 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -1,9 +1,9 @@ #add mandatory 3rdparty libraries add_subdirectory(pugixml_build) -#if(WITH_SVMLIGHT) -# add_subdirectory(svmlight) -#endif(WITH_SVMLIGHT) +if(WITH_SVMLIGHT) + add_subdirectory(svmlightlib) +endif(WITH_SVMLIGHT) if(WITH_GPU) add_subdirectory(SiftGPU) diff --git a/3rdparty/svmlightlib b/3rdparty/svmlightlib new file mode 160000 index 00000000..b1ff32b8 --- /dev/null +++ b/3rdparty/svmlightlib @@ -0,0 +1 @@ +Subproject commit b1ff32b8cd68570c33ceee63e464a8773adb6413 diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d753e8c..23907fe2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,7 +25,6 @@ include(${OD_CMAKE_DIR}/od_macros.cmake) # Optional parameters option(WITH_DOCUMENTATION "Build the OD documentation" ON) option(WITH_GPU "Build GPU components of the project" ON) -option(WITH_SVMLIGHT "Build SVM Light" OFF) option(WITH_EXAMPLES "Build examples" OFF) option(WITH_BOOST_SHARED_PTR "use boost::shared_ptr instead of std::shared_ptr" ON) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 24dac071..21fa9cb0 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -25,13 +25,16 @@ if(build) "src/bindings/svmlight.cpp" ) + + set(SUBSYS_DEPS ${SUBSYS_DEPS} svmlight) + + include_directories(${COMMON_INCLUDE_DIR}) OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${SOURCES}) install(DIRECTORY ${COMMON_INCLUDE_DIR}/od DESTINATION ${OD_INSTALL_INCLUDE_DIR} COMPONENT ${LIB_NAME}) + target_link_libraries(${LIB_NAME} ${SUBSYS_DEPS}) + - if(SUBSYS_DEPS) - target_link_libraries("${LIB_NAME}" ${SUBSYS_DEPS}) - endif(SUBSYS_DEPS) endif(build) \ No newline at end of file diff --git a/common/include/od/common/bindings/svmlight.h b/common/include/od/common/bindings/svmlight.h index ee325651..0c31c20f 100644 --- a/common/include/od/common/bindings/svmlight.h +++ b/common/include/od/common/bindings/svmlight.h @@ -56,8 +56,10 @@ Unless required by applicable law or agreed to in writing, software distributed // namespace required for avoiding collisions of declarations (e.g. LINEAR being declared in flann, svmlight and libsvm) namespace svmlight { + extern "C" { #include "svm_common.h" #include "svm_learn.h" + } } using namespace svmlight; diff --git a/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp b/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp index e86a292e..790f01ab 100644 --- a/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp +++ b/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp @@ -64,6 +64,7 @@ namespace od { public: + ODDetectorMultiAlgo(const std::string & training_data_location_) : ODDetector(training_data_location_){} shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage > scene) ; @@ -75,8 +76,10 @@ namespace od void init(); private: + std::vector<shared_ptr<ODDetector2D> > detectors_2d_; std::vector<shared_ptr<ODDetector3D<PointT> > > detectors_3d_; + }; diff --git a/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h b/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h index 4d221b87..ae2219e9 100644 --- a/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h +++ b/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h @@ -64,7 +64,7 @@ namespace od ODHOGDetector(const std::string & trained_data_location_ = "", const cv::Size & win_size = cv::Size(64,128), - const cv::Size & block_size = cv::Size(16,16), const cv::Size & block_stride = cv::Size(8,8), + const cv::Size & block_size = cv::Size(16,16), const cv::Size & block_stride = cv::Size(8,8), const cv::Size & cell_size = cv::Size(8,8), float hit_threshold = 0.0); void init(); diff --git a/detectors/src/global2D/CMakeLists.txt b/detectors/src/global2D/CMakeLists.txt index cdb12115..f046ceb6 100644 --- a/detectors/src/global2D/CMakeLists.txt +++ b/detectors/src/global2D/CMakeLists.txt @@ -11,38 +11,21 @@ set(build TRUE) if(build) - if(WITH_SVMLIGHT) - set(SOURCES - "ODFaceRecognizer.cpp" - "detection/ODHOGDetector.cpp" - "detection/ODCascadeDetector.cpp" - "training/ODHOGTrainer.cpp" - ) - - else() - set(SOURCES - "ODFaceRecognizer.cpp" - "detection/ODCascadeDetector.cpp" - ) - - endif() - + set(SOURCES + "ODFaceRecognizer.cpp" + "detection/ODCascadeDetector.cpp" + "detection/ODHOGDetector.cpp" + "training/ODHOGTrainer.cpp" + ) + include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${SOURCES}) - if(WITH_SVMLIGHT) - if(SUBSYS_DEPS) - target_link_libraries("${LIB_NAME}" ${SUBSYS_DEPS} svm) - endif(SUBSYS_DEPS) - else() - if(SUBSYS_DEPS) - target_link_libraries("${LIB_NAME}" ${SUBSYS_DEPS} ) - endif(SUBSYS_DEPS) - endif() + set(SUBSYS_DEPS ${SUBSYS_DEPS} svmlight) - + target_link_libraries(${LIB_NAME} ${SUBSYS_DEPS}) #PCL_MAKE_PKGCONFIG("${LIB_NAME}" "${SUBSYS_NAME}" "${SUBSYS_DESC}" "${SUBSYS_DEPS}" "" "" "" "") diff --git a/examples/apps/CMakeLists.txt b/examples/apps/CMakeLists.txt index f4c19d50..d225f8ec 100644 --- a/examples/apps/CMakeLists.txt +++ b/examples/apps/CMakeLists.txt @@ -9,7 +9,5 @@ OD_ADD_EXAMPLE(odcadrecog_test_single_model FILES cadrecog2D/od_test_single_db_s set(SOURCE_FILES version/opendetection.cpp) add_executable(opendetection ${SOURCE_FILES}) -if(WITH_SVMLIGHT) - OD_ADD_EXAMPLE(od_multihog_app FILES global2D/od_multihog_app.cpp - LINK_WITH od_common od_global_image_detector) -endif() \ No newline at end of file +OD_ADD_EXAMPLE(od_multihog_app FILES global2D/od_multihog_app.cpp +LINK_WITH od_common od_global_image_detector) diff --git a/examples/objectdetector/CMakeLists.txt b/examples/objectdetector/CMakeLists.txt index b65b5d06..b9f8539d 100644 --- a/examples/objectdetector/CMakeLists.txt +++ b/examples/objectdetector/CMakeLists.txt @@ -17,37 +17,34 @@ if(image_recognition_example) LINK_WITH od_common od_local_image_detector) endif() -if(WITH_SVMLIGHT) - option(hog_train_example "Build the hog train example" ON) - if(hog_train_example) - OD_ADD_EXAMPLE(od_hog_train FILES od_hog_train.cpp - LINK_WITH od_common od_global_image_detector) - endif() - - option(image_hog_files_example "Build the hog image example" ON) - if(image_hog_files_example) - OD_ADD_EXAMPLE(od_image_hog_files FILES od_image_hog_files.cpp - LINK_WITH od_common od_global_image_detector) - endif() - - option(image_customhog_example "Build the custom hog example" ON) - if(image_customhog_example) - OD_ADD_EXAMPLE(od_image_customhog FILES od_image_customhog.cpp - LINK_WITH od_common od_global_image_detector) - endif() - - option(multialgo_files_example "Build the multialgo example" ON) - if(multialgo_files_example) - OD_ADD_EXAMPLE(od_multialgo_files FILES od_multialgo_files.cpp - LINK_WITH od_miscellaneous_detector) - endif() - - option(multialgo_pc_example "Build the multialgo_pc example" ON) - if(multialgo_pc_example) - OD_ADD_EXAMPLE(od_multialgo_pc FILES od_multialgo_pc.cpp - LINK_WITH od_miscellaneous_detector) - endif() +option(hog_train_example "Build the hog train example" ON) +if(hog_train_example) + OD_ADD_EXAMPLE(od_hog_train FILES od_hog_train.cpp + LINK_WITH od_common od_global_image_detector) +endif() + +option(image_hog_files_example "Build the hog image example" ON) +if(image_hog_files_example) + OD_ADD_EXAMPLE(od_image_hog_files FILES od_image_hog_files.cpp + LINK_WITH od_common od_global_image_detector) +endif() + +option(image_customhog_example "Build the custom hog example" ON) +if(image_customhog_example) + OD_ADD_EXAMPLE(od_image_customhog FILES od_image_customhog.cpp + LINK_WITH od_common od_global_image_detector) +endif() + +option(multialgo_files_example "Build the multialgo_files example" ON) +if(multialgo_files_example) + OD_ADD_EXAMPLE(od_multialgo_files FILES od_multialgo_files.cpp + LINK_WITH od_common od_global_image_detector) +endif() +option(multialgo_pc_example "Build the multialgo_pc example" ON) +if(multialgo_pc_example) + OD_ADD_EXAMPLE(od_multialgo_pc FILES od_multialgo_pc.cpp + LINK_WITH od_common od_global_image_detector) endif() option(cascade_cam_example "Build the cascade camera example" ON) diff --git a/examples/objectdetector/od_multialgo_files.cpp b/examples/objectdetector/od_multialgo_files.cpp index 4d5a2a74..d46ddfc8 100644 --- a/examples/objectdetector/od_multialgo_files.cpp +++ b/examples/objectdetector/od_multialgo_files.cpp @@ -25,9 +25,9 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include <opencv2/highgui.hpp> #include "od/detectors/misc/detection/ODDetectorMultiAlgo.hpp" #include "od/common/utils/ODFrameGenerator.h" +#include <opencv2/highgui.hpp> #include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) From 21c28a5ca5629917961c66b0d394b2a7b33f957a Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Tue, 14 Jun 2016 14:59:38 +0200 Subject: [PATCH 22/79] fixes macros, svlight, doxygen warning --- 3rdparty/CMakeLists.txt | 4 +- cmake/od_macros.cmake | 93 +------------------ common/CMakeLists.txt | 14 +-- common/include/od/common/bindings/svmlight.h | 6 +- .../global2D/detection/ODHOGDetector.h | 5 +- detectors/src/global2D/CMakeLists.txt | 9 +- detectors/src/global3D/CMakeLists.txt | 12 +-- detectors/src/local2D/CMakeLists.txt | 8 +- doc/doxygen/doxyfile.in | 3 +- examples/apps/CMakeLists.txt | 14 ++- examples/objectdetector/CMakeLists.txt | 71 ++++++++------ 11 files changed, 80 insertions(+), 159 deletions(-) diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 4bfcfad5..094ec4d6 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -1,9 +1,7 @@ #add mandatory 3rdparty libraries add_subdirectory(pugixml_build) -if(WITH_SVMLIGHT) - add_subdirectory(svmlightlib) -endif(WITH_SVMLIGHT) +add_subdirectory(svmlightlib) if(WITH_GPU) add_subdirectory(SiftGPU) diff --git a/cmake/od_macros.cmake b/cmake/od_macros.cmake index c5530df4..786677c1 100644 --- a/cmake/od_macros.cmake +++ b/cmake/od_macros.cmake @@ -1,76 +1,18 @@ # global installation type set(OD_LIB_TYPE SHARED) -macro(OD_ADD_LIBRARY1 _name _srcs _incs _impl_incs) - - set(lib_name "od_${_name}") - - message(input to od_add_library: ${OD_LIB_TYPE} ${_srcs} ${_incs} ${_impl_incs}) - add_library(${lib_name} ${OD_LIB_TYPE} ${_srcs} ${_incs} ${_impl_incs}) - - # allways link libs: - target_link_libraries(${lib_name} ${Boost_LIBRARIES}) - - # target properties - set_target_properties(${lib_name} PROPERTIES - VERSION ${OD_VERSION} - SOVERSION ${OD_MAJOR_VERSION}.${OD_MINOR_VERSION} - ) - - # Install library - install(TARGETS ${lib_name} - RUNTIME DESTINATION ${OD_INSTALL_RUNTIME_DIR} COMPONENT ${lib_name} - LIBRARY DESTINATION ${OD_INSTALL_LIBRARY_DIR} COMPONENT ${lib_name} - ARCHIVE DESTINATION ${OD_INSTALL_ARCHIVE_DIR} COMPONENT ${lib_name}) - - #install includes - install(FILES ${_incs} ${_impl_incs} - DESTINATION ${OD_INSTALL_INCLUDE_DIR}/${_name} - COMPONENT ${lib_name}) - -endmacro(OD_ADD_LIBRARY1) - -macro(OD_ADD_LIBRARY_ALL _name ) +macro(OD_ADD_LIBRARY _name ) set(lib_name "od_${_name}") set(options) set(oneValueArgs) - set(multiValueArgs SRCS) - cmake_parse_arguments(OD_ADD_LIBRARY_ALL "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - add_library(${lib_name} ${OD_LIB_TYPE} ${OD_ADD_LIBRARY_ALL_SRCS}) - - # allways link libs: - target_link_libraries(${lib_name} ${Boost_LIBRARIES}) - - # target properties - set_target_properties(${lib_name} PROPERTIES - VERSION ${OD_VERSION} - SOVERSION ${OD_MAJOR_VERSION}.${OD_MINOR_VERSION} - ) - - # Install library - install(TARGETS ${lib_name} - RUNTIME DESTINATION ${OD_INSTALL_RUNTIME_DIR} COMPONENT ${lib_name} - LIBRARY DESTINATION ${OD_INSTALL_LIBRARY_DIR} COMPONENT ${lib_name} - ARCHIVE DESTINATION ${OD_INSTALL_ARCHIVE_DIR} COMPONENT ${lib_name}) - + set(multiValueArgs SRCS LINK_WITH) + cmake_parse_arguments(OD_ADD_LIBRARY "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) -endmacro(OD_ADD_LIBRARY_ALL) + add_library(${lib_name} ${OD_LIB_TYPE} ${OD_ADD_LIBRARY_SRCS}) -macro(OD_ADD_LIBRARY _name) - - set(lib_name "od_${_name}") - - include_directories("${OD_SOURCE_DIR}") - - #message(input to od_add_library: ${OD_LIB_TYPE} ${ARGN}) - add_library(${lib_name} ${OD_LIB_TYPE} ${ARGN}) - - - # allways link libs: - target_link_libraries(${lib_name} ${Boost_LIBRARIES}) + target_link_libraries(${lib_name} ${OD_ADD_LIBRARY_LINK_WITH}) # target properties set_target_properties(${lib_name} PROPERTIES @@ -86,7 +28,6 @@ macro(OD_ADD_LIBRARY _name) endmacro(OD_ADD_LIBRARY) - macro(OD_ADD_EXAMPLE _name) set(options) set(oneValueArgs) @@ -97,30 +38,6 @@ macro(OD_ADD_EXAMPLE _name) target_link_libraries(${_name} ${OD_ADD_EXAMPLE_LINK_WITH}) endmacro(OD_ADD_EXAMPLE) - -macro(OD_ADD_INCLUDES _name) - #install includes - install(FILES ${ARGN} - DESTINATION ${OD_INSTALL_INCLUDE_DIR}/${_name} - COMPONENT ${lib_name}) - -endmacro(OD_ADD_INCLUDES) - -MACRO(HEADER_DIRECTORIES return_list) - FILE(GLOB_RECURSE new_list *.h) - SET(dir_list "") - FOREACH(file_path ${new_list}) - GET_FILENAME_COMPONENT(dir_path ${file_path} PATH) - SET(dir_list ${dir_list} ${dir_path}) - ENDFOREACH() - LIST(REMOVE_DUPLICATES dir_list) - SET(${return_list} ${dir_list}) -ENDMACRO() - -MACRO(CMAKE_SUBDIR_LIST result) - file (GLOB result */CMakeLists.txt) -ENDMACRO() - MACRO(SUBDIR_LIST retval curdir return_relative) file(GLOB sub-dir RELATIVE ${curdir} *) set(list_of_dirs "") diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 21fa9cb0..3c80ab92 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -2,20 +2,20 @@ set(SUBSYS_NAME common) set(LIB_NAME od_${SUBSYS_NAME}) set(SUBSYS_DESC "The core module of OpenDetection having the pipeline logic") set(SUBSYS_DEPS ${OpenCV_LIBS} ${PCL_LIBRARIES}) -set(COMMON_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) +set(COMMON_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) set(COMMON_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include PARENT_SCOPE) if(WITH_GPU) set(SUBSYS_DEPS ${SUBSYS_DEPS} siftgpu) endif() +set(SUBSYS_DEPS ${SUBSYS_DEPS} svmlight) + set(build TRUE) if(build) - set(IMPL_INCLUDES "") - set(SOURCES "src/pipeline/ODObjectDetector.cpp" "src/pipeline/ODScene.cpp" @@ -25,15 +25,11 @@ if(build) "src/bindings/svmlight.cpp" ) - - set(SUBSYS_DEPS ${SUBSYS_DEPS} svmlight) - - include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) - OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${SOURCES}) + OD_ADD_LIBRARY(${SUBSYS_NAME} SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) install(DIRECTORY ${COMMON_INCLUDE_DIR}/od DESTINATION ${OD_INSTALL_INCLUDE_DIR} COMPONENT ${LIB_NAME}) - target_link_libraries(${LIB_NAME} ${SUBSYS_DEPS}) diff --git a/common/include/od/common/bindings/svmlight.h b/common/include/od/common/bindings/svmlight.h index 0c31c20f..b0cf6ba4 100644 --- a/common/include/od/common/bindings/svmlight.h +++ b/common/include/od/common/bindings/svmlight.h @@ -52,15 +52,15 @@ Unless required by applicable law or agreed to in writing, software distributed #include <stdio.h> #include <vector> #include <string> -// svmlight related -// namespace required for avoiding collisions of declarations (e.g. LINEAR being declared in flann, svmlight and libsvm) - + +#ifndef DOXYGEN_SHOULD_SKIP_THIS namespace svmlight { extern "C" { #include "svm_common.h" #include "svm_learn.h" } } +#endif using namespace svmlight; diff --git a/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h b/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h index ae2219e9..c8c166c1 100644 --- a/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h +++ b/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h @@ -60,13 +60,12 @@ namespace od { public: - OD_DEFINE_ENUM_WITH_STRING_CONVERSIONS(SVMType, (OD_CUSTOM)(OD_DEFAULT_PEOPLE)(OD_DAIMLER_PEOPLE)(OD_FILE)) - - ODHOGDetector(const std::string & trained_data_location_ = "", const cv::Size & win_size = cv::Size(64,128), const cv::Size & block_size = cv::Size(16,16), const cv::Size & block_stride = cv::Size(8,8), const cv::Size & cell_size = cv::Size(8,8), float hit_threshold = 0.0); + OD_DEFINE_ENUM_WITH_STRING_CONVERSIONS(SVMType, (OD_CUSTOM)(OD_DEFAULT_PEOPLE)(OD_DAIMLER_PEOPLE)(OD_FILE)) + void init(); void load(const std::string & file_name); diff --git a/detectors/src/global2D/CMakeLists.txt b/detectors/src/global2D/CMakeLists.txt index f046ceb6..b5c151b7 100644 --- a/detectors/src/global2D/CMakeLists.txt +++ b/detectors/src/global2D/CMakeLists.txt @@ -2,7 +2,7 @@ set(SUBSYS_NAME global_image_detector) set(LIB_NAME od_${SUBSYS_NAME}) set(SUBSYS_DESC "global feature based detection in 2D images") -set(SUBSYS_DEPS od_common ${OpenCV_LIBS}) +set(SUBSYS_DEPS od_common ${OpenCV_LIBS} svmlight) set(build TRUE) #PCL_SUBSYS_OPTION(build "${SUBSYS_NAME}" "${SUBSYS_DESC}" ON) @@ -20,12 +20,9 @@ if(build) include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) - OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${SOURCES}) - - set(SUBSYS_DEPS ${SUBSYS_DEPS} svmlight) - - target_link_libraries(${LIB_NAME} ${SUBSYS_DEPS}) + OD_ADD_LIBRARY("${SUBSYS_NAME}" SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) #PCL_MAKE_PKGCONFIG("${LIB_NAME}" "${SUBSYS_NAME}" "${SUBSYS_DESC}" "${SUBSYS_DEPS}" "" "" "" "") diff --git a/detectors/src/global3D/CMakeLists.txt b/detectors/src/global3D/CMakeLists.txt index afa86fb1..e0fb2951 100644 --- a/detectors/src/global3D/CMakeLists.txt +++ b/detectors/src/global3D/CMakeLists.txt @@ -8,19 +8,15 @@ set(build TRUE) if(build) set(SOURCES - "ODPointCloudGlobalMatching.cpp" - "training/ODCADDetectTrainer3DGlobal.cpp" - ) + "ODPointCloudGlobalMatching.cpp" + "training/ODCADDetectTrainer3DGlobal.cpp" + ) include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${DETECTORS_IMPL_DIR}) include_directories(${COMMON_INCLUDE_DIR}) - OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${SOURCES}) - - if(SUBSYS_DEPS) - target_link_libraries("${LIB_NAME}" ${SUBSYS_DEPS}) - endif(SUBSYS_DEPS) + OD_ADD_LIBRARY("${SUBSYS_NAME}" SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) #PCL_MAKE_PKGCONFIG("${LIB_NAME}" "${SUBSYS_NAME}" "${SUBSYS_DESC}" "${SUBSYS_DEPS}" "" "" "" "") diff --git a/detectors/src/local2D/CMakeLists.txt b/detectors/src/local2D/CMakeLists.txt index 885ed83d..f121586e 100644 --- a/detectors/src/local2D/CMakeLists.txt +++ b/detectors/src/local2D/CMakeLists.txt @@ -1,7 +1,7 @@ set(SUBSYS_NAME local_image_detector) set(LIB_NAME od_${SUBSYS_NAME}) set(SUBSYS_DESC "The local feature matching based detector") -set(SUBSYS_DEPS od_common ${PCL_LIBRARIES} ${VTK_LIBRARIES} ${OpenCV_LIBS}) +set(SUBSYS_DEPS od_common ${PCL_LIBRARIES} ${VTK_LIBRARIES} ${OpenCV_LIBS} pugixml) set(build TRUE) #PCL_SUBSYS_OPTION(build "${SUBSYS_NAME}" "${SUBSYS_DESC}" ON) @@ -30,11 +30,7 @@ if(build) include_directories(${CMAKE_3RDPARTY_DIR}/pugixml/src/) include_directories(${COMMON_INCLUDE_DIR}) - OD_ADD_LIBRARY_ALL("${SUBSYS_NAME}" SRCS ${SOURCES}) - - if(SUBSYS_DEPS) - target_link_libraries("${LIB_NAME}" ${SUBSYS_DEPS} pugixml) - endif(SUBSYS_DEPS) + OD_ADD_LIBRARY("${SUBSYS_NAME}" SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) #PCL_MAKE_PKGCONFIG("${LIB_NAME}" "${SUBSYS_NAME}" "${SUBSYS_DESC}" "${SUBSYS_DEPS}" "" "" "" "") diff --git a/doc/doxygen/doxyfile.in b/doc/doxygen/doxyfile.in index c48be864..7f97cd11 100644 --- a/doc/doxygen/doxyfile.in +++ b/doc/doxygen/doxyfile.in @@ -280,7 +280,8 @@ INCLUDE_FILE_PATTERNS = *.h PREDEFINED = = "HAVE_QHULL=1" \ "HAVE_OPENNI=1" \ "HAVE_ENSENSO=1" \ - "PCL_DEPRECATED(x)=" + "PCL_DEPRECATED(x)=" \ + DOXYGEN_SHOULD_SKIP_THIS EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES diff --git a/examples/apps/CMakeLists.txt b/examples/apps/CMakeLists.txt index d225f8ec..d7f02065 100644 --- a/examples/apps/CMakeLists.txt +++ b/examples/apps/CMakeLists.txt @@ -2,12 +2,18 @@ include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) include_directories(${CMAKE_3RDPARTY_DIR}/pugixml/src/) include_directories(${OD_BINARY_DIR}/generated) +include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) + +OD_ADD_EXAMPLE(odcadrecog_test_single_model + FILES cadrecog2D/od_test_single_db_single_model.cpp + LINK_WITH od_common od_local_image_detector +) -OD_ADD_EXAMPLE(odcadrecog_test_single_model FILES cadrecog2D/od_test_single_db_single_model.cpp - LINK_WITH od_common od_local_image_detector ) set(SOURCE_FILES version/opendetection.cpp) add_executable(opendetection ${SOURCE_FILES}) -OD_ADD_EXAMPLE(od_multihog_app FILES global2D/od_multihog_app.cpp -LINK_WITH od_common od_global_image_detector) +OD_ADD_EXAMPLE(od_multihog_app + FILES global2D/od_multihog_app.cpp + LINK_WITH od_common od_global_image_detector +) diff --git a/examples/objectdetector/CMakeLists.txt b/examples/objectdetector/CMakeLists.txt index b9f8539d..05e07036 100644 --- a/examples/objectdetector/CMakeLists.txt +++ b/examples/objectdetector/CMakeLists.txt @@ -4,89 +4,104 @@ include_directories(${COMMON_INCLUDE_DIR}) include_directories(${SIMPLE_RANSAC_INCLUDE_DIR}) include_directories(${DETECTORS_IMPL_DIR}) include_directories(${CMAKE_3RDPARTY_DIR}/pugixml/src/) +include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) option(camera_recognition_example "Build the camera recognition example" ON) if(od_image_camera_example) - OD_ADD_EXAMPLE(od_image_camera FILES od_image_cadrecog_camera.cpp - LINK_WITH od_common od_local_image_detector) + OD_ADD_EXAMPLE(od_image_camera + FILES od_image_cadrecog_camera.cpp + LINK_WITH od_common od_local_image_detector) endif() option(image_recognition_example "Build the image recognition example" ON) if(image_recognition_example) - OD_ADD_EXAMPLE(od_example_files FILES od_image_cadrecog_files.cpp - LINK_WITH od_common od_local_image_detector) + OD_ADD_EXAMPLE(od_example_files + FILES od_image_cadrecog_files.cpp + LINK_WITH od_common od_local_image_detector) endif() option(hog_train_example "Build the hog train example" ON) if(hog_train_example) - OD_ADD_EXAMPLE(od_hog_train FILES od_hog_train.cpp - LINK_WITH od_common od_global_image_detector) + OD_ADD_EXAMPLE(od_hog_train + FILES od_hog_train.cpp + LINK_WITH od_common od_global_image_detector) endif() option(image_hog_files_example "Build the hog image example" ON) if(image_hog_files_example) - OD_ADD_EXAMPLE(od_image_hog_files FILES od_image_hog_files.cpp - LINK_WITH od_common od_global_image_detector) + OD_ADD_EXAMPLE(od_image_hog_files + FILES od_image_hog_files.cpp + LINK_WITH od_common od_global_image_detector) endif() option(image_customhog_example "Build the custom hog example" ON) if(image_customhog_example) - OD_ADD_EXAMPLE(od_image_customhog FILES od_image_customhog.cpp - LINK_WITH od_common od_global_image_detector) + OD_ADD_EXAMPLE(od_image_customhog + FILES od_image_customhog.cpp + LINK_WITH od_common od_global_image_detector) endif() option(multialgo_files_example "Build the multialgo_files example" ON) if(multialgo_files_example) - OD_ADD_EXAMPLE(od_multialgo_files FILES od_multialgo_files.cpp - LINK_WITH od_common od_global_image_detector) + OD_ADD_EXAMPLE(od_multialgo_files + FILES od_multialgo_files.cpp + LINK_WITH od_common od_global_image_detector) endif() option(multialgo_pc_example "Build the multialgo_pc example" ON) if(multialgo_pc_example) - OD_ADD_EXAMPLE(od_multialgo_pc FILES od_multialgo_pc.cpp - LINK_WITH od_common od_global_image_detector) + OD_ADD_EXAMPLE(od_multialgo_pc + FILES od_multialgo_pc.cpp + LINK_WITH od_common od_global_image_detector) endif() option(cascade_cam_example "Build the cascade camera example" ON) if(cascade_cam_example) - OD_ADD_EXAMPLE(od_cascade_cam FILES od_cascade_cam.cpp - LINK_WITH od_common od_global_image_detector) + OD_ADD_EXAMPLE(od_cascade_cam + FILES od_cascade_cam.cpp + LINK_WITH od_common od_global_image_detector) endif() option(cascade_files_example "Build the cascade file example" ON) if(cascade_files_example) - OD_ADD_EXAMPLE(od_cascade_files FILES od_cascade_files.cpp - LINK_WITH od_common od_global_image_detector) + OD_ADD_EXAMPLE(od_cascade_files + FILES od_cascade_files.cpp + LINK_WITH od_common od_global_image_detector) endif() option(image_facerecog_example "Build the face recognition example" ON) if(image_facerecog_example) - OD_ADD_EXAMPLE(od_image_facerecog FILES od_image_facerecog.cpp - LINK_WITH od_common od_global_image_detector) + OD_ADD_EXAMPLE(od_image_facerecog + FILES od_image_facerecog.cpp + LINK_WITH od_common od_global_image_detector) endif() option(pc_global_example "Build the global detector example" ON) if(pc_global_example) - OD_ADD_EXAMPLE(od_example_pc_global FILES od_pc_global.cpp - LINK_WITH od_common od_pointcloud_global_detector) + OD_ADD_EXAMPLE(od_example_pc_global + FILES od_pc_global.cpp + LINK_WITH od_common od_pointcloud_global_detector) endif() option(pc_global_files_example "Build the global detector file example" ON) if(pc_global_files_example) - OD_ADD_EXAMPLE(od_example_pc_global_files FILES od_pc_global_files.cpp - LINK_WITH od_common od_pointcloud_global_detector) + OD_ADD_EXAMPLE(od_example_pc_global_files + FILES od_pc_global_files.cpp + LINK_WITH od_common od_pointcloud_global_detector) endif() option(pc_global_real_time_example "Build the global detector real time example" ON) if(pc_global_real_time_example) - OD_ADD_EXAMPLE(od_example_pc_global_real_time FILES od_pc_global_real_time.cpp - LINK_WITH od_common od_pointcloud_global_detector) + OD_ADD_EXAMPLE(od_example_pc_global_real_time + FILES od_pc_global_real_time.cpp + LINK_WITH od_common od_pointcloud_global_detector) endif() option(framegenerator_example "Build the frame generator example" ON) if(framegenerator_example) - OD_ADD_EXAMPLE(od_example_framegenerator FILES od_example_framegenerator.cpp - LINK_WITH od_common) + OD_ADD_EXAMPLE(od_example_framegenerator + FILES od_example_framegenerator.cpp + LINK_WITH od_common) endif() From 6150162789dcaaa17848d37f3bec3b47ef8a630f Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Tue, 14 Jun 2016 15:25:25 +0200 Subject: [PATCH 23/79] fixes svmlight binding to the codingstyle --- common/include/od/common/bindings/svmlight.h | 36 ++-- common/src/bindings/svmlight.cpp | 202 +++++++++--------- .../src/global2D/training/ODHOGTrainer.cpp | 19 +- 3 files changed, 123 insertions(+), 134 deletions(-) diff --git a/common/include/od/common/bindings/svmlight.h b/common/include/od/common/bindings/svmlight.h index b0cf6ba4..880d0d90 100644 --- a/common/include/od/common/bindings/svmlight.h +++ b/common/include/od/common/bindings/svmlight.h @@ -49,10 +49,10 @@ Unless required by applicable law or agreed to in writing, software distributed * */ #pragma once -#include <stdio.h> +#include <iostream> #include <vector> #include <string> - + #ifndef DOXYGEN_SHOULD_SKIP_THIS namespace svmlight { extern "C" { @@ -62,33 +62,33 @@ namespace svmlight { } #endif -using namespace svmlight; class SVMlight { private: - DOC ** docs; // training examples - long totwords, totdoc, i; // support vector stuff - double * target; - double * alpha_in; - KERNEL_CACHE * kernel_cache; - MODEL * model; // SVM model + + svmlight::DOC ** docs_; // training examples + long totwords_, totdoc_; // support vector stuff + double * target_; + double * alpha_in_; + svmlight::KERNEL_CACHE * kernel_cache_; + svmlight::MODEL * model_; // SVM model SVMlight(); virtual ~SVMlight(); public: - LEARN_PARM * learn_parm; - KERNEL_PARM * kernel_parm; + svmlight::LEARN_PARM learn_parm_; + svmlight::KERNEL_PARM kernel_parm_; static SVMlight * getInstance(); - void saveModelToFile(const std::string _modelFileName); + void saveModelToFile(const std::string model_file_name); - void loadModelFromFile(const std::string _modelFileName); + void loadModelFromFile(const std::string model_file_name); // read in a problem (in svmlight format) - void read_problem(char* filename); + void read_problem(char * filename); // Calls the actual machine learning algorithm void train(); @@ -96,10 +96,9 @@ class SVMlight { /** * Generates a single detecting feature vector (vec1) from the trained support vectors, for use e.g. with the HOG algorithm * vec1 = sum_1_n (alpha_y*x_i). (vec1 is a 1 x n column vector. n = feature vector length) - * @param singleDetectorVector resulting single detector vector for use in openCV HOG - * @param singleDetectorVectorIndices dummy vector for this implementation + * @param single_detector_vector resulting single detector vector for use in openCV HOG */ - void getSingleDetectingVector(std::vector<float>& singleDetectorVector, std::vector<unsigned int>& singleDetectorVectorIndices); + void getSingleDetectingVector(std::vector<float> & single_detector_vector); /** * Return model detection threshold / bias @@ -107,7 +106,6 @@ class SVMlight { */ float getThreshold() const; - const char* getSVMName() const; - + std::string getSVMName() const; }; diff --git a/common/src/bindings/svmlight.cpp b/common/src/bindings/svmlight.cpp index 516e2fe6..177abc32 100644 --- a/common/src/bindings/svmlight.cpp +++ b/common/src/bindings/svmlight.cpp @@ -51,111 +51,107 @@ Unless required by applicable law or agreed to in writing, software distributed #include "od/common/bindings/svmlight.h" - - SVMlight::SVMlight(){ - // Init variables - alpha_in = NULL; - kernel_cache = NULL; // Cache not needed with linear kernel - model = (MODEL *) my_malloc(sizeof (MODEL)); - learn_parm = new LEARN_PARM; - kernel_parm = new KERNEL_PARM; - // Init parameters - verbosity = 1; // Show some messages -v 1 - learn_parm->alphafile[0] = ' '; // NULL; // Important, otherwise files with strange/invalid names appear in the working directory - // learn_parm->alphafile = NULL; // Important, otherwise files with strange/invalid names appear in the working directory - learn_parm->biased_hyperplane = 1; - learn_parm->sharedslack = 0; // 1 - learn_parm->skip_final_opt_check = 0; - learn_parm->svm_maxqpsize = 10; - learn_parm->svm_newvarsinqp = 0; - learn_parm->svm_iter_to_shrink = 2; // 2 is for linear; - learn_parm->kernel_cache_size = 40; - learn_parm->maxiter = 100000; - learn_parm->svm_costratio = 1.0; - learn_parm->svm_costratio_unlab = 1.0; - learn_parm->svm_unlabbound = 1E-5; - learn_parm->eps = 0.1; - learn_parm->transduction_posratio = -1.0; - learn_parm->epsilon_crit = 0.001; - learn_parm->epsilon_a = 1E-15; - learn_parm->compute_loo = 0; - learn_parm->rho = 1.0; - learn_parm->xa_depth = 0; - // The HOG paper uses a soft classifier (C = 0.01), set to 0.0 to get the default calculation - learn_parm->svm_c = 0.01; // -c 0.01 - learn_parm->type = REGRESSION; - learn_parm->remove_inconsistent = 0; // -i 0 - Important - kernel_parm->rbf_gamma = 1.0; - kernel_parm->coef_lin = 1; - kernel_parm->coef_const = 1; - kernel_parm->kernel_type = LINEAR; // -t 0 - kernel_parm->poly_degree = 3; - } - - SVMlight::~SVMlight() { - // Cleanup area - // Free the memory used for the cache - if (kernel_cache) - kernel_cache_cleanup(kernel_cache); - free(alpha_in); - free_model(model, 0); - for (i = 0; i < totdoc; i++) - free_example(docs[i], 1); - free(docs); - free(target); - } - - void SVMlight::saveModelToFile(const std::string _modelFileName) { - write_model(const_cast<char*>(_modelFileName.c_str()), model); - } - - void SVMlight::loadModelFromFile(const std::string _modelFileName) { - model = read_model(const_cast<char*>(_modelFileName.c_str())); - } - - void SVMlight::read_problem(char* filename) { - read_documents(filename, &docs, &target, &totwords, &totdoc); - } - - void SVMlight::train() { - svm_learn_regression(docs, target, totdoc, totwords, learn_parm, kernel_parm, &kernel_cache, model); +SVMlight::SVMlight(){ + // Init variables + alpha_in_ = NULL; + kernel_cache_ = NULL; // Cache not needed with linear kernel + model_ = static_cast<svmlight::MODEL *>(svmlight::my_malloc(sizeof(svmlight::MODEL))); + // Init parameters + svmlight::verbosity = 1; // Show some messages -v 1 + learn_parm_.alphafile[0] = ' '; // NULL; // Important, otherwise files with strange/invalid names appear in the working directory + // learn_parm_->alphafile = NULL; // Important, otherwise files with strange/invalid names appear in the working directory + learn_parm_.biased_hyperplane = 1; + learn_parm_.sharedslack = 0; // 1 + learn_parm_.skip_final_opt_check = 0; + learn_parm_.svm_maxqpsize = 10; + learn_parm_.svm_newvarsinqp = 0; + learn_parm_.svm_iter_to_shrink = 2; // 2 is for linear; + learn_parm_.kernel_cache_size = 40; + learn_parm_.maxiter = 100000; + learn_parm_.svm_costratio = 1.0; + learn_parm_.svm_costratio_unlab = 1.0; + learn_parm_.svm_unlabbound = 1E-5; + learn_parm_.eps = 0.1; + learn_parm_.transduction_posratio = -1.0; + learn_parm_.epsilon_crit = 0.001; + learn_parm_.epsilon_a = 1E-15; + learn_parm_.compute_loo = 0; + learn_parm_.rho = 1.0; + learn_parm_.xa_depth = 0; + // The HOG paper uses a soft classifier (C = 0.01), set to 0.0 to get the default calculation + learn_parm_.svm_c = 0.01; // -c 0.01 + learn_parm_.type = REGRESSION; + learn_parm_.remove_inconsistent = 0; // -i 0 - Important + kernel_parm_.rbf_gamma = 1.0; + kernel_parm_.coef_lin = 1; + kernel_parm_.coef_const = 1; + kernel_parm_.kernel_type = LINEAR; // -t 0 + kernel_parm_.poly_degree = 3; +} + +SVMlight::~SVMlight() { + // Cleanup area + // Free the memory used for the cache + if(kernel_cache_) + svmlight::kernel_cache_cleanup(kernel_cache_); + free(alpha_in_); + free_model(model_, 0); + for(size_t i = 0; i < totdoc_; ++i) + free_example(docs_[i], 1); + free(docs_); + free(target_); +} + +void SVMlight::saveModelToFile(const std::string model_file_name) { + write_model(const_cast<char*>(model_file_name.c_str()), model_); +} + +void SVMlight::loadModelFromFile(const std::string model_file_name) { + model_ = svmlight::read_model(const_cast<char*>(model_file_name.c_str())); +} + +void SVMlight::read_problem(char * file_name) { + read_documents(file_name, &docs_, &target_, &totwords_, &totdoc_); +} + +void SVMlight::train() { + svm_learn_regression(docs_, target_, totdoc_, totwords_, &learn_parm_, &kernel_parm_, &kernel_cache_, model_); +} + +void SVMlight::getSingleDetectingVector(std::vector<float> & single_detector_vector) { + // Now we use the trained svm to retrieve the single detector vector + svmlight::DOC ** supveclist = model_->supvec; + std::cout << "Calculating single descriptor vector out of support vectors (may take some time)" << std::endl; + // Retrieve single detecting vector (v1) from returned ones by calculating vec1 = sum_1_n (alpha_y*x_i). (vec1 is a n x1 column vector. n = feature vector length) + single_detector_vector.clear(); + single_detector_vector.resize(model_->totwords, 0.); + std::cout << "Resulting vector size " << single_detector_vector.size() << std::endl; + + // Walk over every support vector + for(size_t ssv = 1; ssv < model_->sv_num; ++ssv) { // Don't know what's inside model->supvec[0] ?! + // Get a single support vector + svmlight::DOC * singleSupportVector = supveclist[ssv]; // Get next support vector + svmlight::SVECTOR * singleSupportVectorValues = singleSupportVector->fvec; + svmlight::WORD singleSupportVectorComponent; + // Walk through components of the support vector and populate our detector vector + for(unsigned long singleFeature = 0; singleFeature < model_->totwords; ++singleFeature) { + singleSupportVectorComponent = singleSupportVectorValues->words[singleFeature]; + single_detector_vector.at(singleSupportVectorComponent.wnum-1) += (singleSupportVectorComponent.weight * model_->alpha[ssv]); + } } +} +float SVMlight::getThreshold() const { + return model_->b; +} - void SVMlight::getSingleDetectingVector(std::vector<float>& singleDetectorVector, std::vector<unsigned int>& singleDetectorVectorIndices) { - // Now we use the trained svm to retrieve the single detector vector - DOC** supveclist = model->supvec; - printf("Calculating single descriptor vector out of support vectors (may take some time)\n"); - // Retrieve single detecting vector (v1) from returned ones by calculating vec1 = sum_1_n (alpha_y*x_i). (vec1 is a n x1 column vector. n = feature vector length) - singleDetectorVector.clear(); - singleDetectorVector.resize(model->totwords, 0.); - printf("Resulting vector size %lu\n", singleDetectorVector.size()); - - // Walk over every support vector - for (long ssv = 1; ssv < model->sv_num; ++ssv) { // Don't know what's inside model->supvec[0] ?! - // Get a single support vector - DOC* singleSupportVector = supveclist[ssv]; // Get next support vector - SVECTOR* singleSupportVectorValues = singleSupportVector->fvec; - WORD singleSupportVectorComponent; - // Walk through components of the support vector and populate our detector vector - for (unsigned long singleFeature = 0; singleFeature < model->totwords; ++singleFeature) { - singleSupportVectorComponent = singleSupportVectorValues->words[singleFeature]; - singleDetectorVector.at(singleSupportVectorComponent.wnum-1) += (singleSupportVectorComponent.weight * model->alpha[ssv]); - } - } - } - - float SVMlight::getThreshold() const { - return model->b; - } - - const char * SVMlight::getSVMName() const { - return "SVMlight"; - } +std::string SVMlight::getSVMName() const { + return std::string("SVMlight"); +} - /// Singleton - SVMlight * SVMlight::getInstance() { - static SVMlight theInstance; - return &theInstance; - } +/// Singleton +SVMlight * SVMlight::getInstance() { + static SVMlight theInstance; + return &theInstance; +} diff --git a/detectors/src/global2D/training/ODHOGTrainer.cpp b/detectors/src/global2D/training/ODHOGTrainer.cpp index 3b0753fe..f29e4eb8 100644 --- a/detectors/src/global2D/training/ODHOGTrainer.cpp +++ b/detectors/src/global2D/training/ODHOGTrainer.cpp @@ -30,9 +30,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/global2D/training/ODHOGTrainer.h" - -#define TRAINHOG_SVM_TO_TRAIN SVMlight - namespace od { namespace g2d @@ -79,7 +76,6 @@ namespace od descriptor_vector_file_ = getSpecificTrainingDataLocation() + "/descriptorvector.dat"; descriptor_vector_file_ = getSpecificTrainingDataLocation() + "/descriptorvectorHard.dat"; - } void ODHOGTrainer::saveDescriptorVectorToFile(const std::vector<float> & descriptor_vector, const std::string & file_name) @@ -116,7 +112,6 @@ namespace od cv::Mat image_data_orig, image_data; image_data_orig = cv::imread(image_file_name, 0); - if(image_data_orig.empty()) { feature_vector.clear(); @@ -275,23 +270,23 @@ namespace od double ODHOGTrainer::trainWithSVMLight(const std::string & svm_model_file, const std::string & svm_descriptor_file, std::vector<float> & descriptor_vector) { + SVMlight * svmlight= SVMlight::getInstance(); //training takes featurefile as input, produces hitthreshold and vector as output - std::cout << "Calling " << TRAINHOG_SVM_TO_TRAIN::getInstance()->getSVMName() << std::endl; - TRAINHOG_SVM_TO_TRAIN::getInstance()->read_problem(const_cast<char *> (features_file_.c_str())); - TRAINHOG_SVM_TO_TRAIN::getInstance()->train(); // Call the core libsvm training procedure + std::cout << "Calling " << svmlight->getSVMName() << std::endl; + svmlight->read_problem(const_cast<char *> (features_file_.c_str())); + svmlight->train(); // Call the core libsvm training procedure std::cout << "Training done, saving model file!"<< std::endl; - TRAINHOG_SVM_TO_TRAIN::getInstance()->saveModelToFile(svm_model_file); + svmlight->saveModelToFile(svm_model_file); std::cout << "Generating representative single HOG feature vector using svmlight!" << std::endl; descriptor_vector.resize(0); - std::vector<unsigned int> descriptor_vector_indices; // Generate a single detecting feature vector (v1 | b) from the trained support vectors, for use e.g. with the HOG algorithm - TRAINHOG_SVM_TO_TRAIN::getInstance()->getSingleDetectingVector(descriptor_vector, descriptor_vector_indices); + svmlight->getSingleDetectingVector(descriptor_vector); // And save the precious to file system saveDescriptorVectorToFile(descriptor_vector, svm_descriptor_file); // Detector detection tolerance threshold - hit_threshold_ = TRAINHOG_SVM_TO_TRAIN::getInstance()->getThreshold(); + hit_threshold_ = svmlight->getThreshold(); return hit_threshold_; } From 085ba7365949b7670fc528b9c379d78e543e4578 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Tue, 14 Jun 2016 16:39:21 +0200 Subject: [PATCH 24/79] fixes alla warnings --- CMakeLists.txt | 2 +- .../include/od/common/pipeline/ODDetection.h | 2 +- .../include/od/common/pipeline/ODDetector.h | 7 ++-- .../od/common/pipeline/ODObjectDetector.h | 9 ++--- common/src/bindings/svmlight.cpp | 6 ++-- common/src/pipeline/ODDetection.cpp | 2 +- .../detection/ODCADDetector3DGlobal.hpp | 27 ++++++++++----- .../misc/detection/ODDetectorMultiAlgo.hpp | 33 +++++++++++++++++++ .../od/detectors/global2D/ODFaceRecognizer.h | 12 +++++-- .../global2D/detection/ODCascadeDetector.h | 7 ++-- .../global2D/detection/ODHOGDetector.h | 4 ++- .../global2D/training/ODHOGTrainer.h | 2 +- .../global3D/ODPointCloudGlobalMatching.h | 1 + .../training/ODCADDetectTrainer3DGlobal.h | 2 ++ .../detection/ODCADRecognizer2DLocal.h | 10 ++++-- detectors/src/global2D/ODFaceRecognizer.cpp | 29 +++++++++++++++- .../global2D/detection/ODCascadeDetector.cpp | 6 ++++ .../src/global2D/detection/ODHOGDetector.cpp | 1 - .../src/global2D/training/ODHOGTrainer.cpp | 13 -------- .../detection/ODCADRecognizer2DLocal.cpp | 10 ++++++ .../ODCADRecogTrainerSnapshotBased.cpp | 6 ++-- .../od_test_single_db_single_model.cpp | 2 +- examples/apps/global2D/od_multihog_app.cpp | 4 +-- 23 files changed, 141 insertions(+), 56 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 23907fe2..8abd3e53 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 2.8) project(OpenDetection) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall") # Initialize variables set(OD_SOURCE_DIR ${OpenDetection_SOURCE_DIR}) diff --git a/common/include/od/common/pipeline/ODDetection.h b/common/include/od/common/pipeline/ODDetection.h index ce4e3b44..fea5cb13 100644 --- a/common/include/od/common/pipeline/ODDetection.h +++ b/common/include/od/common/pipeline/ODDetection.h @@ -176,7 +176,7 @@ namespace od virtual ~ODDetections(); - int size() const; + unsigned int size() const; void push_back(shared_ptr<ODDetection> detection); diff --git a/common/include/od/common/pipeline/ODDetector.h b/common/include/od/common/pipeline/ODDetector.h index df787d39..efb36ef6 100644 --- a/common/include/od/common/pipeline/ODDetector.h +++ b/common/include/od/common/pipeline/ODDetector.h @@ -49,8 +49,8 @@ namespace od ODDetector(const std::string & training_data_location) : ODDetectorCommon(training_data_location) {} - virtual shared_ptr<ODDetections> detect(shared_ptr<ODScene> scene){} - virtual shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene){} + virtual shared_ptr<ODDetections> detect(shared_ptr<ODScene> scene) = 0; + virtual shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene) = 0; bool meta_info_; @@ -66,8 +66,7 @@ namespace od class ODDetector2D: public ODDetector { public: - ODDetector2D(const std::string & trained_data_location) : ODDetector(trained_data_location) - { } + ODDetector2D(const std::string & trained_data_location) : ODDetector(trained_data_location) {} shared_ptr<ODDetections> detect(shared_ptr<ODScene> scene) { diff --git a/common/include/od/common/pipeline/ODObjectDetector.h b/common/include/od/common/pipeline/ODObjectDetector.h index 8849569a..d5162281 100644 --- a/common/include/od/common/pipeline/ODObjectDetector.h +++ b/common/include/od/common/pipeline/ODObjectDetector.h @@ -111,15 +111,12 @@ namespace od void setDetectionMethod(const DetectionMethod & detection_method); bool getAlwaysTrain() const; - void setAlwaysTrain(bool always_train); std::string getTrainingInputLocation() const; - void setTrainingInputLocation(const std::string & training_input_location); std::string getTrainingDataLocation() const; - void setTrainingDataLocation(const std::string & training_data_location); std::string getSpecificTrainingDataLocation(); @@ -131,10 +128,10 @@ namespace od virtual int train() = 0; - virtual int detect(shared_ptr<ODScene> scene, const std::vector<shared_ptr<ODDetection> > & detections) {} + virtual int detect(shared_ptr<ODScene> scene, const std::vector<shared_ptr<ODDetection> > & detections) = 0; - virtual shared_ptr<ODDetection> detect(shared_ptr<ODScene> scene) {} - virtual shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene) {} + virtual shared_ptr<ODDetection> detect(shared_ptr<ODScene> scene) = 0; + virtual shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene) = 0; protected: diff --git a/common/src/bindings/svmlight.cpp b/common/src/bindings/svmlight.cpp index 177abc32..171435fa 100644 --- a/common/src/bindings/svmlight.cpp +++ b/common/src/bindings/svmlight.cpp @@ -96,7 +96,7 @@ SVMlight::~SVMlight() { svmlight::kernel_cache_cleanup(kernel_cache_); free(alpha_in_); free_model(model_, 0); - for(size_t i = 0; i < totdoc_; ++i) + for(long i = 0; i < totdoc_; ++i) free_example(docs_[i], 1); free(docs_); free(target_); @@ -128,13 +128,13 @@ void SVMlight::getSingleDetectingVector(std::vector<float> & single_detector_vec std::cout << "Resulting vector size " << single_detector_vector.size() << std::endl; // Walk over every support vector - for(size_t ssv = 1; ssv < model_->sv_num; ++ssv) { // Don't know what's inside model->supvec[0] ?! + for(long ssv = 1; ssv < model_->sv_num; ++ssv) { // Don't know what's inside model->supvec[0] ?! // Get a single support vector svmlight::DOC * singleSupportVector = supveclist[ssv]; // Get next support vector svmlight::SVECTOR * singleSupportVectorValues = singleSupportVector->fvec; svmlight::WORD singleSupportVectorComponent; // Walk through components of the support vector and populate our detector vector - for(unsigned long singleFeature = 0; singleFeature < model_->totwords; ++singleFeature) { + for(long singleFeature = 0; singleFeature < model_->totwords; ++singleFeature) { singleSupportVectorComponent = singleSupportVectorValues->words[singleFeature]; single_detector_vector.at(singleSupportVectorComponent.wnum-1) += (singleSupportVectorComponent.weight * model_->alpha[ssv]); } diff --git a/common/src/pipeline/ODDetection.cpp b/common/src/pipeline/ODDetection.cpp index f103a933..84183e5b 100644 --- a/common/src/pipeline/ODDetection.cpp +++ b/common/src/pipeline/ODDetection.cpp @@ -167,7 +167,7 @@ namespace od detections_.resize(0); } - int ODDetections::size() const + unsigned int ODDetections::size() const { return detections_.size(); } diff --git a/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp b/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp index ba0b865e..f0b3a262 100644 --- a/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp +++ b/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp @@ -57,6 +57,9 @@ namespace od desc_name_ = desc_name; } + virtual shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene); + virtual shared_ptr<ODDetections> detect(shared_ptr<ODScene> scene); + protected: int NN; @@ -65,6 +68,20 @@ namespace od }; + template<typename PointT> + shared_ptr<ODDetections> ODCADDetector3DGlobal<PointT>::detect(shared_ptr<ODScene> scene) + { + std::cout << "not implemented, use with shared_ptr<ODScenePointCloud<PointT>>" <<std::endl; + return nullptr; + }; + + template<typename PointT> + shared_ptr<ODDetections> ODCADDetector3DGlobal<PointT>::detectOmni(shared_ptr<ODScene> scene) + { + std::cout << "not implemented, use with shared_ptr<ODScenePointCloud<PointT>>" <<std::endl; + return nullptr; + }; + template<typename PointT> void ODCADDetector3DGlobal<PointT>::init() { @@ -157,13 +174,11 @@ namespace od typename pcl::PointCloud<PointT>::Ptr frame; float Z_DIST_ = 1.25f; - float text_scale = 0.015f; frame = scene->getPointCloud(); pcl::PointCloud<pcl::PointXYZ>::Ptr xyz_points(new pcl::PointCloud<pcl::PointXYZ>); pcl::copyPointCloud(*frame, *xyz_points); - //Step 1 -> Segment pcl::apps::DominantPlaneSegmentation<pcl::PointXYZ> dps; dps.setInputCloud(xyz_points); @@ -179,10 +194,8 @@ namespace od dps.compute_fast(clusters); dps.getIndicesClusters(indices); Eigen::Vector4f table_plane_; - Eigen::Vector3f normal_plane_ = Eigen::Vector3f(table_plane_[0], table_plane_[1], table_plane_[2]); dps.getTableCoefficients(table_plane_); - for(size_t i = 0; i < clusters.size(); ++i) { @@ -219,8 +232,6 @@ namespace od shared_ptr<ODDetections> detections = make_shared<ODDetections>(); typename pcl::PointCloud<PointT>::Ptr frame; - float Z_DIST_ = 1.25f; - float text_scale = 0.015f; frame = scene->getPointCloud(); pcl::PointCloud<pcl::PointXYZ>::Ptr xyz_points(new pcl::PointCloud<pcl::PointXYZ>); @@ -236,10 +247,8 @@ namespace od global_->getConfidence(conf); //detection done! - std::string category = categories[0]; - //now fill up the detection: - shared_ptr<ODDetection> detection = make_shared<ODDetection3D>(ODDetection::OD_DETECTION_CLASS, categories[0], conf[0]); + shared_ptr<ODDetection> detection = make_shared<ODDetection3D>(ODDetection::OD_DETECTION_CLASS, categories[0], conf[0]); detections->push_back(detection); return detections; diff --git a/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp b/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp index 790f01ab..143df954 100644 --- a/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp +++ b/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp @@ -50,6 +50,8 @@ namespace od shared_ptr<ODDetections> detect(shared_ptr<ODScenePointCloud<PointT> > scene); shared_ptr<ODDetections3D> detectOmni(shared_ptr<ODScenePointCloud<PointT> > scene); + virtual shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene); + virtual shared_ptr<ODDetections> detect(shared_ptr<ODScene> scene); void init(); @@ -59,6 +61,7 @@ namespace od std::vector<shared_ptr<ODDetector3D<PointT> > > detectors_3d_; }; + template<typename PointT> class ODDetectorMultiAlgo : public ODDetector { @@ -73,6 +76,9 @@ namespace od shared_ptr<ODDetections> detect(shared_ptr<ODScenePointCloud<PointT> > scene); shared_ptr<ODDetections3D> detectOmni(shared_ptr<ODScenePointCloud<PointT> > scene); + virtual shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene); + virtual shared_ptr<ODDetections> detect(shared_ptr<ODScene> scene); + void init(); private: @@ -82,6 +88,33 @@ namespace od }; + template<typename PointT> + shared_ptr<ODDetections> ODDetectorMultiAlgo<PointT>::detectOmni(shared_ptr<ODScene> scene) + { + std::cout << "not implemented, use with shared_ptr<ODSceneImage> or shared_ptr<ODScenePointCloud<PointT>>" <<std::endl; + return nullptr; + } + + template<typename PointT> + shared_ptr<ODDetections> ODDetectorMultiAlgo<PointT>::detect(shared_ptr<ODScene> scene) + { + std::cout << "not implemented, use with shared_ptr<ODSceneImage> or shared_ptr<ODScenePointCloud<PointT>>" <<std::endl; + return nullptr; + } + + template<typename PointT> + shared_ptr<ODDetections> ODDetectorMultiAlgo2D<PointT>::detectOmni(shared_ptr<ODScene> scene) + { + std::cout << "not implemented, use with shared_ptr<ODSceneImage> or shared_ptr<ODScenePointCloud<PointT>>" <<std::endl; + return nullptr; + } + + template<typename PointT> + shared_ptr<ODDetections> ODDetectorMultiAlgo2D<PointT>::detect(shared_ptr<ODScene> scene) + { + std::cout << "not implemented, use with shared_ptr<ODSceneImage> or shared_ptr<ODScenePointCloud<PointT>>" <<std::endl; + return nullptr; + } //BASED ON 2D SCENE template<typename PointT> diff --git a/detectors/include/od/detectors/global2D/ODFaceRecognizer.h b/detectors/include/od/detectors/global2D/ODFaceRecognizer.h index 7d690d23..b6dd1980 100644 --- a/detectors/include/od/detectors/global2D/ODFaceRecognizer.h +++ b/detectors/include/od/detectors/global2D/ODFaceRecognizer.h @@ -63,6 +63,12 @@ namespace od int train(); shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene); + shared_ptr<ODDetections> detectOmni(shared_ptr<ODSceneImage> scene); + + virtual shared_ptr<ODDetection> detect(shared_ptr<ODScene> scene); + virtual shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene); + + virtual int detect(shared_ptr<ODScene> scene, const std::vector<shared_ptr<ODDetection> > & detections); const FaceRecogType & getRecogtype() const { @@ -99,12 +105,12 @@ namespace od cv::Ptr<cv::face::FaceRecognizer> cv_recognizer_; FaceRecogType recog_type_; - unsigned int im_width_; - unsigned int im_height_; int num_components_; double threshold_; + unsigned int im_height_; + unsigned int im_width_; - + private: void read_csv(const std::string & file_name, std::vector<cv::Mat> & images, std::vector<int> & labels, const std::string & separator = std::string(";")); diff --git a/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h b/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h index c286a8e5..0fb7f3fd 100644 --- a/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h +++ b/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h @@ -56,10 +56,13 @@ namespace od void init(); - shared_ptr<ODDetections2D> detectOmni(shared_ptr<ODSceneImage> scene); - shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene); + virtual shared_ptr<ODDetections2D> detectOmni(shared_ptr<ODSceneImage> scene); + virtual shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene); + + virtual shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene); private: + shared_ptr<cv::CascadeClassifier> haar_cascade_; double scale_factor_; diff --git a/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h b/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h index c8c166c1..25023ed9 100644 --- a/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h +++ b/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h @@ -64,7 +64,7 @@ namespace od const cv::Size & block_size = cv::Size(16,16), const cv::Size & block_stride = cv::Size(8,8), const cv::Size & cell_size = cv::Size(8,8), float hit_threshold = 0.0); - OD_DEFINE_ENUM_WITH_STRING_CONVERSIONS(SVMType, (OD_CUSTOM)(OD_DEFAULT_PEOPLE)(OD_DAIMLER_PEOPLE)(OD_FILE)) + OD_DEFINE_ENUM_WITH_STRING_CONVERSIONS(SVMType, (OD_DEFAULT_PEOPLE)(OD_DAIMLER_PEOPLE)(OD_FILE)) void init(); void load(const std::string & file_name); @@ -100,6 +100,8 @@ namespace od void printParameters(); + virtual shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene) {return nullptr;}; + protected: //properteis cv::Size win_size_; diff --git a/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h b/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h index 0143b30e..ed196582 100644 --- a/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h +++ b/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h @@ -180,7 +180,7 @@ namespace od //algo specific cv::Size training_padding_; cv::Point start_hog_pos_; - int no_features_neg_; + unsigned int no_features_neg_; cv::Size win_stride_; bool train_hard_negative_; diff --git a/detectors/include/od/detectors/global3D/ODPointCloudGlobalMatching.h b/detectors/include/od/detectors/global3D/ODPointCloudGlobalMatching.h index a7755902..f014b922 100644 --- a/detectors/include/od/detectors/global3D/ODPointCloudGlobalMatching.h +++ b/detectors/include/od/detectors/global3D/ODPointCloudGlobalMatching.h @@ -53,6 +53,7 @@ namespace od std::string desc_name_; shared_ptr<ODCADDetectTrainer3DGlobal> trainer_; shared_ptr<ODCADDetector3DGlobal<pcl::PointXYZ> > detector_; + }; } } diff --git a/detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h b/detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h index 8d60a948..5ae5630e 100644 --- a/detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h +++ b/detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h @@ -80,7 +80,9 @@ namespace od void setDescName(const std::string & desc_name); protected: + std::string desc_name_; + }; } } diff --git a/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h b/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h index 4e382e20..84acc442 100644 --- a/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h +++ b/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h @@ -97,12 +97,16 @@ namespace od void init(); - shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene); - shared_ptr<ODDetections3D> detectOmni(shared_ptr<ODSceneImage> scene); + virtual shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene); + virtual shared_ptr<ODDetections3D> detectOmni(shared_ptr<ODSceneImage> scene); + + virtual shared_ptr<ODDetections> detect(shared_ptr<ODScene> scene); + virtual shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene); protected: - bool detectSingleModel(shared_ptr<ODSceneImage> scene, const Model & model, shared_ptr<ODDetection3D> & detection3D, const cv::Mat & frame_viz); + bool detectSingleModel(shared_ptr<ODSceneImage> scene, const Model & model, shared_ptr<ODDetection3D> & detection3D, + const cv::Mat & frame_viz); std::string camera_intrinsic_file_; diff --git a/detectors/src/global2D/ODFaceRecognizer.cpp b/detectors/src/global2D/ODFaceRecognizer.cpp index fbcafb81..5ad257b0 100644 --- a/detectors/src/global2D/ODFaceRecognizer.cpp +++ b/detectors/src/global2D/ODFaceRecognizer.cpp @@ -41,6 +41,13 @@ namespace od TRAINED_DATA_EXT_ = "facerec.xml"; } + shared_ptr<ODDetections> ODFaceRecognizer::detectOmni(shared_ptr<ODSceneImage> scene) + { + std::cout << "not implemented, use detect()" <<std::endl; + return nullptr; + }; + + void ODFaceRecognizer::init() { switch(recog_type_) @@ -92,7 +99,7 @@ namespace od } catch(cv::Exception & e) { std::cerr << "Error opening file \"" << training_input_location_ << "\". Reason: " << e.msg << std::endl; - exit(1); + return -1; } cv_recognizer_->train(images, labels); fileutils::createTrainingDir(getSpecificTrainingDataLocation()); @@ -103,6 +110,26 @@ namespace od //the training set has atleast one image im_width_ = images[0].cols; im_height_ = images[0].rows; + + return 0; + } + + shared_ptr<ODDetection> ODFaceRecognizer::detect(shared_ptr<ODScene> scene) + { + std::cout << "not implemented, use with shared_ptr<ODSceneImage>" <<std::endl; + return nullptr; + }; + + shared_ptr<ODDetections> ODFaceRecognizer::detectOmni(shared_ptr<ODScene> scene) + { + std::cout << "not implemented, use with shared_ptr<ODSceneImage>" <<std::endl; + return nullptr; + }; + + int ODFaceRecognizer::detect(shared_ptr<ODScene> scene, const std::vector<shared_ptr<ODDetection> > & detections) + { + std::cout << "not implemented, use with shared_ptr<ODSceneImage>" <<std::endl; + return -1; } shared_ptr<ODDetections> ODFaceRecognizer::detect(shared_ptr<ODSceneImage> scene) diff --git a/detectors/src/global2D/detection/ODCascadeDetector.cpp b/detectors/src/global2D/detection/ODCascadeDetector.cpp index 21361293..719bc759 100644 --- a/detectors/src/global2D/detection/ODCascadeDetector.cpp +++ b/detectors/src/global2D/detection/ODCascadeDetector.cpp @@ -45,6 +45,12 @@ namespace od meta_info_ = true; } + shared_ptr<ODDetections> ODCascadeDetector::detectOmni(shared_ptr<ODScene> scene) + { + std::cout << "not implemented, use detect()" <<std::endl; + return nullptr; + }; + void ODCascadeDetector::init() { haar_cascade_ = make_shared<cv::CascadeClassifier>(fileutils::getFirstFile(getSpecificTrainingDataLocation(), TRAINED_DATA_ID_)); diff --git a/detectors/src/global2D/detection/ODHOGDetector.cpp b/detectors/src/global2D/detection/ODHOGDetector.cpp index e3e9f017..98c45636 100644 --- a/detectors/src/global2D/detection/ODHOGDetector.cpp +++ b/detectors/src/global2D/detection/ODHOGDetector.cpp @@ -216,7 +216,6 @@ namespace od std::cout << "Reading descriptor vector from file " << file_name << std::endl;; std::ifstream file; - float percent; file.open(file_name.c_str(), std::ios::in); if(file.good() && file.is_open()) { diff --git a/detectors/src/global2D/training/ODHOGTrainer.cpp b/detectors/src/global2D/training/ODHOGTrainer.cpp index f29e4eb8..48d9ce7a 100644 --- a/detectors/src/global2D/training/ODHOGTrainer.cpp +++ b/detectors/src/global2D/training/ODHOGTrainer.cpp @@ -35,16 +35,6 @@ namespace od namespace g2d { - static void storeCursor(void) - { - printf("\033[s"); - } - - static void resetCursor(void) - { - printf("\033[u"); - } - ODHOGTrainer::ODHOGTrainer(const std::string & training_input_location_, const std::string & trained_data_location_, const cv::Size & win_size, const cv::Size & block_size, const cv::Size & block_stride, const cv::Size & cell_size, float hit_threshold): ODTrainer(training_input_location_, trained_data_location_), win_size_(win_size), block_size_(block_size), @@ -89,7 +79,6 @@ namespace od if(file.good() && file.is_open()) { std::cout << "Saving " << descriptors_num << " descriptor vector features:\t" << std::endl; - storeCursor(); for(size_t feature = 0; feature < descriptors_num; ++feature) { if((feature % 10 == 0) || (feature == (descriptors_num - 1))) @@ -322,7 +311,6 @@ namespace od setlocale(LC_ALL, "POSIX"); std::cout << "Reading files, generating HOG features and save them to file: " << features_file_ << std::endl; - float percent; std::fstream file; file.open(features_file_.c_str(), std::fstream::out); @@ -404,7 +392,6 @@ namespace od std::cout << "Reading descriptor vector from file " << file_name << std::endl; std::ifstream file; - float percent; file.open(file_name.c_str(), std::ios::in); if(file.good() && file.is_open()) { diff --git a/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp b/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp index 47cee9af..54e03f47 100644 --- a/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp +++ b/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp @@ -256,7 +256,17 @@ namespace od } + shared_ptr<ODDetections> ODCADRecognizer2DLocal::detect(shared_ptr<ODScene> scene) + { + std::cout << "not implemented, use with shared_ptr<ODScene>" <<std::endl; + return nullptr; + }; + shared_ptr<ODDetections> ODCADRecognizer2DLocal::detectOmni(shared_ptr<ODScene> scene) + { + std::cout << "not implemented, use with shared_ptr<ODScene>" <<std::endl; + return nullptr; + }; shared_ptr<ODDetections> ODCADRecognizer2DLocal::detect(shared_ptr<ODSceneImage> scene) { diff --git a/detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp b/detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp index 33bf7df2..8af1ba65 100644 --- a/detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp +++ b/detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp @@ -253,11 +253,11 @@ namespace od //headers fout << pairs.size() << endl; fout << 3 << " " << descriptors.cols << endl; - for(int i = 0; i < pairs.size(); i++) + for(size_t i = 0; i < pairs.size(); i++) { fout << pairs[i].first.x << " " << pairs[i].first.y << " " << pairs[i].first.z << endl; fout << pairs[i].second.pt.x << " " << pairs[i].second.pt.y << " " << pairs[i].second.octave << " " << pairs[i].second.angle << " " << pairs[i].second.response << " " << pairs[i].second.size << endl; - for(size_t j = 0; j < descriptors.cols; ++j) + for(int j = 0; j < descriptors.cols; ++j) { fout << descriptors.at<float>(i, j) << " "; } @@ -276,7 +276,7 @@ namespace od //root.append_attribute("objid") = "1"; pugi::xml_node points_node = root.append_child("Points"); - for(int i = 0; i < pairs.size(); i++) + for(size_t i = 0; i < pairs.size(); i++) { pugi::xml_node p_node = points_node.append_child("Point"); diff --git a/examples/apps/cadrecog2D/od_test_single_db_single_model.cpp b/examples/apps/cadrecog2D/od_test_single_db_single_model.cpp index 1c2dbbf6..1303dab6 100644 --- a/examples/apps/cadrecog2D/od_test_single_db_single_model.cpp +++ b/examples/apps/cadrecog2D/od_test_single_db_single_model.cpp @@ -67,7 +67,7 @@ int main(int argc, char *argv[]) logfile << scene->getPath() << endl; logfile << detections->size() << endl; - for (int i = 0; i < detections->size(); i++) + for(size_t i = 0; i < detections->size(); i++) { boost::shared_ptr<od::ODDetection3D> detection = detections->at(i); detection->printSelf(); diff --git a/examples/apps/global2D/od_multihog_app.cpp b/examples/apps/global2D/od_multihog_app.cpp index e1e19bb3..2c4a94ee 100644 --- a/examples/apps/global2D/od_multihog_app.cpp +++ b/examples/apps/global2D/od_multihog_app.cpp @@ -56,9 +56,9 @@ int main(int argc, char *argv[]) messages.push_back("OpenCV Daimler People"); detectors.push_back(detector2); - od::g2d::ODHOGDetector detector(trained_data_dir); + od::g2d::ODHOGDetector detector3(trained_data_dir); messages.push_back("Custom HOG from trained data"); - detectors.push_back(detector); + detectors.push_back(detector3); //init all detectors for(size_t i = 0; i < detectors.size(); ++i) From 85f6ada9600d8be413345f8a2bcf9e0ce2a506df Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Thu, 16 Jun 2016 12:28:17 +0200 Subject: [PATCH 25/79] removes ome unused code, fixes coding style and removes unused includes to improve compile time --- .../include/od/common/pipeline/ODDetection.h | 12 +- .../include/od/common/pipeline/ODDetector.h | 4 +- .../od/common/pipeline/ODObjectDetector.h | 5 +- common/include/od/common/pipeline/ODTrainer.h | 2 +- .../od/common/utils/ODFrameGenerator.h | 1 - common/src/pipeline/ODObjectDetector.cpp | 14 +- .../detection/ODCADDetector3DGlobal.hpp | 36 ++-- .../misc/detection/ODDetectorMultiAlgo.hpp | 121 ++++++------ .../global2D/training/ODHOGTrainer.h | 6 +- .../global3D/ODPointCloudGlobalMatching.h | 2 + .../training/ODCADDetectTrainer3DGlobal.h | 4 +- .../detectors/local2D/ODImageLocalMatching.h | 2 +- .../detection/ODCADRecognizer2DLocal.h | 8 +- .../training/ODCADRecogTrainerSnapshotBased.h | 102 +++++----- detectors/src/global2D/ODFaceRecognizer.cpp | 28 +-- .../global2D/detection/ODCascadeDetector.cpp | 21 +-- .../src/global2D/detection/ODHOGDetector.cpp | 10 +- .../src/global2D/training/ODHOGTrainer.cpp | 47 ++--- .../training/ODCADDetectTrainer3DGlobal.cpp | 4 +- .../src/local2D/ODImageLocalMatching.cpp | 81 ++++---- .../detection/ODCADRecognizer2DLocal.cpp | 11 +- .../ODCADRecogTrainerSnapshotBased.cpp | 175 +++++++----------- 22 files changed, 316 insertions(+), 380 deletions(-) diff --git a/common/include/od/common/pipeline/ODDetection.h b/common/include/od/common/pipeline/ODDetection.h index fea5cb13..54582362 100644 --- a/common/include/od/common/pipeline/ODDetection.h +++ b/common/include/od/common/pipeline/ODDetection.h @@ -141,8 +141,8 @@ namespace od const cv::Mat & getMetainfoImage() const; void setMetainfoImage(const cv::Mat & metainfo_image); - const pcl::PointCloud<pcl::PointXYZ>::Ptr & getMetainfoCluster() const; - void setMetainfoCluster(const pcl::PointCloud<pcl::PointXYZ>::Ptr & metainfo_cluster); + const shared_ptr<pcl::PointCloud<pcl::PointXYZ> > & getMetainfoCluster() const; + void setMetainfoCluster(const shared_ptr<pcl::PointCloud<pcl::PointXYZ> > & metainfo_cluster); void printSelf(); @@ -150,7 +150,7 @@ namespace od Eigen::Matrix3Xd orientation_; double scale_; cv::Mat metainfo_image_; - pcl::PointCloud<pcl::PointXYZ>::Ptr metainfo_cluster_; + shared_ptr<pcl::PointCloud<pcl::PointXYZ>> metainfo_cluster_; }; @@ -188,14 +188,14 @@ namespace od const cv::Mat & getMetainfoImage() const; void setMetainfoImage(const cv::Mat & metainfo_image_); - const pcl::PointCloud<pcl::PointXYZ>::Ptr & getMetainfoCluster() const; - void setMetainfoCluster(const pcl::PointCloud<pcl::PointXYZ>::Ptr & metainfo_cluster); + const shared_ptr<pcl::PointCloud<pcl::PointXYZ> > & getMetainfoCluster() const; + void setMetainfoCluster(const shared_ptr<pcl::PointCloud<pcl::PointXYZ> > & metainfo_cluster); protected: std::vector<shared_ptr<ODDetection>> detections_; cv::Mat metainfo_image_; - typename pcl::PointCloud<pcl::PointXYZ>::Ptr metainfo_cluster_; + shared_ptr<pcl::PointCloud<pcl::PointXYZ> > metainfo_cluster_; }; diff --git a/common/include/od/common/pipeline/ODDetector.h b/common/include/od/common/pipeline/ODDetector.h index efb36ef6..0adc97c8 100644 --- a/common/include/od/common/pipeline/ODDetector.h +++ b/common/include/od/common/pipeline/ODDetector.h @@ -106,14 +106,14 @@ namespace od * \param[in] scene An instance of 3D scene * \return A number of detections as an ODDetections instance. */ - virtual shared_ptr<ODDetections> detect(shared_ptr<ODScenePointCloud<PointT>> scene) = 0; + virtual shared_ptr<ODDetections> detect(shared_ptr<ODScenePointCloud<PointT> > scene) = 0; /** \brief Function for performing detection on an entire scene. * The purpose of this function is to detect an object in an entire scene. Thus, other than the type of detection we also have information about the location of the detection w.r.t. the scene. * \param[in] scene An instance of 3D scene * \return A number of detections as an ODDetections3D instance containing information about the detection and its 3D pose. */ - virtual shared_ptr<ODDetections3D> detectOmni(shared_ptr<ODScenePointCloud<PointT>> scene) = 0; + virtual shared_ptr<ODDetections3D> detectOmni(shared_ptr<ODScenePointCloud<PointT> > scene) = 0; }; /** \brief The detector of 2D scene performing a 'complete detection'. diff --git a/common/include/od/common/pipeline/ODObjectDetector.h b/common/include/od/common/pipeline/ODObjectDetector.h index d5162281..b6aff287 100644 --- a/common/include/od/common/pipeline/ODObjectDetector.h +++ b/common/include/od/common/pipeline/ODObjectDetector.h @@ -93,7 +93,7 @@ namespace od protected: std::string training_input_location_, trained_data_location_; - std::string TRAINED_DATA_ID_, TRAINED_LOCATION_DENTIFIER_; + std::string trained_data_id_, trained_location_identifier_; }; @@ -139,8 +139,7 @@ namespace od bool always_train_; bool trained_; std::string training_input_location_, training_data_location_; - - std::string TRAINED_DATA_EXT_, TRAINED_DATA_IDENTIFIER_; + std::string trained_data_ext_, trained_data_identifier_; }; } diff --git a/common/include/od/common/pipeline/ODTrainer.h b/common/include/od/common/pipeline/ODTrainer.h index 71a511c3..5a84b977 100644 --- a/common/include/od/common/pipeline/ODTrainer.h +++ b/common/include/od/common/pipeline/ODTrainer.h @@ -42,7 +42,7 @@ namespace od { public: - ODTrainer(const std::string & training_input_location = "", const std::string & training_data_location = "") : + ODTrainer(const std::string & training_input_location = std::string(""), const std::string & training_data_location = std::string("")) : ODDetectorCommon(training_data_location) { training_input_location_ = training_data_location; diff --git a/common/include/od/common/utils/ODFrameGenerator.h b/common/include/od/common/utils/ODFrameGenerator.h index 99c1e53a..c59f1274 100644 --- a/common/include/od/common/utils/ODFrameGenerator.h +++ b/common/include/od/common/utils/ODFrameGenerator.h @@ -38,7 +38,6 @@ namespace od protected: - int cameraID_; std::vector<std::string> file_list_; std::string video_read_path_; unsigned int curr_image_; diff --git a/common/src/pipeline/ODObjectDetector.cpp b/common/src/pipeline/ODObjectDetector.cpp index c98b3bbb..9f5ce999 100644 --- a/common/src/pipeline/ODObjectDetector.cpp +++ b/common/src/pipeline/ODObjectDetector.cpp @@ -35,9 +35,9 @@ namespace od { ODDetectorCommon::ODDetectorCommon(const std::string & trained_data_location) : trained_data_location_(trained_data_location) { std::string classname = typeid(this).name(); - TRAINED_DATA_ID_ = classname; + trained_data_id_ = classname; std::transform(classname.begin(), classname.end(), classname.begin(), ::toupper); - TRAINED_LOCATION_DENTIFIER_ = classname; + trained_location_identifier_ = classname; } std::string ODDetectorCommon::getTrainingInputLocation() const @@ -62,22 +62,22 @@ namespace od { std::string ODDetectorCommon::getSpecificTrainingDataLocation() { - return trained_data_location_ + "/" + "TD_" + TRAINED_LOCATION_DENTIFIER_; + return trained_data_location_ + "/" + "TD_" + trained_location_identifier_; } std::string ODDetectorCommon::getSpecificTrainingData() { - return getSpecificTrainingDataLocation() + "/" + TRAINED_DATA_ID_; + return getSpecificTrainingDataLocation() + "/" + trained_data_id_; } const std::string & ODDetectorCommon::getTrainedDataID() const { - return TRAINED_DATA_ID_; + return trained_data_id_; } void ODDetectorCommon::setTrainedDataID(const std::string & trainedDataID) { - ODDetectorCommon::TRAINED_DATA_ID_ = trainedDataID; + trained_data_id_ = trainedDataID; } @@ -123,7 +123,7 @@ namespace od { std::string ObjectDetector::getSpecificTrainingDataLocation() { - return training_data_location_ + "/" + "TD_" + TRAINED_DATA_IDENTIFIER_; + return training_data_location_ + "/" + "TD_" + trained_data_identifier_; } } \ No newline at end of file diff --git a/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp b/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp index f0b3a262..a766fe87 100644 --- a/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp +++ b/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp @@ -2,18 +2,16 @@ // Created by sarkar on 10.08.15. // #pragma once -#include "od/common/pipeline/ODDetector.h" -#include <pcl/pcl_macros.h> +#include "od/common/pipeline/ODDetector.h" #include <pcl/apps/3d_rec_framework/pipeline/global_nn_classifier.h> #include <pcl/apps/3d_rec_framework/pc_source/mesh_source.h> #include <pcl/apps/3d_rec_framework/feature_wrapper/global/vfh_estimator.h> #include <pcl/apps/3d_rec_framework/feature_wrapper/global/esf_estimator.h> #include <pcl/apps/3d_rec_framework/feature_wrapper/global/cvfh_estimator.h> #include <pcl/apps/3d_rec_framework/utils/metrics.h> -#include <pcl/visualization/pcl_visualizer.h> #include <pcl/apps/dominant_plane_segmentation.h> -#include <pcl/console/parse.h> + namespace od { namespace g3d @@ -24,27 +22,24 @@ namespace od { public: - ODCADDetector3DGlobal(const std::string & training_data_location = "", const std::string & training_input_location = "") : - ODDetector3D<PointT>(training_data_location), NN(2), desc_name_("esf") + ODCADDetector3DGlobal(const std::string & training_data_location = std::string(""), + const std::string & training_input_location = std::string("")) : + ODDetector3D<PointT>(training_data_location), NN_(2), desc_name_("esf") { - this->TRAINED_LOCATION_DENTIFIER_ = "GLOBAL3DVFH"; + this->trained_location_identifier_ = std::string("GLOBAL3DVFH"); this->training_input_location_ = training_input_location; } void init(); - shared_ptr<ODDetections> detect(shared_ptr<ODScenePointCloud<PointT> > scene); - - shared_ptr<ODDetections3D> detectOmni(shared_ptr<ODScenePointCloud<PointT> > scene); - int getNN() const { - return NN; + return NN_; } void setNN(int NN) { - ODCADDetector3DGlobal::NN = NN; + NN_ = NN; } const std::string & getDescName() const @@ -57,12 +52,14 @@ namespace od desc_name_ = desc_name; } - virtual shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene); - virtual shared_ptr<ODDetections> detect(shared_ptr<ODScene> scene); + shared_ptr<ODDetections> detect(shared_ptr<ODScene> scene); + shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene); + shared_ptr<ODDetections> detect(shared_ptr<ODScenePointCloud<PointT> > scene); + shared_ptr<ODDetections3D> detectOmni(shared_ptr<ODScenePointCloud<PointT> > scene); protected: - int NN; + int NN_; std::string desc_name_; shared_ptr<pcl::rec_3d_framework::GlobalClassifier<pcl::PointXYZ> > global_; @@ -117,7 +114,7 @@ namespace od global->setDataSource(cast_source); global->setTrainingDir(training_dir_specific); global->setDescriptorName(desc_name_); - global->setNN(NN); + global->setNN(NN_); global->setFeatureEstimator(cast_estimator); global->initialize(false); global_ = global; @@ -138,7 +135,7 @@ namespace od global->setTrainingDir(training_dir_specific); global->setDescriptorName(desc_name_); global->setFeatureEstimator(cast_estimator); - global->setNN(NN); + global->setNN(NN_); global->initialize(false); global_ = global; } else if(desc_name_.compare("esf") == 0) @@ -156,7 +153,7 @@ namespace od global->setTrainingDir(training_dir_specific); global->setDescriptorName(desc_name_); global->setFeatureEstimator(cast_estimator); - global->setNN(NN); + global->setNN(NN_); global->initialize(false); global_ = global; } else @@ -254,6 +251,5 @@ namespace od return detections; } - } } diff --git a/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp b/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp index 143df954..0a412f61 100644 --- a/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp +++ b/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp @@ -27,12 +27,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 06.08.15. // #pragma once -#include "od/common/pipeline/ODDetector.h" #include "od/detectors/global2D/detection/ODCascadeDetector.h" #include "od/detectors/global2D/detection/ODHOGDetector.h" -#include "od/detectors/global2D/ODFaceRecognizer.h" -#include "od/detectors/local2D/detection/ODCADRecognizer2DLocal.h" -//3D detectors #include "od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp" namespace od @@ -50,8 +46,8 @@ namespace od shared_ptr<ODDetections> detect(shared_ptr<ODScenePointCloud<PointT> > scene); shared_ptr<ODDetections3D> detectOmni(shared_ptr<ODScenePointCloud<PointT> > scene); - virtual shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene); - virtual shared_ptr<ODDetections> detect(shared_ptr<ODScene> scene); + shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene); + shared_ptr<ODDetections> detect(shared_ptr<ODScene> scene); void init(); @@ -62,46 +58,6 @@ namespace od }; - template<typename PointT> - class ODDetectorMultiAlgo : public ODDetector - { - - public: - - ODDetectorMultiAlgo(const std::string & training_data_location_) : ODDetector(training_data_location_){} - - shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage > scene) ; - shared_ptr<ODDetections2D> detectOmni(shared_ptr<ODSceneImage > scene); - - shared_ptr<ODDetections> detect(shared_ptr<ODScenePointCloud<PointT> > scene); - shared_ptr<ODDetections3D> detectOmni(shared_ptr<ODScenePointCloud<PointT> > scene); - - virtual shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene); - virtual shared_ptr<ODDetections> detect(shared_ptr<ODScene> scene); - - void init(); - - private: - - std::vector<shared_ptr<ODDetector2D> > detectors_2d_; - std::vector<shared_ptr<ODDetector3D<PointT> > > detectors_3d_; - - }; - - template<typename PointT> - shared_ptr<ODDetections> ODDetectorMultiAlgo<PointT>::detectOmni(shared_ptr<ODScene> scene) - { - std::cout << "not implemented, use with shared_ptr<ODSceneImage> or shared_ptr<ODScenePointCloud<PointT>>" <<std::endl; - return nullptr; - } - - template<typename PointT> - shared_ptr<ODDetections> ODDetectorMultiAlgo<PointT>::detect(shared_ptr<ODScene> scene) - { - std::cout << "not implemented, use with shared_ptr<ODSceneImage> or shared_ptr<ODScenePointCloud<PointT>>" <<std::endl; - return nullptr; - } - template<typename PointT> shared_ptr<ODDetections> ODDetectorMultiAlgo2D<PointT>::detectOmni(shared_ptr<ODScene> scene) { @@ -118,13 +74,13 @@ namespace od //BASED ON 2D SCENE template<typename PointT> - shared_ptr<ODDetections> ODDetectorMultiAlgo2D<PointT>::detect(shared_ptr<ODSceneImage > scene) + shared_ptr<ODDetections> ODDetectorMultiAlgo2D<PointT>::detect(shared_ptr<ODSceneImage> scene) { shared_ptr<ODDetections> detections_all = make_shared<ODDetections>(); - for (size_t i = 0; i < detectors_2d_.size(); ++i) + + for(auto & d : detectors_2d_) { - shared_ptr<ODDetections> detections_individual = detectors_2d_[i]->detect(scene); - detections_all->append(detections_individual); + detections_all->append(d->detect(scene)); } return detections_all; @@ -134,10 +90,9 @@ namespace od shared_ptr<ODDetections2D> ODDetectorMultiAlgo2D<PointT>::detectOmni(shared_ptr<ODSceneImage > scene) { shared_ptr<ODDetections2D> detections_all = make_shared<ODDetections2D>(); - for (size_t i = 0; i < detectors_2d_.size(); ++i) + for(auto & d : detectors_2d_) { - shared_ptr<ODDetections2D> detections_individual = detectors_2d_[i]->detectOmni(scene); - detections_all->append(detections_individual); + detections_all->append(d->detectOmni(scene)); } return detections_all; @@ -152,21 +107,63 @@ namespace od detectors_2d_.push_back(make_shared<g2d::ODHOGDetector>(trained_data_location_)); // detectors.push_back(new ODCADRecognizer2DLocal(trained_data_location_)); - for(size_t i = 0; i < detectors_2d_.size(); ++i) + for(auto & d : detectors_2d_) { - detectors_2d_[i]->init(); + d->init(); } } + + template<typename PointT> + class ODDetectorMultiAlgo : public ODDetector + { + + public: + + ODDetectorMultiAlgo(const std::string & training_data_location_) : ODDetector(training_data_location_){} + + shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene) ; + shared_ptr<ODDetections2D> detectOmni(shared_ptr<ODSceneImage> scene); + + shared_ptr<ODDetections> detect(shared_ptr<ODScenePointCloud<PointT> > scene); + shared_ptr<ODDetections3D> detectOmni(shared_ptr<ODScenePointCloud<PointT> > scene); + + shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene); + shared_ptr<ODDetections> detect(shared_ptr<ODScene> scene); + + void init(); + + private: + + std::vector<shared_ptr<ODDetector2D> > detectors_2d_; + std::vector<shared_ptr<ODDetector3D<PointT> > > detectors_3d_; + + }; + + template<typename PointT> + shared_ptr<ODDetections> ODDetectorMultiAlgo<PointT>::detectOmni(shared_ptr<ODScene> scene) + { + std::cout << "not implemented, use with shared_ptr<ODSceneImage> or shared_ptr<ODScenePointCloud<PointT>>" <<std::endl; + return nullptr; + } + + template<typename PointT> + shared_ptr<ODDetections> ODDetectorMultiAlgo<PointT>::detect(shared_ptr<ODScene> scene) + { + std::cout << "not implemented, use with shared_ptr<ODSceneImage> or shared_ptr<ODScenePointCloud<PointT>>" <<std::endl; + return nullptr; + } + + /////############BASED ON 3D SCENE##################### template<typename PointT> void ODDetectorMultiAlgo<PointT>::init() { //3D detectors_3d_.push_back( make_shared<g3d::ODCADDetector3DGlobal<PointT> >(trained_data_location_, training_input_location_)); - for(size_t i = 0; i < detectors_3d_.size(); ++i) + for(auto & d : detectors_3d_) { - detectors_3d_[i]->init(); + d->init(); } } @@ -174,10 +171,9 @@ namespace od shared_ptr<ODDetections> ODDetectorMultiAlgo<PointT>::detect(shared_ptr<ODScenePointCloud<PointT> > scene) { shared_ptr<ODDetections> detections_all = make_shared<ODDetections>(); - for(size_t i = 0; i < detectors_3d_.size(); ++i) + for(auto & d : detectors_3d_) { - shared_ptr<ODDetections> detections_individual = detectors_3d_[i]->detect(scene); - detections_all->append(detections_individual); + detections_all->append(d->detect(scene)); } return detections_all; @@ -187,10 +183,9 @@ namespace od shared_ptr<ODDetections3D> ODDetectorMultiAlgo<PointT>::detectOmni(shared_ptr<ODScenePointCloud<PointT> > scene) { shared_ptr<ODDetections3D> detections_all = make_shared<ODDetections3D>(); - for(size_t i = 0; i < detectors_3d_.size(); i++) + for(auto & d : detectors_3d_) { - shared_ptr<ODDetections3D> detections_individual = detectors_3d_[i]->detectOmni(scene); - detections_all->append(detections_individual); + detections_all->append(d->detectOmni(scene)); } return detections_all; diff --git a/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h b/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h index ed196582..b6d6049b 100644 --- a/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h +++ b/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h @@ -29,12 +29,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #pragma once #include <opencv2/objdetect.hpp> #include <opencv2/opencv.hpp> - -#include "od/common/pipeline/ODTrainer.h" -//#include "od/common/utils/ODUtils.h" #include <fstream> - -//binding class for svmlight +#include "od/common/pipeline/ODTrainer.h" #include "od/common/bindings/svmlight.h" namespace od diff --git a/detectors/include/od/detectors/global3D/ODPointCloudGlobalMatching.h b/detectors/include/od/detectors/global3D/ODPointCloudGlobalMatching.h index f014b922..3704a37d 100644 --- a/detectors/include/od/detectors/global3D/ODPointCloudGlobalMatching.h +++ b/detectors/include/od/detectors/global3D/ODPointCloudGlobalMatching.h @@ -45,7 +45,9 @@ namespace od { void init(){} + int train(); + int detect(shared_ptr<ODScene> scene, std::vector<shared_ptr<ODDetection>> & detections); protected: diff --git a/detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h b/detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h index 5ae5630e..928d15a1 100644 --- a/detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h +++ b/detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h @@ -69,14 +69,14 @@ namespace od { public: - ODCADDetectTrainer3DGlobal(const std::string & training_input_location_ = "", const std::string & training_data_location_ = ""); + ODCADDetectTrainer3DGlobal(const std::string & training_input_location_ = std::string(""), + const std::string & training_data_location_ = std::string("")); int train(); void init() {}; const std::string & getDescName() const; - void setDescName(const std::string & desc_name); protected: diff --git a/detectors/include/od/detectors/local2D/ODImageLocalMatching.h b/detectors/include/od/detectors/local2D/ODImageLocalMatching.h index 98658f4f..08f51512 100644 --- a/detectors/include/od/detectors/local2D/ODImageLocalMatching.h +++ b/detectors/include/od/detectors/local2D/ODImageLocalMatching.h @@ -91,7 +91,7 @@ namespace od int train(); - int detect(shared_ptr<ODScene> scene, std::vector<shared_ptr<ODDetection> > detections); + int detect(shared_ptr<ODScene> scene, const std::vector<shared_ptr<ODDetection> > & detections); protected: diff --git a/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h b/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h index 84acc442..b4afbc34 100644 --- a/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h +++ b/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h @@ -97,11 +97,11 @@ namespace od void init(); - virtual shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene); - virtual shared_ptr<ODDetections3D> detectOmni(shared_ptr<ODSceneImage> scene); + shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene); + shared_ptr<ODDetections3D> detectOmni(shared_ptr<ODSceneImage> scene); - virtual shared_ptr<ODDetections> detect(shared_ptr<ODScene> scene); - virtual shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene); + shared_ptr<ODDetections> detect(shared_ptr<ODScene> scene); + shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene); protected: diff --git a/detectors/include/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h b/detectors/include/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h index b29866d6..4e4a1eab 100644 --- a/detectors/include/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h +++ b/detectors/include/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h @@ -28,69 +28,28 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 17.03.15. // #pragma once -#include "od/common/pipeline/ODTrainer.h" -#include "od/common/utils/ODUtils.h" #include "od/detectors/local2D/ODImageLocalMatching.h" +#include "pugixml.hpp" -#include <string> -#include <stdlib.h> - -#include <vtkSmartPointer.h> #include <vtkSphereSource.h> -#include <vtkPolyData.h> -#include <vtkPolyDataMapper.h> -#include <vtkActor.h> #include <vtkCamera.h> #include <vtkMatrix4x4.h> -#include <vtkRendererCollection.h> #include <vtkCommand.h> -#include <vtkRenderer.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> #include <vtkOBJReader.h> -#include <vtkUnstructuredGrid.h> -#include <vtkCell.h> -#include <vtkCellArray.h> -#include <vtkIdList.h> -#include <vtkUnsignedCharArray.h> -#include <vtkPointData.h> -#include <string> #include <vtkRendererCollection.h> -#include <vtkCellArray.h> #include <vtkInteractorStyleTrackballCamera.h> #include <vtkObjectFactory.h> -#include <vtkPlaneSource.h> -#include <vtkPoints.h> -#include <vtkPolyData.h> #include <vtkPolyDataMapper.h> #include <vtkPropPicker.h> -#include <vtkPointPicker.h> - +#include <vtkJPEGWriter.h> #include <vtkImageReader2Factory.h> #include <vtkImageReader.h> -#include <vtkTexture.h> -#include <vtkAxesActor.h> #include <vtkWindowToImageFilter.h> -#include <vtkPNGWriter.h> - -#include <sstream> -#include <opencv2/core/types.hpp> -#include <map> - -#include <boost/filesystem.hpp> - -//opencv -#include <opencv2/imgcodecs.hpp> -#include <opencv2/core/core.hpp> -#include <opencv2/core/utility.hpp> -#include <opencv2/highgui/highgui.hpp> -#include <opencv2/imgproc/imgproc.hpp> -#include <opencv2/calib3d/calib3d.hpp> -#include <opencv2/video/tracking.hpp> + #include <opencv2/xfeatures2d.hpp> -#include <vtkJPEGWriter.h> -#include "pugixml.hpp" #define VIEW_ANGLE 30 #define NO_SNAPSHOTS 30 @@ -110,7 +69,8 @@ namespace od public: - ODCADRecogTrainerSnapshotBased(const std::string & training_input_location_ = "", const std::string & training_data_location_ = "") : + ODCADRecogTrainerSnapshotBased(const std::string & training_input_location_ = std::string(""), + const std::string & training_data_location_ = std::string("")) : ODImageLocalMatchingTrainer(training_input_location_, training_data_location_){} int train(); @@ -123,8 +83,60 @@ namespace od float view_angle_; int no_snapshot_per_ring_; + }; + + class vtkTimerCallbackSnapshot : public vtkCommand + { + public: + + static vtkTimerCallbackSnapshot * New() + { + vtkTimerCallbackSnapshot * cb = new vtkTimerCallbackSnapshot; + cb->snap_count_ = 0; + cb->snap_mode = true; + cb->feature_type = std::string("SIFT"); + return cb; + } + + virtual void Execute(vtkObject * caller, unsigned long eventId, void * vtkNotUsed(callData)); + + private: + + int snap_count_; + + public: + + std::string takeSnapshot(vtkRenderWindow * render_window, int snap_no); + + void write_pairs(const std::vector<std::pair<cv::Point3f, cv::KeyPoint> > & pairs, const cv::Mat & descriptors, const std::string & file_name); + + void write_pairs_xml(const std::vector<std::pair<cv::Point3f, cv::KeyPoint> > & pairs, const cv::Mat & descriptors, const std::string & file_name); + + void process_image(const std::string & img_name, vtkRenderer * ren, vtkActor * actor, int ino); + + void process(vtkRenderWindowInteractor * iren, vtkActor * actor, vtkRenderer * renderer, int ino); + + //some local variables used + + struct fcomp3d_euclidian + { + bool operator()(const cv::Point3f & lhs, const cv::Point3f & rhs) const + { return lhs.x < rhs.x; } + }; + + std::vector<std::pair<cv::Point3f, cv::KeyPoint> > pairs_3d_2d; + cv::Mat common_descriptors; + std::map<cv::Point3f, cv::KeyPoint, fcomp3d_euclidian> map_3d_2d; + std::string feature_type; + std::string input_file, input_dir, output_dir, output_extension; + + vtkActor * actor; + vtkRenderer * renderer; + bool snap_mode; }; + + } } diff --git a/detectors/src/global2D/ODFaceRecognizer.cpp b/detectors/src/global2D/ODFaceRecognizer.cpp index 5ad257b0..653b9f7e 100644 --- a/detectors/src/global2D/ODFaceRecognizer.cpp +++ b/detectors/src/global2D/ODFaceRecognizer.cpp @@ -34,20 +34,14 @@ namespace od namespace g2d { - ODFaceRecognizer::ODFaceRecognizer(FaceRecogType recog_type, int num_components, double threshold) - : recog_type_(recog_type), num_components_(num_components), threshold_(threshold), im_height_(0), im_width_(0) + ODFaceRecognizer::ODFaceRecognizer(FaceRecogType recog_type, int num_components, double threshold): + recog_type_(recog_type), num_components_(num_components), threshold_(threshold), + im_height_(0), im_width_(0) { - TRAINED_DATA_IDENTIFIER_ = "FACERECOG"; - TRAINED_DATA_EXT_ = "facerec.xml"; + trained_data_identifier_ = std::string("FACERECOG"); + trained_data_ext_ = std::string("facerec.xml"); } - shared_ptr<ODDetections> ODFaceRecognizer::detectOmni(shared_ptr<ODSceneImage> scene) - { - std::cout << "not implemented, use detect()" <<std::endl; - return nullptr; - }; - - void ODFaceRecognizer::init() { switch(recog_type_) @@ -76,7 +70,7 @@ namespace od //get models in the directory std::vector<std::string> files; - fileutils::getFilesInDirectoryRec(getSpecificTrainingDataLocation(), TRAINED_DATA_EXT_, files); + fileutils::getFilesInDirectoryRec(getSpecificTrainingDataLocation(), trained_data_ext_, files); if(files.size() == 0) { @@ -104,7 +98,7 @@ namespace od cv_recognizer_->train(images, labels); fileutils::createTrainingDir(getSpecificTrainingDataLocation()); - cv_recognizer_->save(getSpecificTrainingDataLocation() + "/trained." + TRAINED_DATA_EXT_); + cv_recognizer_->save(getSpecificTrainingDataLocation() + "/trained." + trained_data_ext_); trained_ = true; //the training set has atleast one image @@ -132,6 +126,13 @@ namespace od return -1; } + shared_ptr<ODDetections> ODFaceRecognizer::detectOmni(shared_ptr<ODSceneImage> scene) + { + std::cout << "not implemented, use detect()" <<std::endl; + return nullptr; + }; + + shared_ptr<ODDetections> ODFaceRecognizer::detect(shared_ptr<ODSceneImage> scene) { cv::Mat face_edited; @@ -150,6 +151,7 @@ namespace od shared_ptr<ODDetection2D> detection = make_shared<ODDetection2D>(ODDetection::OD_DETECTION_CLASS, std::to_string(label), confidence); shared_ptr<ODDetections2D> detections = make_shared<ODDetections2D>(); detections->push_back(detection); + return detections; } diff --git a/detectors/src/global2D/detection/ODCascadeDetector.cpp b/detectors/src/global2D/detection/ODCascadeDetector.cpp index 719bc759..fcfe9573 100644 --- a/detectors/src/global2D/detection/ODCascadeDetector.cpp +++ b/detectors/src/global2D/detection/ODCascadeDetector.cpp @@ -36,12 +36,12 @@ namespace od { ODCascadeDetector::ODCascadeDetector(const std::string & trained_data_location, double scale_factor, int min_neighbors, int flags, - const cv::Size & min_size, const cv::Size & max_size) - : ODDetector2D(trained_data_location), scale_factor_(scale_factor), min_neighbors_(min_neighbors), - min_size_(min_size), max_size_(max_size) + const cv::Size & min_size, const cv::Size & max_size): + ODDetector2D(trained_data_location), scale_factor_(scale_factor), min_neighbors_(min_neighbors), + min_size_(min_size), max_size_(max_size) { - TRAINED_LOCATION_DENTIFIER_ = "CASCADE"; - TRAINED_DATA_ID_ = "cascade.xml"; + trained_location_identifier_ = std::string("CASCADE"); + trained_data_id_ = std::string("cascade.xml"); meta_info_ = true; } @@ -53,7 +53,7 @@ namespace od void ODCascadeDetector::init() { - haar_cascade_ = make_shared<cv::CascadeClassifier>(fileutils::getFirstFile(getSpecificTrainingDataLocation(), TRAINED_DATA_ID_)); + haar_cascade_ = make_shared<cv::CascadeClassifier>(fileutils::getFirstFile(getSpecificTrainingDataLocation(), trained_data_id_)); } shared_ptr<ODDetections2D> ODCascadeDetector::detectOmni(shared_ptr<ODSceneImage> scene) @@ -67,19 +67,16 @@ namespace od //always create detections shared_ptr<ODDetections2D> detections = make_shared<ODDetections2D>(); cv::Mat viz = scene->getCVImage().clone(); - cv::Rect face_i; - for(size_t i = 0; i < faces.size(); ++i) + for(auto & face : faces) { // Process face by face: - face_i = faces[i]; - shared_ptr<ODDetection2D> detection2D = make_shared<ODDetection2D>(ODDetection::OD_DETECTION_CLASS, "FACE", 1); - detection2D->setBoundingBox(face_i); + detection2D->setBoundingBox(face); detections->push_back(detection2D); if(meta_info_) { - cv::rectangle(viz, face_i, CV_RGB(0, 255, 0), 1); + cv::rectangle(viz, face, CV_RGB(0, 255, 0), 1); } } detections->setMetainfoImage(viz); diff --git a/detectors/src/global2D/detection/ODHOGDetector.cpp b/detectors/src/global2D/detection/ODHOGDetector.cpp index 98c45636..1c3f6b1c 100644 --- a/detectors/src/global2D/detection/ODHOGDetector.cpp +++ b/detectors/src/global2D/detection/ODHOGDetector.cpp @@ -40,12 +40,12 @@ namespace od cell_size_(cell_size), hit_threshold_(hit_threshold), hog_(win_size, block_size, block_stride, cell_size, 9, 1, -1, cv::HOGDescriptor::L2Hys, 0.2, false, cv::HOGDescriptor::DEFAULT_NLEVELS) { - TRAINED_LOCATION_DENTIFIER_ = "HOG"; - TRAINED_DATA_ID_ = "hog.xml"; + trained_location_identifier_ = std::string("HOG"); + trained_data_id_ = std::string("hog.xml"); meta_info_ = true; svm_type_ = OD_DEFAULT_PEOPLE; - if (trained_data_location_ != "") + if(trained_data_location_ != std::string("")) svm_type_ = OD_FILE; } @@ -67,7 +67,7 @@ namespace od //hog_.save(getSpecificTrainingDataLocation() + "/daimlerpeople." + TRAINED_DATA_EXT_); break; case OD_FILE: - std::string hogfile = fileutils::getFirstFile(getSpecificTrainingDataLocation(), TRAINED_DATA_ID_); + std::string hogfile = fileutils::getFirstFile(getSpecificTrainingDataLocation(), trained_data_id_); load(hogfile); std::cout << "HOG TYPE: Custom HOG features loaded from: " << hogfile << std::endl; break; @@ -95,7 +95,7 @@ namespace od //always create a detection shared_ptr<ODDetections2D> detections = make_shared<ODDetections2D>(); - std::vector<cv::Rect> found, found_filtered; + std::vector<cv::Rect> found; hog_.detectMultiScale(scene->getCVImage(), found, hit_threshold_, cv::Size(8, 8), cv::Size(32, 32), 1.05, 2); cv::Mat viz = scene->getCVImage().clone(); diff --git a/detectors/src/global2D/training/ODHOGTrainer.cpp b/detectors/src/global2D/training/ODHOGTrainer.cpp index 48d9ce7a..55e81b9a 100644 --- a/detectors/src/global2D/training/ODHOGTrainer.cpp +++ b/detectors/src/global2D/training/ODHOGTrainer.cpp @@ -41,8 +41,8 @@ namespace od block_stride_(block_stride), cell_size_(cell_size), hog_(win_size, block_size, block_stride, cell_size, 9) { - TRAINED_LOCATION_DENTIFIER_ = "HOG"; - TRAINED_DATA_ID_ = "hog.xml"; + trained_location_identifier_ = std::string("HOG"); + trained_data_id_ = std::string("hog.xml"); //algo parameter init training_padding_ = cv::Size(0, 0); @@ -51,20 +51,20 @@ namespace od win_stride_ = cv::Size(); train_hard_negative_ = false; - if(trained_data_location_ != "") + if(trained_data_location_ != std::string("")) { - pos_samples_dir_ = training_input_location_ + "/pos"; - neg_samples_dir_ = training_input_location_ + "/neg"; + pos_samples_dir_ = training_input_location_ + std::string("/pos"); + neg_samples_dir_ = training_input_location_ + std::string("/neg"); } //internal data fileutils::createTrainingDir(getSpecificTrainingDataLocation()); - features_file_ = getSpecificTrainingDataLocation() + "/features.dat"; - svm_model_file_ = getSpecificTrainingDataLocation() + "/svmlightmodel.dat"; - svm_model_hard_ = getSpecificTrainingDataLocation() + "/svmlightmodelhard.dat"; - descriptor_vector_file_ = getSpecificTrainingDataLocation() + "/descriptorvector.dat"; - descriptor_vector_file_ = getSpecificTrainingDataLocation() + "/descriptorvectorHard.dat"; + features_file_ = getSpecificTrainingDataLocation() + std::string("/features.dat"); + svm_model_file_ = getSpecificTrainingDataLocation() + std::string("/svmlightmodel.dat"); + svm_model_hard_ = getSpecificTrainingDataLocation() + std::string("/svmlightmodelhard.dat"); + descriptor_vector_file_ = getSpecificTrainingDataLocation() + std::string("/descriptorvector.dat"); + descriptor_vector_file_ = getSpecificTrainingDataLocation() + std::string("/descriptorvectorHard.dat"); } @@ -88,7 +88,7 @@ namespace od } file << descriptor_vector[feature] << " "; } - printf("\n"); + std::cout << std::endl; file << std::endl; file.flush(); file.close(); @@ -98,8 +98,7 @@ namespace od void ODHOGTrainer::calculateFeaturesFromInput(const std::string & image_file_name, std::vector<float> & feature_vector, cv::HOGDescriptor & hog) { - cv::Mat image_data_orig, image_data; - image_data_orig = cv::imread(image_file_name, 0); + cv::Mat image_data_orig = cv::imread(image_file_name, 0); if(image_data_orig.empty()) { @@ -113,7 +112,6 @@ namespace od hog.compute(image_data_orig, feature_vector, win_stride_, training_padding_, locations); //cout << "Desc size :" << featureVector.size(); //cout << ": Expected size :" << hog.getDescriptorSize() << endl; - image_data_orig.release(); // Release the image again after features are extracted } void ODHOGTrainer::detectTrainingSetTest(const cv::HOGDescriptor & hog, double hit_threshold, const std::vector<std::string> & pos_file_names, @@ -123,6 +121,7 @@ namespace od unsigned int true_negatives = 0; unsigned int false_positives = 0; unsigned int false_negatives = 0; + std::vector<cv::Point> found_detection; cv::Mat image_data; // Walk over positive training samples, generate images and detect @@ -216,14 +215,11 @@ namespace od int counter = 0; for(auto & nf : neg_file_names) { - const cv::Mat image_data = cv::imread(nf, 0); - std::vector<cv::Rect> found_locations; hog.detectMultiScale(image_data, found_locations, hit_threshold); - for(size_t i = 0; i < found_locations.size(); ++i) { counter++; @@ -261,14 +257,14 @@ namespace od { SVMlight * svmlight= SVMlight::getInstance(); //training takes featurefile as input, produces hitthreshold and vector as output - std::cout << "Calling " << svmlight->getSVMName() << std::endl; + std::cout << "Calling " << svmlight->getSVMName() << std::endl; svmlight->read_problem(const_cast<char *> (features_file_.c_str())); svmlight->train(); // Call the core libsvm training procedure + std::cout << "Training done, saving model file!"<< std::endl; svmlight->saveModelToFile(svm_model_file); std::cout << "Generating representative single HOG feature vector using svmlight!" << std::endl; - descriptor_vector.resize(0); // Generate a single detecting feature vector (v1 | b) from the trained support vectors, for use e.g. with the HOG algorithm svmlight->getSingleDetectingVector(descriptor_vector); @@ -285,6 +281,7 @@ namespace od std::vector<std::string> positive_training_images; std::vector<std::string> negative_training_images; std::vector<std::string> valid_extensions; + valid_extensions.push_back(".jpg"); valid_extensions.push_back(".png"); valid_extensions.push_back(".ppm"); @@ -297,11 +294,10 @@ namespace od std::cout << "No of positive Training Files: " << pos_size << std::endl; std::cout << "No of neg Training Files: "<< neg_size << std::endl; - if(pos_size + neg_size == 0) { std::cout << "No training sample files found, nothing to do!" << std::endl; - return EXIT_SUCCESS; + return 0; } /// @WARNING: This is really important, some libraries (e.g. ROS) seems to set the system locale @@ -353,7 +349,7 @@ namespace od } else { std::cout << "Error opening file " << features_file_; - return EXIT_FAILURE; + return -1; } @@ -378,12 +374,11 @@ namespace od hog_.setSVMDetector(descriptor_vector); std::cout << "Testing training phase using training set as test set after HARD EXAMPLES (just to check if training is ok - no detection quality conclusion with this!)" << std::endl; detectTrainingSetTest(hog_, hit_threshold_, positive_training_images, negative_training_images); - } - save(getSpecificTrainingDataLocation() + "/odtrained." + TRAINED_DATA_ID_); + save(getSpecificTrainingDataLocation() + "/odtrained." + trained_data_id_); - return EXIT_SUCCESS; + return 0; } @@ -395,7 +390,6 @@ namespace od file.open(file_name.c_str(), std::ios::in); if(file.good() && file.is_open()) { - double d; while(file >> d) { @@ -407,7 +401,6 @@ namespace od void ODHOGTrainer::save(const std::string & file_name) { - cv::FileStorage fs(file_name, cv::FileStorage::WRITE); fs << "hitThreshold" << hit_threshold_; hog_.write(fs, cv::FileStorage::getDefaultObjectName(file_name)); diff --git a/detectors/src/global3D/training/ODCADDetectTrainer3DGlobal.cpp b/detectors/src/global3D/training/ODCADDetectTrainer3DGlobal.cpp index 48da14ef..215dea6d 100644 --- a/detectors/src/global3D/training/ODCADDetectTrainer3DGlobal.cpp +++ b/detectors/src/global3D/training/ODCADDetectTrainer3DGlobal.cpp @@ -36,8 +36,8 @@ namespace od { ODCADDetectTrainer3DGlobal::ODCADDetectTrainer3DGlobal(const std::string & training_input_location_, const std::string & training_data_location_): ODTrainer(training_input_location_, training_data_location_) { - desc_name_ = "esf"; - TRAINED_LOCATION_DENTIFIER_ = "GLOBAL3DVFH"; + desc_name_ = std::string("esf"); + trained_location_identifier_ = std::string("GLOBAL3DVFH"); } diff --git a/detectors/src/local2D/ODImageLocalMatching.cpp b/detectors/src/local2D/ODImageLocalMatching.cpp index 60660086..1c477d8a 100644 --- a/detectors/src/local2D/ODImageLocalMatching.cpp +++ b/detectors/src/local2D/ODImageLocalMatching.cpp @@ -36,56 +36,55 @@ namespace od namespace l2d { - ODImageLocalMatchingTrainer::ODImageLocalMatchingTrainer(const std::string & training_input_location_, - const std::string & training_data_location_) : - ODTrainer(training_input_location_, training_data_location_) - { - TRAINED_LOCATION_DENTIFIER_ = "FEATCORR"; - TRAINED_DATA_ID_ = "corr.xml"; - } - - ODImageLocalMatchingDetector::ODImageLocalMatchingDetector(const std::string & training_data_location_) : - ODDetector2DComplete(training_data_location_) + ODImageLocalMatchingTrainer::ODImageLocalMatchingTrainer(const std::string & training_input_location, + const std::string & training_data_location) : + ODTrainer(training_input_location, training_data_location) { - TRAINED_LOCATION_DENTIFIER_ = "FEATCORR"; - TRAINED_DATA_ID_ = ".xml"; + trained_location_identifier_ = std::string("FEATCORR"); + trained_data_id_ = std::string("corr.xml"); } + ODImageLocalMatchingDetector::ODImageLocalMatchingDetector(const std::string & training_data_location) : + ODDetector2DComplete(training_data_location) + { + trained_location_identifier_ = std::string("FEATCORR"); + trained_data_id_ = std::string(".xml"); + } - shared_ptr<ODImageLocalMatchingTrainer> ODImageLocalMatching::getTrainer() const - { - return trainer_; - } + shared_ptr<ODImageLocalMatchingTrainer> ODImageLocalMatching::getTrainer() const + { + return trainer_; + } - void ODImageLocalMatching::setTrainer(shared_ptr<ODImageLocalMatchingTrainer> trainer_) - { - ODImageLocalMatching::trainer_ = trainer_; - } + void ODImageLocalMatching::setTrainer(shared_ptr<ODImageLocalMatchingTrainer> trainer) + { + trainer_ = trainer; + } - shared_ptr<ODImageLocalMatchingDetector> ODImageLocalMatching::getDetector() const - { - return detector_; - } + shared_ptr<ODImageLocalMatchingDetector> ODImageLocalMatching::getDetector() const + { + return detector_; + } - void ODImageLocalMatching:: setDetector(shared_ptr<ODImageLocalMatchingDetector> detector_) - { - ODImageLocalMatching::detector_ = detector_; - } + void ODImageLocalMatching:: setDetector(shared_ptr<ODImageLocalMatchingDetector> detector) + { + detector_ = detector; + } - ODImageLocalMatching::ODImageLocalMatching() - { - TRAINED_DATA_EXT_ = "corr.xml"; - } + ODImageLocalMatching::ODImageLocalMatching() + { + trained_data_ext_ = std::string("corr.xml"); + } - int ODImageLocalMatching::train() - { - return trainer_->train(); - } + int ODImageLocalMatching::train() + { + return trainer_->train(); + } - int ODImageLocalMatching::detect(shared_ptr<ODScene> scene, std::vector<shared_ptr<ODDetection> > detections) - { - //detector_->detect(scene, detections); - return 1; - } + int ODImageLocalMatching::detect(shared_ptr<ODScene> scene, const std::vector<shared_ptr<ODDetection> > & detections) + { + //detector_->detect(scene, detections); + return 1; + } } } \ No newline at end of file diff --git a/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp b/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp index 54e03f47..c5ceaaee 100644 --- a/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp +++ b/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp @@ -40,7 +40,7 @@ namespace od { meta_info_ = true; - camera_intrinsic_file_ = "Data/out_camera_data_lion_old.yml"; // mesh + camera_intrinsic_file_ = std::string("Data/out_camera_data_lion_old.yml"); // mesh red_ = cv::Scalar(0, 0, 255); green_ = cv::Scalar(0, 255, 0); @@ -60,7 +60,7 @@ namespace od min_inliers_ = 30; // Kalman threshold updating pnp_method_ = cv::SOLVEPNP_EPNP; - f_type_default_ = "SIFT"; + f_type_default_ = std::string("SIFT"); feature_detector_ = make_shared<ODFeatureDetector2D>(f_type_default_, use_gpu_); } @@ -241,7 +241,7 @@ namespace od pnp_detection_ = PnPProblem(cam_man, dist_coeff); // get all trained models - fileutils::getFilesInDirectoryRec(getSpecificTrainingDataLocation(), TRAINED_DATA_ID_, model_names_); + fileutils::getFilesInDirectoryRec(getSpecificTrainingDataLocation(), trained_data_id_, model_names_); for(size_t i = 0; i < model_names_.size(); ++i) { @@ -303,24 +303,19 @@ namespace od return false; std::vector<cv::Point3f> list_points3d_model_match; // container for the model 3D coordinates found in the scene - std::vector<cv::KeyPoint> list_keypoints_model_match; // container for the model 2D coordinates in the textured image of the corresponding 3D coordinates std::vector<cv::Point2f> list_points2d_scene_match; // container for the model 2D coordinates found in the scene for(size_t match_index = 0; match_index < good_matches.size(); ++match_index) { cv::Point3f point3d_model = list_points3d_model[good_matches[match_index].trainIdx]; // 3D point from model - cv::KeyPoint kp_model = list_keypoints_model[good_matches[match_index].trainIdx]; // 2D point from model cv::Point2f point2d_scene = keypoints_scene[good_matches[match_index].queryIdx].pt; // 2D point from the scene list_points3d_model_match.push_back(point3d_model); // add 3D point - list_keypoints_model_match.push_back(kp_model); list_points2d_scene_match.push_back(point2d_scene); // add 2D point } cv::Mat inliers_idx; - std::vector<cv::Point2f> list_points2d_inliers; - // -- Step 3: Estimate the pose using RANSAC approach pnp_detection_.estimatePoseRANSAC(list_points3d_model_match, list_points2d_scene_match, pnp_method_, inliers_idx, diff --git a/detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp b/detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp index 8af1ba65..8c4241f0 100644 --- a/detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp +++ b/detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp @@ -31,103 +31,13 @@ namespace od namespace l2d { - class vtkTimerCallbackSnapshot : public vtkCommand - { - public: - - static vtkTimerCallbackSnapshot * New() - { - vtkTimerCallbackSnapshot * cb = new vtkTimerCallbackSnapshot; - cb->snap_count = 0; - cb->snap_mode = true; - cb->feature_type = "SIFT"; - return cb; - } - - virtual void Execute(vtkObject * caller, unsigned long eventId, void * vtkNotUsed(callData)) - { - vtkRenderWindowInteractor * iren = vtkRenderWindowInteractor::SafeDownCast(caller); - - - if(!this->snap_mode) return; - - if(vtkCommand::TimerEvent == eventId) - { - ++this->snap_count; - } - - //no need to do extra processing if we have enough snaps - if(this->snap_count >= NO_SNAPSHOTS) - { - this->snap_mode = false; - //after getting all snaps, write everything together - cout << "Processing finished... Writing the final descriptors" << endl; - - std::string filename = boost::filesystem::path(input_file).filename().replace_extension(feature_type + "." + output_extension).c_str(); - fileutils::createTrainingDir(output_dir); - - write_pairs(pairs_3d_2d, common_descriptors, output_dir + "/" + filename); - write_pairs_xml(pairs_3d_2d, common_descriptors, output_dir + "/" + filename); - - //delete and remove everything - iren->TerminateApp(); - pairs_3d_2d.clear(); - } - - - - //MAIN CALLBACK, take snapshot, find features for the data from current actor - process(iren, actor, renderer, snap_count); - - //update data with the actor - actor->RotateY(360 / NO_SNAPSHOTS); - iren->GetRenderWindow()->Render(); - } - - private: - - int snap_count; - - public: - - std::string takeSnapshot(vtkRenderWindow *renderWindow, int snap_no); - - void write_pairs(std::vector<std::pair<cv::Point3f, cv::KeyPoint> > pairs, cv::Mat descriptors, std::string filename); - - void write_pairs_xml(std::vector<std::pair<cv::Point3f, cv::KeyPoint> > pairs, cv::Mat descriptors, std::string filename); - - void process_image(std::string imgname, vtkRenderer *ren, vtkActor *actor, int ino); - - void process(vtkRenderWindowInteractor *iren, vtkActor *actor, vtkRenderer *renderer, int ino); - - - //some local variables used - - struct fcomp3d_euclidian - { - bool operator()(const cv::Point3f & lhs, const cv::Point3f & rhs) const - { return lhs.x < rhs.x; } - }; - - std::vector<std::pair<cv::Point3f, cv::KeyPoint> > pairs_3d_2d; - cv::Mat common_descriptors; - std::map<cv::Point3f, cv::KeyPoint, fcomp3d_euclidian> map_3d_2d; - std::string feature_type; - std::string input_file, input_dir, output_dir, output_extension; - - vtkActor * actor; - vtkRenderer * renderer; - bool snap_mode; - - }; - int ODCADRecogTrainerSnapshotBased::train() { fileutils::createTrainingDir(trained_data_location_); //get models in the directory std::vector<std::string> files; - std::string start = ""; + std::string start = std::string(""); std::string ext = std::string("obj"); boost::filesystem::path dir = training_input_location_; fileutils::getFilesInDirectory(dir, start, files, ext); @@ -136,7 +46,7 @@ namespace od for(size_t i = 0; i < files.size(); ++i) { - trainSingleModel(training_input_location_ + "/" + files[i]); + trainSingleModel(training_input_location_ + std::string("/") + files[i]); } return 1; @@ -152,7 +62,7 @@ namespace od boost::filesystem::path p(objname); cb->input_dir = boost::filesystem::path(objname).parent_path().c_str(); cb->output_dir = getSpecificTrainingDataLocation(); - cb->output_extension = TRAINED_DATA_ID_; + cb->output_extension = trained_data_id_; vtkSmartPointer<vtkOBJReader> reader = vtkSmartPointer<vtkOBJReader>::New(); reader->SetFileName(objname.c_str()); @@ -221,34 +131,35 @@ namespace od } - std::string vtkTimerCallbackSnapshot::takeSnapshot(vtkRenderWindow *renderWindow, int snap_no) + std::string vtkTimerCallbackSnapshot::takeSnapshot(vtkRenderWindow * render_window, int snap_no) { // Screenshot vtkSmartPointer<vtkWindowToImageFilter> windowToImageFilter = vtkSmartPointer<vtkWindowToImageFilter>::New(); - windowToImageFilter->SetInput(renderWindow); + windowToImageFilter->SetInput(render_window); //windowToImageFilter->SetMagnification(3); //set the resolution of the output image (3 times the current resolution of vtk render window) //windowToImageFilter->SetInputBufferTypeToRGBA(); //also record the alpha (transparency) channel //windowToImageFilter->ReadFrontBufferOff(); // read from the back buffer windowToImageFilter->Update(); vtkSmartPointer<vtkJPEGWriter> writer = vtkSmartPointer<vtkJPEGWriter>::New(); - std::string filename; + std::string file_name; - filename = input_dir + "/" + std::string("snapshot") + std::to_string(snap_no) + ".jpg"; + file_name = input_dir + std::string("/") + std::string("snapshot") + std::to_string(snap_no) + std::string(".jpg"); - writer->SetFileName(filename.c_str()); + writer->SetFileName(file_name.c_str()); writer->SetInputConnection(windowToImageFilter->GetOutputPort()); - renderWindow->Render(); + render_window->Render(); writer->Write(); - return filename; + + return file_name; } - void vtkTimerCallbackSnapshot::write_pairs(std::vector<std::pair<cv::Point3f, cv::KeyPoint> > pairs, cv::Mat descriptors, - std::string filename) + void vtkTimerCallbackSnapshot::write_pairs(const std::vector<std::pair<cv::Point3f, cv::KeyPoint> > & pairs, const cv::Mat & descriptors, + const std::string & file_name) { std::ofstream fout; - fout.open(filename.c_str()); + fout.open(file_name.c_str()); //headers fout << pairs.size() << endl; @@ -266,13 +177,13 @@ namespace od fout.close(); } - void vtkTimerCallbackSnapshot::write_pairs_xml(std::vector<std::pair<cv::Point3f, cv::KeyPoint> > pairs, cv::Mat descriptors, - std::string filename) + void vtkTimerCallbackSnapshot::write_pairs_xml(const std::vector<std::pair<cv::Point3f, cv::KeyPoint> > & pairs, const cv::Mat & descriptors, + const std::string & file_name) { pugi::xml_document doc; pugi::xml_node root = doc.append_child("Model"); - root.append_attribute("name") = filename.c_str(); + root.append_attribute("name") = file_name.c_str(); //root.append_attribute("objid") = "1"; pugi::xml_node points_node = root.append_child("Points"); @@ -304,7 +215,7 @@ namespace od p_node.append_attribute("desc") = ss.str().c_str(); } //save - doc.save_file(filename.c_str()); + doc.save_file(file_name.c_str()); } bool valid_3D_point(double *pt) @@ -316,10 +227,10 @@ namespace od //1. find interesting 2d points, 2. find their corresponding 3d points corresponding to the transformation defined by the "actor" //3. transform the 3d points to the original by using the inverse of the "actor" - void vtkTimerCallbackSnapshot::process_image(std::string imgname, vtkRenderer *ren, vtkActor *actor, int ino) + void vtkTimerCallbackSnapshot::process_image(const std::string & img_name, vtkRenderer * ren, vtkActor * actor, int ino) { //1. find interesting 2d location - cv::Mat img = cv::imread(imgname); + cv::Mat img = cv::imread(img_name); std::vector<cv::KeyPoint> kpts; cv::Mat descriptors_local, descriptors_local_good; cv::Ptr<cv::FeatureDetector> fdetector = cv::xfeatures2d::SIFT::create(); @@ -359,12 +270,12 @@ namespace od common_descriptors.push_back(descriptors_local_good); //writing a local set - write_pairs(pairs_local, descriptors_local_good, input_dir + "/" + "local" + std::to_string(ino) + ".pairs"); - write_pairs_xml(pairs_local, descriptors_local_good, input_dir + "/" + "local" + std::to_string(ino) + ".xml"); + write_pairs(pairs_local, descriptors_local_good, input_dir + std::string("/local") + std::to_string(ino) + std::string(".pairs")); + write_pairs_xml(pairs_local, descriptors_local_good, input_dir + std::string("/local") + std::to_string(ino) + std::string(".xml")); std::cout << "Processed view " << ino << std::endl; } - void vtkTimerCallbackSnapshot::process(vtkRenderWindowInteractor *iren, vtkActor *actor, vtkRenderer *renderer, int ino) + void vtkTimerCallbackSnapshot::process(vtkRenderWindowInteractor * iren, vtkActor * actor, vtkRenderer * renderer, int ino) { vtkRenderWindow *win = iren->GetRenderWindow(); @@ -425,6 +336,46 @@ namespace od vtkStandardNewMacro(MouseInteractorStyle2) + void vtkTimerCallbackSnapshot::Execute(vtkObject * caller, unsigned long eventId, void * vtkNotUsed(callData)) + { + vtkRenderWindowInteractor * iren = vtkRenderWindowInteractor::SafeDownCast(caller); + + + if(!this->snap_mode) return; + + if(vtkCommand::TimerEvent == eventId) + { + ++this->snap_count_; + } + + //no need to do extra processing if we have enough snaps + if(this->snap_count_ >= NO_SNAPSHOTS) + { + this->snap_mode = false; + //after getting all snaps, write everything together + cout << "Processing finished... Writing the final descriptors" << endl; + + std::string filename = boost::filesystem::path(input_file).filename().replace_extension(feature_type + "." + output_extension).c_str(); + fileutils::createTrainingDir(output_dir); + + write_pairs(pairs_3d_2d, common_descriptors, output_dir + "/" + filename); + write_pairs_xml(pairs_3d_2d, common_descriptors, output_dir + "/" + filename); + + //delete and remove everything + iren->TerminateApp(); + pairs_3d_2d.clear(); + } + + + + //MAIN CALLBACK, take snapshot, find features for the data from current actor + process(iren, actor, renderer, snap_count_); + + //update data with the actor + actor->RotateY(360 / NO_SNAPSHOTS); + iren->GetRenderWindow()->Render(); + } + } } \ No newline at end of file From 0b0c76ffdd45062813a5160bcab648bd0c486222 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Thu, 16 Jun 2016 14:33:21 +0200 Subject: [PATCH 26/79] some other fixes --- common/src/utils/ODFeatureDetector2D.cpp | 2 +- common/src/utils/ODUtils.cpp | 10 +- .../local2D/simple_ransac_detection/Model.h | 7 +- .../simple_ransac_detection/RobustMatcher.h | 2 +- .../local2D/simple_ransac_detection/Utils.h | 35 ++-- .../detection/ODCADRecognizer2DLocal.cpp | 2 +- .../local2D/simple_ransac_detection/Model.cpp | 9 +- .../simple_ransac_detection/PnPProblem.cpp | 2 +- .../simple_ransac_detection/RobustMatcher.cpp | 78 ++++----- .../local2D/simple_ransac_detection/Utils.cpp | 161 +++++++----------- 10 files changed, 136 insertions(+), 172 deletions(-) diff --git a/common/src/utils/ODFeatureDetector2D.cpp b/common/src/utils/ODFeatureDetector2D.cpp index c91912e7..34303091 100644 --- a/common/src/utils/ODFeatureDetector2D.cpp +++ b/common/src/utils/ODFeatureDetector2D.cpp @@ -168,7 +168,7 @@ namespace od { sift_gpu_->RunSIFT(image_name.c_str()); - int nFeat = sift_gpu_->GetFeatureNum();//get feature count + unsigned int nFeat = sift_gpu_->GetFeatureNum();//get feature count //allocate memory for readback std::vector<SiftGPU::SiftKeypoint> keys(nFeat); //read back keypoints and normalized descritpros diff --git a/common/src/utils/ODUtils.cpp b/common/src/utils/ODUtils.cpp index f5a96fdc..2b4550f4 100644 --- a/common/src/utils/ODUtils.cpp +++ b/common/src/utils/ODUtils.cpp @@ -21,15 +21,15 @@ namespace od void normL2(cv::Mat & descriptors) { - for (int r = 0; r < descriptors.rows; r++) + for(int r = 0; r < descriptors.rows; r++) { float norm = 0; - for (size_t c = 0; c < descriptors.cols; c++) + for(int c = 0; c < descriptors.cols; c++) norm += (descriptors.at<float>(r, c)*descriptors.at<float>(r, c)); norm = 1.0/sqrt(norm); - for (size_t c = 0; c < descriptors.cols; c++) + for(int c = 0; c < descriptors.cols; c++) descriptors.at<float>(r, c) *= norm; } } @@ -47,8 +47,8 @@ namespace od printmssg = true; const float n_imgs = imgs.size(); - const int imgs_in_row = ceil(sqrt(n_imgs)); - const int imgs_in_col = ceil(n_imgs/imgs_in_row); + const unsigned int imgs_in_row = ceil(sqrt(n_imgs)); + const unsigned int imgs_in_col = ceil(n_imgs/imgs_in_row); const unsigned int cell_width = cell_size.width; const unsigned int cell_height = cell_size.height; diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/Model.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/Model.h index 9ec64fb5..6d2d5098 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/Model.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/Model.h @@ -60,12 +60,7 @@ namespace od { /** The list of 2D points descriptors */ cv::Mat descriptors_; - int getDescriptorSize() - { - //if (f_type == "SIFT") - //return 128; - return 128; - } + unsigned int getDescriptorSize(); }; } diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/RobustMatcher.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/RobustMatcher.h index 6e1b9f61..8056b7d8 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/RobustMatcher.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/RobustMatcher.h @@ -45,7 +45,7 @@ namespace od { void computeDescriptors( const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints, cv::Mat & descriptors); // Set ratio parameter for the ratio test - void setRatio( float rat) { ratio_ = rat; } + void setRatio(float rat); // Clear matches for which NN ratio is > than threshold // return the number of removed points diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/Utils.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/Utils.h index 04b3300d..72d3f48d 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/Utils.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/Utils.h @@ -22,50 +22,51 @@ namespace od { namespace l2d { - void viewImage(cv::Mat image, std::vector<cv::KeyPoint> keypoints = std::vector<cv::KeyPoint>(0)); + void viewImage(const cv::Mat & image, const std::vector<cv::KeyPoint> & keypoints = std::vector<cv::KeyPoint>(0)); // Draw a text with the question point - void drawQuestion(cv::Mat image, cv::Point3f point, cv::Scalar color); + void drawQuestion(cv::Mat & image, const cv::Point3f & point, const cv::Scalar & color); // Draw a text with the number of entered points - void drawText(cv::Mat image, std::string text, cv::Scalar color); + void drawText(cv::Mat & image, const std::string & text, const cv::Scalar & color); // Draw a text with the number of entered points - void drawText2(cv::Mat image, std::string text, cv::Scalar color); + void drawText2(cv::Mat & image, const std::string & text, const cv::Scalar & color); // Draw a text with the frame ratio - void drawFPS(cv::Mat image, double fps, cv::Scalar color); + void drawFPS(cv::Mat & image, double fps, const cv::Scalar & color); // Draw a text with the frame ratio - void drawConfidence(cv::Mat image, double confidence, cv::Scalar color); + void drawConfidence(cv::Mat & image, double confidence, const cv::Scalar & color); // Draw a text with the number of entered points - void drawCounter(cv::Mat image, int n, int n_max, cv::Scalar color); + void drawCounter(cv::Mat & image, int n, int n_max, const cv::Scalar & color); // Draw the points and the coordinates - void drawPoints(cv::Mat image, std::vector<cv::Point2f> & list_points_2d, std::vector<cv::Point3f> & list_points_3d, cv::Scalar color); + void drawPoints(cv::Mat & image, const std::vector<cv::Point2f> & list_points_2d, const std::vector<cv::Point3f> & list_points_3d, + const cv::Scalar & color); // Draw only the 2D points - void draw2DPoints(cv::Mat image, std::vector<cv::Point2f> & list_points, cv::Scalar color); + void draw2DPoints(cv::Mat & image, const std::vector<cv::Point2f> & list_points, const cv::Scalar & color); // Draw an arrow into the image - void drawArrow(cv::Mat image, cv::Point2i p, cv::Point2i q, cv::Scalar color, int arrowMagnitude = 9, + void drawArrow(cv::Mat & image, cv::Point2i & p, const cv::Point2i & q, const cv::Scalar & color, int arrowMagnitude = 9, int thickness = 1, int line_type = 8, int shift = 0); // Draw the 3D coordinate axes - void draw3DCoordinateAxes(cv::Mat image, const std::vector<cv::Point2f> & list_points2d); + void draw3DCoordinateAxes(cv::Mat & image, const std::vector<cv::Point2f> & list_points2d); // Draw the object mesh - void drawObjectMesh(cv::Mat image, const Mesh * mesh, PnPProblem * pnpProblem, cv::Scalar color); - void drawObjectMesh1(cv::Mat image, const Mesh * mesh, const Model * model, PnPProblem * pnpProblem, cv::Scalar color); - void drawModel(cv::Mat image, const Model * model, PnPProblem * pnpProblem, cv::Scalar color); - void drawModel(cv::Mat image, const Model * model, cv::Mat R_vect, cv::Mat t_mat, cv::Mat A_mat, cv::Mat dist, cv::Scalar color); + void drawObjectMesh(cv::Mat & image, const Mesh & mesh, PnPProblem & pnpProblem, const cv::Scalar & color); + void drawModel(cv::Mat & image, const Model & model, PnPProblem & pnpProblem, const cv::Scalar color); + void drawModel(cv::Mat & image, const Model & model, cv::Mat r_vect, cv::Mat t_mat, cv::Mat a_mat, + cv::Mat dist, const cv::Scalar & color); // Computes the norm of the translation error - double get_translation_error(const cv::Mat & t_true, const cv::Mat & t); + double getTranslationError(const cv::Mat & t_true, const cv::Mat & t); // Computes the norm of the rotation error - double get_rotation_error(const cv::Mat & R_true, const cv::Mat & R); + double getRotationError(const cv::Mat & r_true, const cv::Mat & r); // Converts a given Rotation Matrix to Euler angles cv::Mat rot2euler(const cv::Mat & rotationMatrix); diff --git a/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp b/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp index c5ceaaee..4ec76d18 100644 --- a/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp +++ b/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp @@ -357,7 +357,7 @@ namespace od detections->push_back(detection); if(meta_info_) - drawModel(viz, &models_[i], pnp_detection_.getRVect(), pnp_detection_.getTMatrix(), pnp_detection_.getAMatrix(), + drawModel(viz, models_[i], pnp_detection_.getRVect(), pnp_detection_.getTMatrix(), pnp_detection_.getAMatrix(), pnp_detection_.getDistCoef(), yellow_); } } diff --git a/detectors/src/local2D/simple_ransac_detection/Model.cpp b/detectors/src/local2D/simple_ransac_detection/Model.cpp index 132d1d22..18d0132a 100644 --- a/detectors/src/local2D/simple_ransac_detection/Model.cpp +++ b/detectors/src/local2D/simple_ransac_detection/Model.cpp @@ -119,7 +119,7 @@ namespace od { return ; } - int nop, desc_size, point_size; + unsigned int nop, desc_size, point_size; infile >> nop; infile >> point_size >> desc_size; @@ -145,6 +145,13 @@ namespace od { id_ = path; } + unsigned int Model::getDescriptorSize() + { + //if (f_type == "SIFT") + //return 128; + return 128; + } + void Model::loadNewXml(const std::string & path) { diff --git a/detectors/src/local2D/simple_ransac_detection/PnPProblem.cpp b/detectors/src/local2D/simple_ransac_detection/PnPProblem.cpp index 6cd060fc..98de1072 100644 --- a/detectors/src/local2D/simple_ransac_detection/PnPProblem.cpp +++ b/detectors/src/local2D/simple_ransac_detection/PnPProblem.cpp @@ -206,7 +206,7 @@ namespace od { std::vector<cv::Point2f> PnPProblem::verifyPoints(shared_ptr<Mesh> mesh) { std::vector<cv::Point2f> verified_points_2d; - for(size_t i = 0; i < mesh->getNumVertices(); ++i) + for(int i = 0; i < mesh->getNumVertices(); ++i) { cv::Point3f point3d = mesh->getVertex(i); cv::Point2f point2d = backproject3DPoint(point3d); diff --git a/detectors/src/local2D/simple_ransac_detection/RobustMatcher.cpp b/detectors/src/local2D/simple_ransac_detection/RobustMatcher.cpp index d2db03a3..f02ead49 100644 --- a/detectors/src/local2D/simple_ransac_detection/RobustMatcher.cpp +++ b/detectors/src/local2D/simple_ransac_detection/RobustMatcher.cpp @@ -11,9 +11,9 @@ namespace od { namespace l2d { +/* void convertToUnsignedSiftGPU(const cv::Mat & cv_des, std::vector<unsigned char> & siftgpu_des) { - int totdes = cv_des.rows * 128; siftgpu_des.resize(cv_des.rows * 128); int desi = 0; @@ -24,7 +24,7 @@ namespace od { } } - void convertToDmatch(int siftgpu_matches[][2], int num_match, std::vector<cv::DMatch> & cv_matches) + void convertToDmatch(int siftgpu_matches[][2], unsigned int num_match, std::vector<cv::DMatch> & cv_matches) { cv_matches.resize(num_match); for(size_t i = 0; i < num_match; ++i) @@ -38,6 +38,19 @@ namespace od { } } + void getGoodMatches(const std::vector<std::vector<cv::DMatch> > & matches, std::vector<cv::DMatch> & goodMatches) + { + for (std::vector<std::vector<cv::DMatch> >::const_iterator + matchIterator1 = matches.begin(); matchIterator1 != matches.end(); ++matchIterator1) + { + // ignore deleted matches + if (matchIterator1->empty() || matchIterator1->size() < 2) + continue; + if ((*matchIterator1)[0].distance < (*matchIterator1)[1].distance) + goodMatches.push_back((*matchIterator1)[0]); + } + } +*/ void RobustMatcher::instantiateMatcher(const Model & model, bool use_gpu) { @@ -64,15 +77,11 @@ namespace od { use_gpu_ = use_gpu_match; - - // ORB is the default feature - ratio_ =0.8f; + ratio_ = 0.8f; detector_ = cv::ORB::create(); extractor_ = cv::ORB::create(); - - instantiateMatcher(model, use_gpu_match); // // BruteFroce matcher with Norm Hamming is the default matcher @@ -106,14 +115,14 @@ namespace od { extractor_->compute(image, keypoints, descriptors); } - int RobustMatcher::ratioTest(std::vector<std::vector<cv::DMatch> > &matches) + int RobustMatcher::ratioTest(std::vector<std::vector<cv::DMatch> > & matches) { int removed = 0; // for all matches for(std::vector<std::vector<cv::DMatch> >::iterator matchIterator= matches.begin(); matchIterator!= matches.end(); ++matchIterator) { - // if 2 NN has been identified + // if 2 NN have been identified if(matchIterator->size() > 1) { // check distance ratio @@ -132,18 +141,9 @@ namespace od { return removed; } - void getGoodMatches(const std::vector<std::vector<cv::DMatch> > & matches, std::vector<cv::DMatch> & goodMatches) - { - for (std::vector<std::vector<cv::DMatch> >::const_iterator - matchIterator1 = matches.begin(); matchIterator1 != matches.end(); ++matchIterator1) - { - // ignore deleted matches - if (matchIterator1->empty() || matchIterator1->size() < 2) - continue; - if ((*matchIterator1)[0].distance < (*matchIterator1)[1].distance) - goodMatches.push_back((*matchIterator1)[0]); - } - + void RobustMatcher::setRatio(float rat) + { + ratio_ = rat; } void RobustMatcher::symmetryTest(const std::vector<std::vector<cv::DMatch> > & matches1, @@ -190,33 +190,26 @@ namespace od { { std::vector<std::vector<cv::DMatch> > matches; - if (use_gpu_) + if(use_gpu_) { matcher_gpu_->knnMatch(cv::cuda::GpuMat(descriptors_frame), matches, 2); // return 2 nearest neighbours - ratioTest(matches); - for ( std::vector<std::vector<cv::DMatch> >::iterator - matchIterator= matches.begin(); matchIterator!= matches.end(); ++matchIterator) - { - if (!matchIterator->empty()) good_matches.push_back((*matchIterator)[0]); - } - } - else - { + }else{ matcher_->knnMatch(descriptors_frame, descriptors_model, matches, 2); // return 2 nearest neighbours - ratioTest(matches); - // 4. Fill good matches container - for ( std::vector<std::vector<cv::DMatch> >::iterator - matchIterator= matches.begin(); matchIterator!= matches.end(); ++matchIterator) - { - if (!matchIterator->empty()) good_matches.push_back((*matchIterator)[0]); - } + } + ratioTest(matches); + // 4. Fill good matches container + for(auto & match : matches) + { + good_matches.push_back(match[0]); } + } void RobustMatcher::matchNormalized(cv::Mat & descriptors_frame, cv::Mat & descriptors_model, std::vector<cv::DMatch> & good_matches) { - od::normL2(descriptors_frame); od::normL2(descriptors_model); + od::normL2(descriptors_frame); + od::normL2(descriptors_model); match(descriptors_frame, descriptors_model, good_matches); } @@ -241,7 +234,6 @@ namespace od { // 2b. From image 2 to image 1 matcher_->knnMatch(descriptors_model, descriptors_frame, matches21, 2); // return 2 nearest neighbours - // 3. Remove matches for which NN ratio is > than threshold // clean image 1 -> image 2 matches ratioTest(matches12); @@ -310,12 +302,10 @@ namespace od { ratioTest(matches); // 4. Fill good matches container - for(std::vector<std::vector<cv::DMatch> >::iterator - matchIterator= matches.begin(); matchIterator!= matches.end(); ++matchIterator) + for(auto & match : matches) { - if (!matchIterator->empty()) good_matches.push_back((*matchIterator)[0]); + good_matches.push_back(match[0]); } - } } diff --git a/detectors/src/local2D/simple_ransac_detection/Utils.cpp b/detectors/src/local2D/simple_ransac_detection/Utils.cpp index e62e3fbe..a4ef573e 100644 --- a/detectors/src/local2D/simple_ransac_detection/Utils.cpp +++ b/detectors/src/local2D/simple_ransac_detection/Utils.cpp @@ -20,7 +20,7 @@ namespace od { int radius = 1; double thickness_circ = -1; - void viewImage(cv::Mat image, std::vector<cv::KeyPoint> keypoints) + void viewImage(const cv::Mat & image, const std::vector<cv::KeyPoint> & keypoints) { cv::Mat outimage = image; cv::drawKeypoints(image, keypoints, outimage, cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS); @@ -33,7 +33,7 @@ namespace od { } // Draw a text with the question point - void drawQuestion(cv::Mat image, cv::Point3f point, cv::Scalar color) + void drawQuestion(cv::Mat & image, const cv::Point3f & point, const cv::Scalar & color) { std::string x = std::to_string((int)point.x); std::string y = std::to_string((int)point.y); @@ -44,19 +44,19 @@ namespace od { } // Draw a text with the number of entered points - void drawText(cv::Mat image, std::string text, cv::Scalar color) + void drawText(cv::Mat & image, const std::string & text, const cv::Scalar & color) { cv::putText(image, text, cv::Point(25,50), fontFace, fontScale, color, thickness_font, 8); } // Draw a text with the number of entered points - void drawText2(cv::Mat image, std::string text, cv::Scalar color) + void drawText2(cv::Mat & image, const std::string & text, const cv::Scalar & color) { cv::putText(image, text, cv::Point(25,75), fontFace, fontScale, color, thickness_font, 8); } // Draw a text with the frame ratio - void drawFPS(cv::Mat image, double fps, cv::Scalar color) + void drawFPS(cv::Mat & image, double fps, const cv::Scalar & color) { std::string fps_str = std::to_string((int)fps); std::string text = fps_str + " FPS"; @@ -64,15 +64,15 @@ namespace od { } // Draw a text with the frame ratio - void drawConfidence(cv::Mat image, double confidence, cv::Scalar color) + void drawConfidence(cv::Mat & image, double confidence, const cv::Scalar & color) { std::string conf_str = std::to_string((int)confidence); - std::string text = conf_str + " %"; + std::string text = conf_str + std::string(" %"); cv::putText(image, text, cv::Point(500,75), fontFace, fontScale, color, thickness_font, 8); } // Draw a text with the number of entered points - void drawCounter(cv::Mat image, int n, int n_max, cv::Scalar color) + void drawCounter(cv::Mat & image, int n, int n_max, const cv::Scalar & color) { std::string n_str = std::to_string(n); std::string n_max_str = std::to_string(n_max); @@ -81,9 +81,10 @@ namespace od { } // Draw the points and the coordinates - void drawPoints(cv::Mat image, std::vector<cv::Point2f> & list_points_2d, std::vector<cv::Point3f> & list_points_3d, cv::Scalar color) + void drawPoints(cv::Mat & image, const std::vector<cv::Point2f> & list_points_2d, const std::vector<cv::Point3f> & list_points_3d, + const cv::Scalar & color) { - for (unsigned int i = 0; i < list_points_2d.size(); ++i) + for(size_t i = 0; i < list_points_2d.size(); ++i) { cv::Point2f point_2d = list_points_2d[i]; cv::Point3f point_3d = list_points_3d[i]; @@ -104,9 +105,9 @@ namespace od { } // Draw only the 2D points - void draw2DPoints(cv::Mat image, std::vector<cv::Point2f> & list_points, cv::Scalar color) + void draw2DPoints(cv::Mat & image, const std::vector<cv::Point2f> & list_points, const cv::Scalar & color) { - for( size_t i = 0; i < list_points.size(); i++) + for(size_t i = 0; i < list_points.size(); i++) { cv::Point2f point_2d = list_points[i]; @@ -116,13 +117,14 @@ namespace od { } // Draw an arrow into the image - void drawArrow(cv::Mat image, cv::Point2i p, cv::Point2i q, cv::Scalar color, int arrowMagnitude, int thickness, int line_type, int shift) + void drawArrow(cv::Mat & image, cv::Point2i & p, const cv::Point2i & q, const cv::Scalar & color, + int arrowMagnitude, int thickness, int line_type, int shift) { //Draw the principle line cv::line(image, p, q, color, thickness, line_type, shift); const double PI = CV_PI; //compute the angle alpha - double angle = atan2((double)p.y-q.y, (double)p.x-q.x); + const double angle = atan2((double)p.y-q.y, (double)p.x-q.x); //compute the coordinates of the first segment p.x = (int) ( q.x + arrowMagnitude * cos(angle + PI/4)); p.y = (int) ( q.y + arrowMagnitude * sin(angle + PI/4)); @@ -136,7 +138,7 @@ namespace od { } // Draw the 3D coordinate axes - void draw3DCoordinateAxes(cv::Mat image, const std::vector<cv::Point2f> & list_points2d) + void draw3DCoordinateAxes(cv::Mat & image, const std::vector<cv::Point2f> & list_points2d) { cv::Scalar red(0, 0, 255); cv::Scalar green(0,255,0); @@ -156,9 +158,9 @@ namespace od { } // Draw the object mesh - void drawObjectMesh(cv::Mat image, const Mesh * mesh, PnPProblem * pnpProblem, cv::Scalar color) + void drawObjectMesh(cv::Mat image, const Mesh & mesh, PnPProblem & pnpProblem, const cv::Scalar & color) { - std::vector<std::vector<int> > list_triangles = mesh->getTrianglesList(); + std::vector<std::vector<int> > list_triangles = mesh.getTrianglesList(); // if (pnpProblem->get_A_matrix().at<float>(0,2) < 300 && list_triangles.size() > 1000) // skip = 10; @@ -166,15 +168,15 @@ namespace od { { std::vector<int> tmp_triangle = list_triangles.at(i); - cv::Point3f point_3d_0 = mesh->getVertex(tmp_triangle[0]); - cv::Point3f point_3d_1 = mesh->getVertex(tmp_triangle[1]); - cv::Point3f point_3d_2 = mesh->getVertex(tmp_triangle[2]); + const cv::Point3f point_3d_0 = mesh.getVertex(tmp_triangle[0]); + const cv::Point3f point_3d_1 = mesh.getVertex(tmp_triangle[1]); + cv::Point3f point_3d_2 = mesh.getVertex(tmp_triangle[2]); //std::cout << point_3d_0 << std::endl << point_3d_1 << std::endl << point_3d_2 << std::endl << std::endl; - cv::Point2f point_2d_0 = pnpProblem->backproject3DPoint(point_3d_0); - cv::Point2f point_2d_1 = pnpProblem->backproject3DPoint(point_3d_1); - cv::Point2f point_2d_2 = pnpProblem->backproject3DPoint(point_3d_2); + const cv::Point2f point_2d_0 = pnpProblem.backproject3DPoint(point_3d_0); + const cv::Point2f point_2d_1 = pnpProblem.backproject3DPoint(point_3d_1); + const cv::Point2f point_2d_2 = pnpProblem.backproject3DPoint(point_3d_2); cv::line(image, point_2d_0, point_2d_1, color, 1); cv::line(image, point_2d_1, point_2d_2, color, 1); @@ -182,31 +184,24 @@ namespace od { } } - void drawObjectMesh1(cv::Mat image, const Mesh * mesh, const Model * model, PnPProblem * pnpProblem, cv::Scalar color) + void drawModel(cv::Mat & image, const Model & model, const PnPProblem & pnpProblem, const cv::Scalar & color) { - std::vector<cv::Point2f> imgpoints; - cv::projectPoints(model->getPoints3d(), pnpProblem->getRVect(), pnpProblem->getTMatrix(), pnpProblem->getAMatrix(), pnpProblem->getDistCoef(), imgpoints); - draw2DPoints(image, imgpoints, color); + std::vector<cv::Point2f> img_points; + cv::projectPoints(model.getPoints3d(), pnpProblem.getRVect(), pnpProblem.getTMatrix(), pnpProblem.getAMatrix(), + pnpProblem.getDistCoef(), img_points); + draw2DPoints(image, img_points, color); } - void drawModel(cv::Mat image, const Model * model, PnPProblem * pnpProblem, cv::Scalar color) + void drawModel(cv::Mat & image, const Model & model, cv::Mat r_vect, + cv::Mat t_mat, cv::Mat a_mat, cv::Mat dist, const cv::Scalar & color) { - std::vector<cv::Point2f> imgpoints; - cv::projectPoints(model->getPoints3d(), pnpProblem->getRVect(), pnpProblem->getTMatrix(), pnpProblem->getAMatrix(), pnpProblem->getDistCoef(), imgpoints); - draw2DPoints(image, imgpoints, color); + std::vector<cv::Point2f> img_points; + cv::projectPoints(model.getPoints3d(), r_vect, t_mat, a_mat, dist, img_points); + draw2DPoints(image, img_points, color); } - void drawModel(cv::Mat image, const Model * model, cv::Mat R_vect, cv::Mat t_mat, cv::Mat A_mat, cv::Mat dist, cv::Scalar color) - { - std::vector<cv::Point2f> imgpoints; - cv::projectPoints(model->getPoints3d(), R_vect, t_mat, A_mat, dist, imgpoints); - draw2DPoints(image, imgpoints, color); - - } - - // Computes the norm of the translation error double getTranslationError(const cv::Mat & t_true, const cv::Mat & t) { @@ -214,27 +209,27 @@ namespace od { } // Computes the norm of the rotation error - double getRotationError(const cv::Mat & R_true, const cv::Mat & R) + double getRotationError(const cv::Mat & r_true, const cv::Mat & r) { cv::Mat error_vec, error_mat; - error_mat = R_true * cv::Mat(R.inv()).mul(-1); + error_mat = r_true * cv::Mat(r.inv()).mul(-1); cv::Rodrigues(error_mat, error_vec); return cv::norm(error_vec); } // Converts a given Rotation Matrix to Euler angles - cv::Mat rot2euler(const cv::Mat & rotationMatrix) + cv::Mat rot2euler(const cv::Mat & rotation_matrix) { cv::Mat euler(3,1,CV_64F); - double m00 = rotationMatrix.at<double>(0,0); - double m02 = rotationMatrix.at<double>(0,2); - double m10 = rotationMatrix.at<double>(1,0); - double m11 = rotationMatrix.at<double>(1,1); - double m12 = rotationMatrix.at<double>(1,2); - double m20 = rotationMatrix.at<double>(2,0); - double m22 = rotationMatrix.at<double>(2,2); + const double m00 = rotation_matrix.at<double>(0,0); + const double m02 = rotation_matrix.at<double>(0,2); + const double m10 = rotation_matrix.at<double>(1,0); + const double m11 = rotation_matrix.at<double>(1,1); + const double m12 = rotation_matrix.at<double>(1,2); + const double m20 = rotation_matrix.at<double>(2,0); + const double m22 = rotation_matrix.at<double>(2,2); double x, y, z; @@ -266,19 +261,19 @@ namespace od { // Converts a given Euler angles to Rotation Matrix cv::Mat euler2rot(const cv::Mat & euler) { - cv::Mat rotationMatrix(3,3,CV_64F); + cv::Mat rotation_matrix(3, 3, CV_64F); - double x = euler.at<double>(0); - double y = euler.at<double>(1); - double z = euler.at<double>(2); + const double x = euler.at<double>(0); + const double y = euler.at<double>(1); + const double z = euler.at<double>(2); // Assuming the angles are in radians. - double ch = cos(z); - double sh = sin(z); - double ca = cos(y); - double sa = sin(y); - double cb = cos(x); - double sb = sin(x); + const double ch = cos(z); + const double sh = sin(z); + const double ca = cos(y); + const double sa = sin(y); + const double cb = cos(x); + const double sb = sin(x); double m00, m01, m02, m10, m11, m12, m20, m21, m22; @@ -292,41 +287,17 @@ namespace od { m21 = sh*sa*cb + ch*sb; m22 = -sh*sa*sb + ch*cb; - rotationMatrix.at<double>(0,0) = m00; - rotationMatrix.at<double>(0,1) = m01; - rotationMatrix.at<double>(0,2) = m02; - rotationMatrix.at<double>(1,0) = m10; - rotationMatrix.at<double>(1,1) = m11; - rotationMatrix.at<double>(1,2) = m12; - rotationMatrix.at<double>(2,0) = m20; - rotationMatrix.at<double>(2,1) = m21; - rotationMatrix.at<double>(2,2) = m22; - - return rotationMatrix; - } - - // Converts a given string to an integer - int StringToInt (const std::string & Text ) - { - std::istringstream ss(Text); - int result; - return ss >> result ? result : 0; - } - - // Converts a given float to a string - std::string FloatToString (float Number ) - { - std::ostringstream ss; - ss << Number; - return ss.str(); - } - - // Converts a given integer to a string - std::string IntToString (int Number ) - { - std::ostringstream ss; - ss << Number; - return ss.str(); + rotation_matrix.at<double>(0,0) = m00; + rotation_matrix.at<double>(0,1) = m01; + rotation_matrix.at<double>(0,2) = m02; + rotation_matrix.at<double>(1,0) = m10; + rotation_matrix.at<double>(1,1) = m11; + rotation_matrix.at<double>(1,2) = m12; + rotation_matrix.at<double>(2,0) = m20; + rotation_matrix.at<double>(2,1) = m21; + rotation_matrix.at<double>(2,2) = m22; + + return rotation_matrix; } } From 0a73382a85a81dbd002dbca9361fc811c33ac135 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Mon, 20 Jun 2016 12:08:32 +0200 Subject: [PATCH 27/79] Creates FindOD.cmake and adds uninstall target --- CMakeLists.txt | 26 +++++++--- cmake/ODConfig.cmake.in | 3 ++ cmake/cmake_uninstall.cmake.in | 21 ++++++++ cmake/od_macros.cmake | 2 + cmake/od_targets.cmake | 10 +++- cmake/{od_config.h.in => od_version.h.in} | 0 common/CMakeLists.txt | 3 +- .../od/common/pipeline/ODAlgorithmBase.h | 51 ------------------- detectors/CMakeLists.txt | 3 +- detectors/src/global2D/CMakeLists.txt | 6 --- detectors/src/global3D/CMakeLists.txt | 2 - detectors/src/local2D/CMakeLists.txt | 7 --- examples/apps/version/opendetection.cpp | 2 +- 13 files changed, 59 insertions(+), 77 deletions(-) create mode 100644 cmake/ODConfig.cmake.in create mode 100644 cmake/cmake_uninstall.cmake.in rename cmake/{od_config.h.in => od_version.h.in} (100%) delete mode 100644 common/include/od/common/pipeline/ODAlgorithmBase.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8abd3e53..3f5ff331 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,16 +6,18 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall") # Initialize variables set(OD_SOURCE_DIR ${OpenDetection_SOURCE_DIR}) set(OD_BINARY_DIR ${OpenDetection_BINARY_DIR}) -set(OD_CMAKE_DIR "${OpenDetection_SOURCE_DIR}/cmake") +set(OD_CMAKE_DIR ${OpenDetection_SOURCE_DIR}/cmake) set(CMAKE_MODULE_PATH ${OD_CMAKE_DIR}/modules) set(CMAKE_3RDPARTY_DIR ${OD_SOURCE_DIR}/3rdparty) - # Initialize versioning include(${OD_CMAKE_DIR}/od_version.cmake) -set(OD_VERSION "${OD_MAJOR_VERSION}.${OD_MINOR_VERSION}") -set(OD_VERSION_DETAILED "${OD_MAJOR_VERSION}.${OD_MINOR_VERSION}.${OD_BUILD_VERSION}") -configure_file("${OD_CMAKE_DIR}/od_config.h.in" "${OD_BINARY_DIR}/generated/od_config.h") +set(OD_VERSION ${OD_MAJOR_VERSION}.${OD_MINOR_VERSION}) +set(OD_VERSION_DETAILED ${OD_MAJOR_VERSION}.${OD_MINOR_VERSION}.${OD_BUILD_VERSION}) + +# Prepare configuration files +configure_file(${OD_CMAKE_DIR}/od_version.h.in ${OD_BINARY_DIR}/generated/od_version.h) +configure_file(${OD_CMAKE_DIR}/ODConfig.cmake.in ${OD_BINARY_DIR}/FindOD.cmake) # Set up macros and configurations include(${OD_CMAKE_DIR}/od_targets.cmake) @@ -38,6 +40,18 @@ set(OD_MODULES_DIRS ${OD_MODULES_NAMES}) # Add modules foreach(subdir ${OD_MODULES_DIRS}) - add_subdirectory("${OD_SOURCE_DIR}/${subdir}") + add_subdirectory(${OD_SOURCE_DIR}/${subdir}) endforeach(subdir) +# Install the FindPackage configuration file +get_property(OD_INSTALLED_LIBRARIES GLOBAL PROPERTY OD_INSTALLED_LIBRARIES) +install(FILES ${OD_BINARY_DIR}/FindOD.cmake DESTINATION ${OD_CMAKE_INSTALL_DIR}) + +# uninstall target +configure_file( + "${OD_CMAKE_DIR}/cmake_uninstall.cmake.in" + "${OD_BINARY_DIR}/cmake_uninstall.cmake" + IMMEDIATE @ONLY) + +add_custom_target(uninstall + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) \ No newline at end of file diff --git a/cmake/ODConfig.cmake.in b/cmake/ODConfig.cmake.in new file mode 100644 index 00000000..0a78855e --- /dev/null +++ b/cmake/ODConfig.cmake.in @@ -0,0 +1,3 @@ +set(OD_INCLUDE_DIRS ${CMAKE_INSTALL_PREFIX}/${OD_INSTALL_INCLUDE_DIR}) +set(OD_LIBRARY_PATH ${CMAKE_INSTALL_PREFIX}/${OD_INSTALL_LIBRARY_DIR}) +set(OD_LIBRARIES ${OD_INSTALLED_LIBRARIES}) \ No newline at end of file diff --git a/cmake/cmake_uninstall.cmake.in b/cmake/cmake_uninstall.cmake.in new file mode 100644 index 00000000..2037e365 --- /dev/null +++ b/cmake/cmake_uninstall.cmake.in @@ -0,0 +1,21 @@ +if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") +endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + +file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) +string(REGEX REPLACE "\n" ";" files "${files}") +foreach(file ${files}) + message(STATUS "Uninstalling $ENV{DESTDIR}${file}") + if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") + exec_program( + "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" + OUTPUT_VARIABLE rm_out + RETURN_VALUE rm_retval + ) + if(NOT "${rm_retval}" STREQUAL 0) + message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") + endif(NOT "${rm_retval}" STREQUAL 0) + else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") + message(STATUS "File $ENV{DESTDIR}${file} does not exist.") + endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") +endforeach(file) diff --git a/cmake/od_macros.cmake b/cmake/od_macros.cmake index 786677c1..73de7d22 100644 --- a/cmake/od_macros.cmake +++ b/cmake/od_macros.cmake @@ -26,6 +26,8 @@ macro(OD_ADD_LIBRARY _name ) LIBRARY DESTINATION ${OD_INSTALL_LIBRARY_DIR} COMPONENT ${lib_name} ARCHIVE DESTINATION ${OD_INSTALL_ARCHIVE_DIR} COMPONENT ${lib_name}) + set_property(GLOBAL APPEND PROPERTY OD_INSTALLED_LIBRARIES ${lib_name}) + endmacro(OD_ADD_LIBRARY) macro(OD_ADD_EXAMPLE _name) diff --git a/cmake/od_targets.cmake b/cmake/od_targets.cmake index 7e950181..26aecfb9 100644 --- a/cmake/od_targets.cmake +++ b/cmake/od_targets.cmake @@ -23,12 +23,20 @@ if(NOT OD_INSTALL_DOC_DIR) set(OD_INSTALL_DOC_DIR share/doc/od-${OD_VERSION}) endif() +if(NOT OD_CMAKE_INSTALL_DIR) + set(OD_CMAKE_INSTALL_DIR lib/cmake) +endif() + if(NOT OD_INSTALL_PACKAGE_DIR) - set(OD_INSTALL_PACKAGE_DIR "lib/cmake/od-${OD_VERSION}") + set(OD_INSTALL_PACKAGE_DIR lib/cmake/od-${OD_VERSION}) endif() if(NOT OD_INSTALL_DOXYGEN_DIR) set(OD_INSTALL_DOXYGEN_DIR ${OD_INSTALL_DOC_DIR}/doxygen) endif() +if(NOT OD_INSTALLED_LIBRARIES) + set_property(GLOBAL PROPERTY OD_INSTALLED_LIBRARIES "") +endif() + diff --git a/cmake/od_config.h.in b/cmake/od_version.h.in similarity index 100% rename from cmake/od_config.h.in rename to cmake/od_version.h.in diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 3c80ab92..3bbb8485 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -30,7 +30,6 @@ if(build) OD_ADD_LIBRARY(${SUBSYS_NAME} SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) install(DIRECTORY ${COMMON_INCLUDE_DIR}/od DESTINATION ${OD_INSTALL_INCLUDE_DIR} COMPONENT ${LIB_NAME}) - - + endif(build) \ No newline at end of file diff --git a/common/include/od/common/pipeline/ODAlgorithmBase.h b/common/include/od/common/pipeline/ODAlgorithmBase.h deleted file mode 100644 index ada40f86..00000000 --- a/common/include/od/common/pipeline/ODAlgorithmBase.h +++ /dev/null @@ -1,51 +0,0 @@ -/* -Copyright (c) 2015, Kripasindhu Sarkar -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions 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. - * Neither the name of the copyright holder(s) nor the - names of its contributors may 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 COPYRIGHT HOLDER 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. -*/ -// -// Created by sarkar on 12.06.15. -// -#pragma once -#include <boost/filesystem.hpp> - -namespace od -{ - - /** \brief The base of all algorithm classes - * - * \author Kripasindhu Sarkar - * - */ - class ODAlgorithmBase - { - public: - - virtual void parseParameterString(const std::string & parameter_string) - { } - - virtual void init() - { } - }; -} diff --git a/detectors/CMakeLists.txt b/detectors/CMakeLists.txt index 3126c695..2c0c8258 100644 --- a/detectors/CMakeLists.txt +++ b/detectors/CMakeLists.txt @@ -10,4 +10,5 @@ add_subdirectory("src/local2D") add_subdirectory("src/global2D") add_subdirectory("src/global3D") -install(DIRECTORY ${DETECTORS_INCLUDE_DIR}/od DESTINATION ${OD_INSTALL_INCLUDE_DIR}) \ No newline at end of file +install(DIRECTORY ${DETECTORS_INCLUDE_DIR}/od DESTINATION ${OD_INSTALL_INCLUDE_DIR}) +set(${OD_INSTALLED_LIBRARIES} PARENT_SCOPE) \ No newline at end of file diff --git a/detectors/src/global2D/CMakeLists.txt b/detectors/src/global2D/CMakeLists.txt index b5c151b7..4443e437 100644 --- a/detectors/src/global2D/CMakeLists.txt +++ b/detectors/src/global2D/CMakeLists.txt @@ -5,9 +5,6 @@ set(SUBSYS_DESC "global feature based detection in 2D images") set(SUBSYS_DEPS od_common ${OpenCV_LIBS} svmlight) set(build TRUE) -#PCL_SUBSYS_OPTION(build "${SUBSYS_NAME}" "${SUBSYS_DESC}" ON) -#PCL_SUBSYS_DEPEND(build "${SUBSYS_NAME}" DEPS ${SUBSYS_DEPS}) - if(build) @@ -24,7 +21,4 @@ if(build) OD_ADD_LIBRARY("${SUBSYS_NAME}" SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) - #PCL_MAKE_PKGCONFIG("${LIB_NAME}" "${SUBSYS_NAME}" "${SUBSYS_DESC}" "${SUBSYS_DEPS}" "" "" "" "") - - endif(build) \ No newline at end of file diff --git a/detectors/src/global3D/CMakeLists.txt b/detectors/src/global3D/CMakeLists.txt index e0fb2951..91dcd783 100644 --- a/detectors/src/global3D/CMakeLists.txt +++ b/detectors/src/global3D/CMakeLists.txt @@ -18,6 +18,4 @@ if(build) OD_ADD_LIBRARY("${SUBSYS_NAME}" SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) - #PCL_MAKE_PKGCONFIG("${LIB_NAME}" "${SUBSYS_NAME}" "${SUBSYS_DESC}" "${SUBSYS_DEPS}" "" "" "" "") - endif(build) \ No newline at end of file diff --git a/detectors/src/local2D/CMakeLists.txt b/detectors/src/local2D/CMakeLists.txt index f121586e..f302ee08 100644 --- a/detectors/src/local2D/CMakeLists.txt +++ b/detectors/src/local2D/CMakeLists.txt @@ -4,14 +4,9 @@ set(SUBSYS_DESC "The local feature matching based detector") set(SUBSYS_DEPS od_common ${PCL_LIBRARIES} ${VTK_LIBRARIES} ${OpenCV_LIBS} pugixml) set(build TRUE) -#PCL_SUBSYS_OPTION(build "${SUBSYS_NAME}" "${SUBSYS_DESC}" ON) -#PCL_SUBSYS_DEPEND(build "${SUBSYS_NAME}" DEPS ${SUBSYS_DEPS}) - if(build) - set(IMPL_INCLUDES "") - set(SOURCES "ODImageLocalMatching.cpp" "training/ODCADRecogTrainerSnapshotBased.cpp" @@ -32,6 +27,4 @@ if(build) OD_ADD_LIBRARY("${SUBSYS_NAME}" SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) - #PCL_MAKE_PKGCONFIG("${LIB_NAME}" "${SUBSYS_NAME}" "${SUBSYS_DESC}" "${SUBSYS_DEPS}" "" "" "" "") - endif(build) \ No newline at end of file diff --git a/examples/apps/version/opendetection.cpp b/examples/apps/version/opendetection.cpp index f1b41b7e..d5dc2388 100644 --- a/examples/apps/version/opendetection.cpp +++ b/examples/apps/version/opendetection.cpp @@ -2,7 +2,7 @@ // Created by sarkar on 03.06.15. // -#include "od_config.h" +#include "od_version.h" #include <iostream> int main (int argc, char *argv[]) From 2940a49a30a5b7e7864c001175022cf95f99b597 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Mon, 20 Jun 2016 14:11:33 +0200 Subject: [PATCH 28/79] renames some files and minor fixes --- CMakeLists.txt | 21 ++++++++++--------- cmake/ODDeb.cmake | 12 +++++++++++ ...od_dependency.cmake => ODDependency.cmake} | 0 cmake/{od_macros.cmake => ODMacros.cmake} | 0 cmake/{od_targets.cmake => ODTargets.cmake} | 0 ...ninstall.cmake.in => ODUninstall.cmake.in} | 0 cmake/{od_version.cmake => ODVersion.cmake} | 0 cmake/{od_version.h.in => ODVersion.h.in} | 0 common/CMakeLists.txt | 4 ++-- .../bindings/{svmlight.h => ODSvmlight.h} | 0 .../bindings/{svmlight.cpp => ODSvmlight.cpp} | 2 +- .../od/detectors/global2D/ODFaceRecognizer.h | 4 ++-- .../global2D/detection/ODHOGDetector.h | 2 +- .../global2D/training/ODHOGTrainer.h | 6 +++--- .../detection/ODCADRecognizer2DLocal.h | 4 +--- .../simple_ransac_detection/RobustMatcher.h | 6 +++--- .../simple_ransac_detection/RobustMatcher.cpp | 18 ++++++++++++++++ 17 files changed, 54 insertions(+), 25 deletions(-) create mode 100644 cmake/ODDeb.cmake rename cmake/{od_dependency.cmake => ODDependency.cmake} (100%) rename cmake/{od_macros.cmake => ODMacros.cmake} (100%) rename cmake/{od_targets.cmake => ODTargets.cmake} (100%) rename cmake/{cmake_uninstall.cmake.in => ODUninstall.cmake.in} (100%) rename cmake/{od_version.cmake => ODVersion.cmake} (100%) rename cmake/{od_version.h.in => ODVersion.h.in} (100%) rename common/include/od/common/bindings/{svmlight.h => ODSvmlight.h} (100%) rename common/src/bindings/{svmlight.cpp => ODSvmlight.cpp} (99%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f5ff331..ddbe1675 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,19 +11,21 @@ set(CMAKE_MODULE_PATH ${OD_CMAKE_DIR}/modules) set(CMAKE_3RDPARTY_DIR ${OD_SOURCE_DIR}/3rdparty) # Initialize versioning -include(${OD_CMAKE_DIR}/od_version.cmake) +include(${OD_CMAKE_DIR}/ODVersion.cmake) set(OD_VERSION ${OD_MAJOR_VERSION}.${OD_MINOR_VERSION}) set(OD_VERSION_DETAILED ${OD_MAJOR_VERSION}.${OD_MINOR_VERSION}.${OD_BUILD_VERSION}) +# Set up macros and configurations +include(${OD_CMAKE_DIR}/ODTargets.cmake) +include(${OD_CMAKE_DIR}/ODDependency.cmake) +include(${OD_CMAKE_DIR}/ODMacros.cmake) +include(${OD_CMAKE_DIR}/ODDeb.cmake) + # Prepare configuration files -configure_file(${OD_CMAKE_DIR}/od_version.h.in ${OD_BINARY_DIR}/generated/od_version.h) +configure_file(${OD_CMAKE_DIR}/ODVersion.h.in ${OD_BINARY_DIR}/generated/od_version.h) +install(FILES ${OD_BINARY_DIR}/generated/od_version.h DESTINATION ${OD_INSTALL_INCLUDE_DIR}) configure_file(${OD_CMAKE_DIR}/ODConfig.cmake.in ${OD_BINARY_DIR}/FindOD.cmake) -# Set up macros and configurations -include(${OD_CMAKE_DIR}/od_targets.cmake) -include(${OD_CMAKE_DIR}/od_dependency.cmake) -include(${OD_CMAKE_DIR}/od_macros.cmake) - # Optional parameters option(WITH_DOCUMENTATION "Build the OD documentation" ON) option(WITH_GPU "Build GPU components of the project" ON) @@ -49,9 +51,8 @@ install(FILES ${OD_BINARY_DIR}/FindOD.cmake DESTINATION ${OD_CMAKE_INSTALL_DIR}) # uninstall target configure_file( - "${OD_CMAKE_DIR}/cmake_uninstall.cmake.in" + "${OD_CMAKE_DIR}/ODUninstall.cmake.in" "${OD_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) -add_custom_target(uninstall - COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) \ No newline at end of file +add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) \ No newline at end of file diff --git a/cmake/ODDeb.cmake b/cmake/ODDeb.cmake new file mode 100644 index 00000000..af1d8e48 --- /dev/null +++ b/cmake/ODDeb.cmake @@ -0,0 +1,12 @@ +set(CPACK_GENERATOR "DEB") +set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Giacomo Dabisias") +set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "Opendetection library installation package") +set(CPACK_DEBIAN_PACKAGE_VERSION {OD_MAJOR_VERSION}.${OD_MINOR_VERSION}.${OD_BUILD_VERSION}) + +# Set the correct architecture for the deb +execute_process(COMMAND dpkg --print-architecture COMMAND tr -d '\n' OUTPUT_VARIABLE ARCHITECTURE) +if(ARCHITECTURE) + set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE ${ARCHITECTURE}) +endif() + +include(CPack) \ No newline at end of file diff --git a/cmake/od_dependency.cmake b/cmake/ODDependency.cmake similarity index 100% rename from cmake/od_dependency.cmake rename to cmake/ODDependency.cmake diff --git a/cmake/od_macros.cmake b/cmake/ODMacros.cmake similarity index 100% rename from cmake/od_macros.cmake rename to cmake/ODMacros.cmake diff --git a/cmake/od_targets.cmake b/cmake/ODTargets.cmake similarity index 100% rename from cmake/od_targets.cmake rename to cmake/ODTargets.cmake diff --git a/cmake/cmake_uninstall.cmake.in b/cmake/ODUninstall.cmake.in similarity index 100% rename from cmake/cmake_uninstall.cmake.in rename to cmake/ODUninstall.cmake.in diff --git a/cmake/od_version.cmake b/cmake/ODVersion.cmake similarity index 100% rename from cmake/od_version.cmake rename to cmake/ODVersion.cmake diff --git a/cmake/od_version.h.in b/cmake/ODVersion.h.in similarity index 100% rename from cmake/od_version.h.in rename to cmake/ODVersion.h.in diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 3bbb8485..e5e93337 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -22,7 +22,7 @@ if(build) "src/pipeline/ODDetection.cpp" "src/utils/ODUtils.cpp" "src/utils/ODFeatureDetector2D.cpp" - "src/bindings/svmlight.cpp" + "src/bindings/ODSvmlight.cpp" ) include_directories(${COMMON_INCLUDE_DIR}) @@ -30,6 +30,6 @@ if(build) OD_ADD_LIBRARY(${SUBSYS_NAME} SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) install(DIRECTORY ${COMMON_INCLUDE_DIR}/od DESTINATION ${OD_INSTALL_INCLUDE_DIR} COMPONENT ${LIB_NAME}) - + endif(build) \ No newline at end of file diff --git a/common/include/od/common/bindings/svmlight.h b/common/include/od/common/bindings/ODSvmlight.h similarity index 100% rename from common/include/od/common/bindings/svmlight.h rename to common/include/od/common/bindings/ODSvmlight.h diff --git a/common/src/bindings/svmlight.cpp b/common/src/bindings/ODSvmlight.cpp similarity index 99% rename from common/src/bindings/svmlight.cpp rename to common/src/bindings/ODSvmlight.cpp index 171435fa..82411ccc 100644 --- a/common/src/bindings/svmlight.cpp +++ b/common/src/bindings/ODSvmlight.cpp @@ -49,7 +49,7 @@ Unless required by applicable law or agreed to in writing, software distributed * */ -#include "od/common/bindings/svmlight.h" +#include "od/common/bindings/ODSvmlight.h" SVMlight::SVMlight(){ // Init variables diff --git a/detectors/include/od/detectors/global2D/ODFaceRecognizer.h b/detectors/include/od/detectors/global2D/ODFaceRecognizer.h index b6dd1980..081eba75 100644 --- a/detectors/include/od/detectors/global2D/ODFaceRecognizer.h +++ b/detectors/include/od/detectors/global2D/ODFaceRecognizer.h @@ -116,7 +116,7 @@ namespace od void read_csv(const std::string & file_name, std::vector<cv::Mat> & images, std::vector<int> & labels, const std::string & separator = std::string(";")); }; - /** \example objectdetector/od_image_facerecog.cpp - */ + } + } diff --git a/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h b/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h index 25023ed9..0bf367da 100644 --- a/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h +++ b/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h @@ -32,7 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/common/utils/ODUtils.h" #include "od/common/utils/ODFeatureDetector2D.h" #include "od/common/utils/ODShared_pointers.h" -#include "od/common/bindings/svmlight.h" +#include "od/common/bindings/ODSvmlight.h" #include <iostream> #include <opencv2/objdetect.hpp> diff --git a/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h b/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h index b6d6049b..39c0c4dd 100644 --- a/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h +++ b/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h @@ -31,7 +31,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <opencv2/opencv.hpp> #include <fstream> #include "od/common/pipeline/ODTrainer.h" -#include "od/common/bindings/svmlight.h" +#include "od/common/bindings/ODSvmlight.h" namespace od { @@ -223,7 +223,7 @@ namespace od std::string descriptor_vector_hard_; }; - /** \example objectdetector/od_hog_train.cpp - */ + } + } diff --git a/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h b/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h index b4afbc34..c7c2bc70 100644 --- a/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h +++ b/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h @@ -141,9 +141,7 @@ namespace od shared_ptr<ODFeatureDetector2D> feature_detector_; }; - /** \example objectdetector/od_image_cadrecog_camera.cpp - * \example objectdetector/od_image_cadrecog_files.cpp - */ + } } diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/RobustMatcher.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/RobustMatcher.h index 8056b7d8..fa6f4af3 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/RobustMatcher.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/RobustMatcher.h @@ -30,13 +30,13 @@ namespace od { virtual ~RobustMatcher(); // Set the feature detector - void setFeatureDetector(const cv::Ptr<cv::FeatureDetector> & detect) { detector_ = detect; } + void setFeatureDetector(const cv::Ptr<cv::FeatureDetector> & detect); // Set the descriptor extractor - void setDescriptorExtractor(const cv::Ptr<cv::DescriptorExtractor> & desc) { extractor_ = desc; } + void setDescriptorExtractor(const cv::Ptr<cv::DescriptorExtractor> & desc); // Set the matcher - void setDescriptorMatcher(const cv::Ptr<cv::DescriptorMatcher> & match) { matcher_ = match; } + void setDescriptorMatcher(const cv::Ptr<cv::DescriptorMatcher> & match); // Compute the keypoints of an image void computeKeyPoints( const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints); diff --git a/detectors/src/local2D/simple_ransac_detection/RobustMatcher.cpp b/detectors/src/local2D/simple_ransac_detection/RobustMatcher.cpp index f02ead49..90db345d 100644 --- a/detectors/src/local2D/simple_ransac_detection/RobustMatcher.cpp +++ b/detectors/src/local2D/simple_ransac_detection/RobustMatcher.cpp @@ -52,6 +52,24 @@ namespace od { } */ + // Set the feature detector + void RobustMatcher::setFeatureDetector(const cv::Ptr<cv::FeatureDetector> & detect) + { + detector_ = detect; + } + + // Set the descriptor extractor + void RobustMatcher::setDescriptorExtractor(const cv::Ptr<cv::DescriptorExtractor> & desc) + { + extractor_ = desc; + } + + // Set the matcher + void RobustMatcher::setDescriptorMatcher(const cv::Ptr<cv::DescriptorMatcher> & match) + { + matcher_ = match; + } + void RobustMatcher::instantiateMatcher(const Model & model, bool use_gpu) { From 216923d2d81e441eed8c95873ed4ce60dd971f96 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Mon, 20 Jun 2016 14:38:45 +0200 Subject: [PATCH 29/79] finished renaming files for the new naming scheme --- .../detection/ODCADRecognizer2DLocal.h | 4 ++-- .../{CsvReader.h => ODCsvReader.h} | 2 +- .../{CsvWriter.h => ODCsvWriter.h} | 2 +- .../{Mesh.h => ODMesh.h} | 2 +- .../{Model.h => ODModel.h} | 2 +- ...elRegistration.h => ODModelRegistration.h} | 0 .../{PnPProblem.h => ODPnPProblem.h} | 4 ++-- .../{RobustMatcher.h => ODRobustMatcher.h} | 4 ++-- .../{Utils.h => ODUtils.h} | 9 ++++--- detectors/src/local2D/CMakeLists.txt | 16 ++++++------- .../{CsvReader.cpp => ODCsvReader.cpp} | 2 +- .../{CsvWriter.cpp => ODCsvWriter.cpp} | 2 +- .../{Mesh.cpp => ODMesh.cpp} | 2 +- .../{Model.cpp => ODModel.cpp} | 2 +- ...gistration.cpp => ODModelRegistration.cpp} | 2 +- .../{PnPProblem.cpp => ODPnPProblem.cpp} | 2 +- ...{RobustMatcher.cpp => ODRobustMatcher.cpp} | 2 +- .../{Utils.cpp => ODUtils.cpp} | 2 +- .../gsoc2016_blog_giacomo.md | 24 +++++++++++++++++++ 19 files changed, 54 insertions(+), 31 deletions(-) rename detectors/include/od/detectors/local2D/simple_ransac_detection/{CsvReader.h => ODCsvReader.h} (94%) rename detectors/include/od/detectors/local2D/simple_ransac_detection/{CsvWriter.h => ODCsvWriter.h} (89%) rename detectors/include/od/detectors/local2D/simple_ransac_detection/{Mesh.h => ODMesh.h} (97%) rename detectors/include/od/detectors/local2D/simple_ransac_detection/{Model.h => ODModel.h} (96%) rename detectors/include/od/detectors/local2D/simple_ransac_detection/{ModelRegistration.h => ODModelRegistration.h} (100%) rename detectors/include/od/detectors/local2D/simple_ransac_detection/{PnPProblem.h => ODPnPProblem.h} (94%) rename detectors/include/od/detectors/local2D/simple_ransac_detection/{RobustMatcher.h => ODRobustMatcher.h} (96%) rename detectors/include/od/detectors/local2D/simple_ransac_detection/{Utils.h => ODUtils.h} (90%) rename detectors/src/local2D/simple_ransac_detection/{CsvReader.cpp => ODCsvReader.cpp} (97%) rename detectors/src/local2D/simple_ransac_detection/{CsvWriter.cpp => ODCsvWriter.cpp} (95%) rename detectors/src/local2D/simple_ransac_detection/{Mesh.cpp => ODMesh.cpp} (97%) rename detectors/src/local2D/simple_ransac_detection/{Model.cpp => ODModel.cpp} (98%) rename detectors/src/local2D/simple_ransac_detection/{ModelRegistration.cpp => ODModelRegistration.cpp} (94%) rename detectors/src/local2D/simple_ransac_detection/{PnPProblem.cpp => ODPnPProblem.cpp} (99%) rename detectors/src/local2D/simple_ransac_detection/{RobustMatcher.cpp => ODRobustMatcher.cpp} (99%) rename detectors/src/local2D/simple_ransac_detection/{Utils.cpp => ODUtils.cpp} (99%) diff --git a/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h b/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h index c7c2bc70..bc7a9015 100644 --- a/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h +++ b/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h @@ -30,8 +30,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #pragma once #include "od/detectors/local2D/ODImageLocalMatching.h" #include "od/common/pipeline/ODScene.h" -#include "od/detectors/local2D/simple_ransac_detection/Mesh.h" -#include "od/detectors/local2D/simple_ransac_detection/RobustMatcher.h" +#include "od/detectors/local2D/simple_ransac_detection/ODMesh.h" +#include "od/detectors/local2D/simple_ransac_detection/ODRobustMatcher.h" #include <iostream> diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvReader.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/ODCsvReader.h similarity index 94% rename from detectors/include/od/detectors/local2D/simple_ransac_detection/CsvReader.h rename to detectors/include/od/detectors/local2D/simple_ransac_detection/ODCsvReader.h index cf530ce9..574c2bf0 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvReader.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/ODCsvReader.h @@ -4,7 +4,7 @@ #include <opencv2/core/core.hpp> #include <string> #include <stdlib.h> -#include "od/detectors/local2D/simple_ransac_detection/Utils.h" +#include "od/detectors/local2D/simple_ransac_detection/ODUtils.h" namespace od { diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvWriter.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/ODCsvWriter.h similarity index 89% rename from detectors/include/od/detectors/local2D/simple_ransac_detection/CsvWriter.h rename to detectors/include/od/detectors/local2D/simple_ransac_detection/ODCsvWriter.h index 3d249ea6..79491d25 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvWriter.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/ODCsvWriter.h @@ -2,7 +2,7 @@ #include <iostream> #include <fstream> #include <opencv2/core/core.hpp> -#include "od/detectors/local2D/simple_ransac_detection/Utils.h" +#include "od/detectors/local2D/simple_ransac_detection/ODUtils.h" namespace od { diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/Mesh.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/ODMesh.h similarity index 97% rename from detectors/include/od/detectors/local2D/simple_ransac_detection/Mesh.h rename to detectors/include/od/detectors/local2D/simple_ransac_detection/ODMesh.h index 71304db1..2cd0a0d4 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/Mesh.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/ODMesh.h @@ -8,7 +8,7 @@ #pragma once #include <iostream> #include <opencv2/core/core.hpp> -#include "od/detectors/local2D/simple_ransac_detection/CsvReader.h" +#include "od/detectors/local2D/simple_ransac_detection/ODCsvReader.h" namespace od { diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/Model.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/ODModel.h similarity index 96% rename from detectors/include/od/detectors/local2D/simple_ransac_detection/Model.h rename to detectors/include/od/detectors/local2D/simple_ransac_detection/ODModel.h index 6d2d5098..b013dbf3 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/Model.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/ODModel.h @@ -12,7 +12,7 @@ #include <pugixml.hpp> #include <sstream> #include <boost/algorithm/string.hpp> -#include "od/detectors/local2D/simple_ransac_detection/CsvWriter.h" +#include "od/detectors/local2D/simple_ransac_detection/ODCsvWriter.h" namespace od { diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/ModelRegistration.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/ODModelRegistration.h similarity index 100% rename from detectors/include/od/detectors/local2D/simple_ransac_detection/ModelRegistration.h rename to detectors/include/od/detectors/local2D/simple_ransac_detection/ODModelRegistration.h diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/PnPProblem.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/ODPnPProblem.h similarity index 94% rename from detectors/include/od/detectors/local2D/simple_ransac_detection/PnPProblem.h rename to detectors/include/od/detectors/local2D/simple_ransac_detection/ODPnPProblem.h index 71add135..a9d47542 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/PnPProblem.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/ODPnPProblem.h @@ -10,8 +10,8 @@ #include <opencv2/calib3d/calib3d.hpp> #include <iostream> #include <sstream> -#include "od/detectors/local2D/simple_ransac_detection/Mesh.h" -#include "od/detectors/local2D/simple_ransac_detection/ModelRegistration.h" +#include "od/detectors/local2D/simple_ransac_detection/ODMesh.h" +#include "od/detectors/local2D/simple_ransac_detection/ODModelRegistration.h" #include "od/common/utils/ODShared_pointers.h" class Mesh; diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/RobustMatcher.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/ODRobustMatcher.h similarity index 96% rename from detectors/include/od/detectors/local2D/simple_ransac_detection/RobustMatcher.h rename to detectors/include/od/detectors/local2D/simple_ransac_detection/ODRobustMatcher.h index fa6f4af3..871e1783 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/RobustMatcher.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/ODRobustMatcher.h @@ -14,8 +14,8 @@ #include <opencv2/ml.hpp> #include "od/common/utils/ODFeatureDetector2D.h" #include "od/common/utils/ODUtils.h" -#include "od/detectors/local2D/simple_ransac_detection/Model.h" -#include "od/detectors/local2D/simple_ransac_detection/Utils.h" +#include "od/detectors/local2D/simple_ransac_detection/ODModel.h" +#include "od/detectors/local2D/simple_ransac_detection/ODUtils.h" namespace od { diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/Utils.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/ODUtils.h similarity index 90% rename from detectors/include/od/detectors/local2D/simple_ransac_detection/Utils.h rename to detectors/include/od/detectors/local2D/simple_ransac_detection/ODUtils.h index 72d3f48d..c2babe48 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/Utils.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/ODUtils.h @@ -6,11 +6,10 @@ */ #pragma once #include <iostream> -#include <sys/time.h> -#include "od/detectors/local2D/simple_ransac_detection/PnPProblem.h" -#include "od/detectors/local2D/simple_ransac_detection/ModelRegistration.h" -#include "od/detectors/local2D/simple_ransac_detection/Model.h" -#include "od/detectors/local2D/simple_ransac_detection/Mesh.h" +#include "od/detectors/local2D/simple_ransac_detection/ODPnPProblem.h" +#include "od/detectors/local2D/simple_ransac_detection/ODModelRegistration.h" +#include "od/detectors/local2D/simple_ransac_detection/ODModel.h" +#include "od/detectors/local2D/simple_ransac_detection/ODMesh.h" #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/calib3d/calib3d.hpp> diff --git a/detectors/src/local2D/CMakeLists.txt b/detectors/src/local2D/CMakeLists.txt index f302ee08..7816b64d 100644 --- a/detectors/src/local2D/CMakeLists.txt +++ b/detectors/src/local2D/CMakeLists.txt @@ -11,14 +11,14 @@ if(build) "ODImageLocalMatching.cpp" "training/ODCADRecogTrainerSnapshotBased.cpp" "detection/ODCADRecognizer2DLocal.cpp" - "simple_ransac_detection/CsvReader.cpp" - "simple_ransac_detection/CsvWriter.cpp" - "simple_ransac_detection/ModelRegistration.cpp" - "simple_ransac_detection/Mesh.cpp" - "simple_ransac_detection/Model.cpp" - "simple_ransac_detection/PnPProblem.cpp" - "simple_ransac_detection/Utils.cpp" - "simple_ransac_detection/RobustMatcher.cpp" + "simple_ransac_detection/ODCsvReader.cpp" + "simple_ransac_detection/ODCsvWriter.cpp" + "simple_ransac_detection/ODModelRegistration.cpp" + "simple_ransac_detection/ODMesh.cpp" + "simple_ransac_detection/ODModel.cpp" + "simple_ransac_detection/ODPnPProblem.cpp" + "simple_ransac_detection/ODUtils.cpp" + "simple_ransac_detection/ODRobustMatcher.cpp" ) include_directories(${DETECTORS_INCLUDE_DIR}) diff --git a/detectors/src/local2D/simple_ransac_detection/CsvReader.cpp b/detectors/src/local2D/simple_ransac_detection/ODCsvReader.cpp similarity index 97% rename from detectors/src/local2D/simple_ransac_detection/CsvReader.cpp rename to detectors/src/local2D/simple_ransac_detection/ODCsvReader.cpp index 887a5b52..fa699374 100644 --- a/detectors/src/local2D/simple_ransac_detection/CsvReader.cpp +++ b/detectors/src/local2D/simple_ransac_detection/ODCsvReader.cpp @@ -1,4 +1,4 @@ -#include "od/detectors/local2D/simple_ransac_detection/CsvReader.h" +#include "od/detectors/local2D/simple_ransac_detection/ODCsvReader.h" namespace od { diff --git a/detectors/src/local2D/simple_ransac_detection/CsvWriter.cpp b/detectors/src/local2D/simple_ransac_detection/ODCsvWriter.cpp similarity index 95% rename from detectors/src/local2D/simple_ransac_detection/CsvWriter.cpp rename to detectors/src/local2D/simple_ransac_detection/ODCsvWriter.cpp index 6737bd7d..aec80e67 100644 --- a/detectors/src/local2D/simple_ransac_detection/CsvWriter.cpp +++ b/detectors/src/local2D/simple_ransac_detection/ODCsvWriter.cpp @@ -1,4 +1,4 @@ -#include "od/detectors/local2D/simple_ransac_detection/CsvWriter.h" +#include "od/detectors/local2D/simple_ransac_detection/ODCsvWriter.h" namespace od { diff --git a/detectors/src/local2D/simple_ransac_detection/Mesh.cpp b/detectors/src/local2D/simple_ransac_detection/ODMesh.cpp similarity index 97% rename from detectors/src/local2D/simple_ransac_detection/Mesh.cpp rename to detectors/src/local2D/simple_ransac_detection/ODMesh.cpp index b545d6ad..72f66462 100644 --- a/detectors/src/local2D/simple_ransac_detection/Mesh.cpp +++ b/detectors/src/local2D/simple_ransac_detection/ODMesh.cpp @@ -5,7 +5,7 @@ * Author: edgar */ -#include "od/detectors/local2D/simple_ransac_detection/Mesh.h" +#include "od/detectors/local2D/simple_ransac_detection/ODMesh.h" namespace od { diff --git a/detectors/src/local2D/simple_ransac_detection/Model.cpp b/detectors/src/local2D/simple_ransac_detection/ODModel.cpp similarity index 98% rename from detectors/src/local2D/simple_ransac_detection/Model.cpp rename to detectors/src/local2D/simple_ransac_detection/ODModel.cpp index 18d0132a..63c7deb6 100644 --- a/detectors/src/local2D/simple_ransac_detection/Model.cpp +++ b/detectors/src/local2D/simple_ransac_detection/ODModel.cpp @@ -6,7 +6,7 @@ */ -#include "od/detectors/local2D/simple_ransac_detection/Model.h" +#include "od/detectors/local2D/simple_ransac_detection/ODModel.h" namespace od { diff --git a/detectors/src/local2D/simple_ransac_detection/ModelRegistration.cpp b/detectors/src/local2D/simple_ransac_detection/ODModelRegistration.cpp similarity index 94% rename from detectors/src/local2D/simple_ransac_detection/ModelRegistration.cpp rename to detectors/src/local2D/simple_ransac_detection/ODModelRegistration.cpp index 7e04ed72..91e51204 100644 --- a/detectors/src/local2D/simple_ransac_detection/ModelRegistration.cpp +++ b/detectors/src/local2D/simple_ransac_detection/ODModelRegistration.cpp @@ -5,7 +5,7 @@ * Author: edgar */ -#include "od/detectors/local2D/simple_ransac_detection/ModelRegistration.h" +#include "od/detectors/local2D/simple_ransac_detection/ODModelRegistration.h" namespace od { diff --git a/detectors/src/local2D/simple_ransac_detection/PnPProblem.cpp b/detectors/src/local2D/simple_ransac_detection/ODPnPProblem.cpp similarity index 99% rename from detectors/src/local2D/simple_ransac_detection/PnPProblem.cpp rename to detectors/src/local2D/simple_ransac_detection/ODPnPProblem.cpp index 98de1072..4c27add5 100644 --- a/detectors/src/local2D/simple_ransac_detection/PnPProblem.cpp +++ b/detectors/src/local2D/simple_ransac_detection/ODPnPProblem.cpp @@ -5,7 +5,7 @@ * Author: Edgar Riba */ -#include "od/detectors/local2D/simple_ransac_detection/PnPProblem.h" +#include "od/detectors/local2D/simple_ransac_detection/ODPnPProblem.h" namespace od { diff --git a/detectors/src/local2D/simple_ransac_detection/RobustMatcher.cpp b/detectors/src/local2D/simple_ransac_detection/ODRobustMatcher.cpp similarity index 99% rename from detectors/src/local2D/simple_ransac_detection/RobustMatcher.cpp rename to detectors/src/local2D/simple_ransac_detection/ODRobustMatcher.cpp index 90db345d..acb8e1ab 100644 --- a/detectors/src/local2D/simple_ransac_detection/RobustMatcher.cpp +++ b/detectors/src/local2D/simple_ransac_detection/ODRobustMatcher.cpp @@ -5,7 +5,7 @@ * Author: eriba */ -#include "od/detectors/local2D/simple_ransac_detection/RobustMatcher.h" +#include "od/detectors/local2D/simple_ransac_detection/ODRobustMatcher.h" namespace od { diff --git a/detectors/src/local2D/simple_ransac_detection/Utils.cpp b/detectors/src/local2D/simple_ransac_detection/ODUtils.cpp similarity index 99% rename from detectors/src/local2D/simple_ransac_detection/Utils.cpp rename to detectors/src/local2D/simple_ransac_detection/ODUtils.cpp index a4ef573e..ac15b5aa 100644 --- a/detectors/src/local2D/simple_ransac_detection/Utils.cpp +++ b/detectors/src/local2D/simple_ransac_detection/ODUtils.cpp @@ -5,7 +5,7 @@ * Author: Edgar Riba */ -#include "od/detectors/local2D/simple_ransac_detection/Utils.h" +#include "od/detectors/local2D/simple_ransac_detection/ODUtils.h" namespace od { diff --git a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md index 25056e38..4edba641 100644 --- a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md +++ b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md @@ -178,3 +178,27 @@ The following step took quite a bit in order to have everything work well. Until While restructuring the code I also fixed again some coding style issues, but I still have to go through the code a few more times in order to finish fixing everything. The next step is as previously mentioned, to check the compilation with the svmlight option. +##Cmake improvments 20/06/15## + +I created a **FindOD.cmake** which can be used by other libraries to find the include directories path, the library path and the library names. The variables which get set are: + +- OD_INCLUDE_DIRS +- OD_LIBRARY_PATH +- OD_LIBRARIES + +There variables can be used in any project by using: + +@code +find_package(OD REQUIRED) +include_directories(${OD_INCLUDE_DIRS}) +link_directories(${OD_LIBRARY_PATH}) +target_link_libraries(example_target ${OD_LIBRARIES}) +@endcode + +The **FindOD.cmake** is generated automatically starting from the **${CMAKE_INSTALL_PREFIX}** variable so there is no absolute path and it is installed in ****${CMAKE_INSTALL_PREFIX}/lib/cmake/** . + +I added then an **uninstall** target to delete all the installed files if necessary. This custom target is standard and can be found easily on the internet. + +Another important missing thing was the creation of a .deb package which can be useful to install the library on other systems. This package has been created using the **Cpack** utility provided by cmake. By just setting some basic variables it is possible to create e complete .deb file which can then be installed using the usual *dpkg -i package.deb* command. + +All files in the library have been renamed according to a common scheme which consists in a Prefix "OD" followed by the first charachter of the file name uppercase. \ No newline at end of file From 49752c0418fa972ef4340836c644bdd25ae6e8f8 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Tue, 21 Jun 2016 15:51:24 +0200 Subject: [PATCH 30/79] some minor fixes --- .../include/od/common/bindings/ODSvmlight.h | 2 +- .../include/od/common/pipeline/ODDetection.h | 6 +-- common/include/od/common/pipeline/ODScene.h | 23 +++++----- common/src/pipeline/ODDetection.cpp | 12 ++--- common/src/utils/ODFeatureDetector2D.cpp | 13 +++--- common/src/utils/ODUtils.cpp | 20 ++++---- .../detection/ODCADDetector3DGlobal.hpp | 18 +++----- .../misc/detection/ODDetectorMultiAlgo.hpp | 13 +++--- .../global2D/detection/ODCascadeDetector.h | 8 ++-- .../global2D/detection/ODHOGDetector.h | 6 +-- .../global2D/training/ODHOGTrainer.h | 4 +- .../detectors/local2D/ODImageLocalMatching.h | 8 ++-- .../detection/ODCADRecognizer2DLocal.h | 2 +- .../simple_ransac_detection/ODCsvWriter.h | 2 +- .../local2D/simple_ransac_detection/ODMesh.h | 23 ++++------ .../simple_ransac_detection/ODPnPProblem.h | 2 +- .../simple_ransac_detection/ODRobustMatcher.h | 37 +++++++-------- .../local2D/simple_ransac_detection/ODUtils.h | 2 +- .../training/ODCADRecogTrainerSnapshotBased.h | 20 ++++---- .../src/global2D/detection/ODHOGDetector.cpp | 5 ++ .../detection/ODCADRecognizer2DLocal.cpp | 4 +- .../simple_ransac_detection/ODMesh.cpp | 46 ++++++++++++++----- .../simple_ransac_detection/ODPnPProblem.cpp | 2 +- .../ODRobustMatcher.cpp | 46 ++++++++----------- 24 files changed, 167 insertions(+), 157 deletions(-) diff --git a/common/include/od/common/bindings/ODSvmlight.h b/common/include/od/common/bindings/ODSvmlight.h index 880d0d90..59530efb 100644 --- a/common/include/od/common/bindings/ODSvmlight.h +++ b/common/include/od/common/bindings/ODSvmlight.h @@ -41,7 +41,7 @@ Unless required by applicable law or agreed to in writing, software distributed */ /** - * @file: svmlight.h + * @file: ODSvmlight.h * @author: Jan Hendriks (dahoc3150 [at] gmail.com) * @date: Created on 11. Mai 2011 * @brief: Wrapper interface for SVMlight, diff --git a/common/include/od/common/pipeline/ODDetection.h b/common/include/od/common/pipeline/ODDetection.h index 54582362..4b24a559 100644 --- a/common/include/od/common/pipeline/ODDetection.h +++ b/common/include/od/common/pipeline/ODDetection.h @@ -56,7 +56,7 @@ namespace od virtual ~ODDetection() {} - ODDetection(const DetectionType & type_ = OD_DETECTION_NULL, const std::string & id_ = "", double confidence_ = 1.0); + ODDetection(const DetectionType & type = OD_DETECTION_NULL, const std::string & id = std::string(""), double confidence = 1.0); void printSelf(); @@ -93,7 +93,7 @@ namespace od virtual ~ODDetection2D(){} - ODDetection2D(const DetectionType & type_ = OD_DETECTION_NULL, const std::string & id_ = "", double confidence_ = 1.0); + ODDetection2D(const DetectionType & type = OD_DETECTION_NULL, const std::string & id = std::string(""), double confidence = 1.0); const Eigen::Vector3d & getLocation() const; @@ -123,7 +123,7 @@ namespace od virtual ~ODDetection3D(){} - ODDetection3D(const DetectionType & type_ = OD_DETECTION_NULL, const std::string & id_ = "", double confidence_ = 1.0); + ODDetection3D(const DetectionType & type = OD_DETECTION_NULL, const std::string & id = std::string(""), double confidence = 1.0); const Eigen::Vector4d & getLocation() const; diff --git a/common/include/od/common/pipeline/ODScene.h b/common/include/od/common/pipeline/ODScene.h index afaf3459..b7b5552a 100644 --- a/common/include/od/common/pipeline/ODScene.h +++ b/common/include/od/common/pipeline/ODScene.h @@ -33,6 +33,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <opencv2/highgui/highgui.hpp> #include <pcl/point_cloud.h> #include <pcl/io/pcd_io.h> +#include "od/common/utils/ODShared_pointers.h" namespace od { @@ -47,7 +48,7 @@ namespace od virtual void * getData() = 0; - std::string const & getPath() const; + const std::string & getPath() const; protected: @@ -98,24 +99,24 @@ namespace od public: - ODScenePointCloud(const typename pcl::PointCloud<PointType>::Ptr & point_cloud); + ODScenePointCloud(const shared_ptr<pcl::PointCloud<PointType> > point_cloud); ODScenePointCloud(const std::string & point_cloud_file); - ODScenePointCloud(): point_cloud_(new typename pcl::PointCloud<PointType>()){} + ODScenePointCloud(): point_cloud_(new pcl::PointCloud<PointType>()){} - const typename pcl::PointCloud<PointType>::Ptr & getPointCloud() const; - typename pcl::PointCloud<PointType>::Ptr & getPointCloudRef() const; - void setPointCloud(const typename pcl::PointCloud<PointType>::Ptr & point_cloud_); + shared_ptr<pcl::PointCloud<PointType> > getPointCloud() const; + shared_ptr<pcl::PointCloud<PointType> > getPointCloudRef() const; + void setPointCloud(const shared_ptr<pcl::PointCloud<PointType> > point_cloud_); void * getData(); protected: - typename pcl::PointCloud<PointType>::Ptr point_cloud_; + shared_ptr<pcl::PointCloud<PointType> > point_cloud_; }; template <typename PointType> - ODScenePointCloud<PointType>::ODScenePointCloud(const typename pcl::PointCloud<PointType>::Ptr & point_cloud) + ODScenePointCloud<PointType>::ODScenePointCloud(const shared_ptr<pcl::PointCloud<PointType> > point_cloud) { point_cloud_ = point_cloud; } @@ -132,19 +133,19 @@ namespace od } template <typename PointType> - const typename pcl::PointCloud<PointType>::Ptr & ODScenePointCloud<PointType>::getPointCloud() const + shared_ptr<pcl::PointCloud<PointType> > ODScenePointCloud<PointType>::getPointCloud() const { return point_cloud_; } template <typename PointType> - typename pcl::PointCloud<PointType>::Ptr & ODScenePointCloud<PointType>::getPointCloudRef() const + shared_ptr<pcl::PointCloud<PointType> > ODScenePointCloud<PointType>::getPointCloudRef() const { return point_cloud_; } template <typename PointType> - void ODScenePointCloud<PointType>::setPointCloud(const typename pcl::PointCloud<PointType>::Ptr & point_cloud) + void ODScenePointCloud<PointType>::setPointCloud(const shared_ptr<pcl::PointCloud<PointType> > point_cloud) { point_cloud_ = point_cloud; } diff --git a/common/src/pipeline/ODDetection.cpp b/common/src/pipeline/ODDetection.cpp index 84183e5b..7f323195 100644 --- a/common/src/pipeline/ODDetection.cpp +++ b/common/src/pipeline/ODDetection.cpp @@ -3,8 +3,8 @@ namespace od { - ODDetection::ODDetection(const DetectionType & type_, const std::string & id_, double confidence_) : - type_(type_), id_(id_), confidence_(confidence_) {} + ODDetection::ODDetection(const DetectionType & type, const std::string & id, double confidence) : + type_(type), id_(id), confidence_(confidence) {} void ODDetection::printSelf() { @@ -47,8 +47,8 @@ namespace od - ODDetection2D::ODDetection2D(const DetectionType & type_, const std::string & id_ , double confidence_ ) : - ODDetection(type_, id_, confidence_) + ODDetection2D::ODDetection2D(const DetectionType & type, const std::string & id , double confidence) : + ODDetection(type, id, confidence) { location_2d_ = Eigen::Vector3d::UnitZ(); } @@ -85,8 +85,8 @@ namespace od - ODDetection3D::ODDetection3D(const DetectionType & type_, const std::string & id_, double confidence_) : - ODDetection(type_, id_, confidence_) + ODDetection3D::ODDetection3D(const DetectionType & type, const std::string & id, double confidence) : + ODDetection(type, id, confidence) { location_3d_ = Eigen::Vector4d::UnitW(); orientation_.setIdentity(); diff --git a/common/src/utils/ODFeatureDetector2D.cpp b/common/src/utils/ODFeatureDetector2D.cpp index 34303091..3753ea79 100644 --- a/common/src/utils/ODFeatureDetector2D.cpp +++ b/common/src/utils/ODFeatureDetector2D.cpp @@ -12,10 +12,10 @@ namespace od mode_ = SIFT; if(use_gpu) { - if(feature_type == "ORB") { + if(feature_type.compare("ORB") == 0) { mode_ = ORB_GPU; feature_detector_ = cv::cuda::ORB::create(); - }else if(feature_type == "SIFT") { + }else if(feature_type.compare("SIFT") == 0) { mode_ = SIFT_GPU; sift_gpu_ = new SiftGPU(); char *argv[] = {(char *) "-fo", (char *) "-1", (char *) "-v", (char *) "1"}; @@ -25,13 +25,13 @@ namespace od std::cout << "FATAL ERROR cannot create SIFTGPU context" << std::endl; } } else { - if(feature_type == "SIFT") { + if(feature_type.compare("SIFT") == 0) { mode_ = SIFT; feature_detector_ = cv::xfeatures2d::SIFT::create(); - } else if(feature_type == "ORB") { + } else if(feature_type.compare("ORB") == 0) { mode_ = ORB; feature_detector_ = cv::ORB::create(); - } else if(feature_type == "SURF") { + } else if(feature_type.compare("SURF") == 0) { mode_ = SURF; feature_detector_ = cv::xfeatures2d::SURF::create(); } @@ -66,7 +66,7 @@ namespace od int argc = sizeof(argv) / sizeof(char *); sift_gpu_->ParseParam(argc, argv); if(sift_gpu_->CreateContextGL() != SiftGPU::SIFTGPU_FULL_SUPPORTED) - std::cout << "FATAL ERROR cannot create SIFTGPU context"; + std::cout << "FATAL ERROR cannot create SIFTGPU context" << std::endl; break; } @@ -201,7 +201,6 @@ namespace od sift_gpu_->SaveSIFT(path.c_str()); } else { //DO NOTHING! IMPLEMENT LATER - } } } diff --git a/common/src/utils/ODUtils.cpp b/common/src/utils/ODUtils.cpp index 2b4550f4..355ac05e 100644 --- a/common/src/utils/ODUtils.cpp +++ b/common/src/utils/ODUtils.cpp @@ -94,7 +94,7 @@ namespace od std::istringstream iss(line); std::string tok1; iss >> tok1; - if(tok1 == "mtllib") + if(tok1.compare(std::string("mtllib")) == 0) { std::string tok2; @@ -106,15 +106,15 @@ namespace od { std::istringstream issmtl(linemtl); issmtl >> tok1; - if(tok1 == "map_Kd") + if(tok1.compare(std::string("map_Kd")) == 0) { issmtl >> tok2; - return input_dir + "/" + tok2; + return input_dir + std::string("/") + tok2; } } } } - return ""; + return std::string(""); } cv::Scalar getHashedColor(const std::string & name, int offset) @@ -158,9 +158,9 @@ namespace od //check if its a directory, then get models in it if(boost::filesystem::is_directory(*itr)) { #if BOOST_FILESYSTEM_VERSION == 3 - std::string so_far = rel_path_so_far + (itr->path().filename()).string() + "/"; + std::string so_far = rel_path_so_far + (itr->path().filename()).string() + std::string("/"); #else - std::string so_far = rel_path_so_far + (itr->path ()).filename () + "/"; + std::string so_far = rel_path_so_far + (itr->path ()).filename () + std::string("/"); #endif boost::filesystem::path curr_path = itr->path(); @@ -205,9 +205,9 @@ namespace od //check if its a directory, then get models in it if(boost::filesystem::is_directory(*itr)) { #if BOOST_FILESYSTEM_VERSION == 3 - std::string so_far = rel_path_so_far + (itr->path().filename()).string() + "/"; + std::string so_far = rel_path_so_far + (itr->path().filename()).string() + std::string("/"); #else - std::string so_far = rel_path_so_far + (itr->path ()).filename () + "/"; + std::string so_far = rel_path_so_far + (itr->path ()).filename () + std::string("/"); #endif boost::filesystem::path curr_path = itr->path(); @@ -245,9 +245,9 @@ namespace od //check if its a directory, then get models in it if(boost::filesystem::is_directory(*itr)) { #if BOOST_FILESYSTEM_VERSION == 3 - std::string so_far = rel_path_so_far + (itr->path().filename()).string() + "/"; + std::string so_far = rel_path_so_far + (itr->path().filename()).string() + std::string("/"); #else - std::string so_far = rel_path_so_far + (itr->path ()).filename () + "/"; + std::string so_far = rel_path_so_far + (itr->path ()).filename () + std::string("/"); #endif boost::filesystem::path curr_path = itr->path(); diff --git a/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp b/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp index a766fe87..585177f5 100644 --- a/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp +++ b/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp @@ -68,14 +68,14 @@ namespace od template<typename PointT> shared_ptr<ODDetections> ODCADDetector3DGlobal<PointT>::detect(shared_ptr<ODScene> scene) { - std::cout << "not implemented, use with shared_ptr<ODScenePointCloud<PointT>>" <<std::endl; + std::cout << "not implemented, use with shared_ptr<ODScenePointCloud<PointT>>" << std::endl; return nullptr; }; template<typename PointT> shared_ptr<ODDetections> ODCADDetector3DGlobal<PointT>::detectOmni(shared_ptr<ODScene> scene) { - std::cout << "not implemented, use with shared_ptr<ODScenePointCloud<PointT>>" <<std::endl; + std::cout << "not implemented, use with shared_ptr<ODScenePointCloud<PointT>>" << std::endl; return nullptr; }; @@ -158,7 +158,7 @@ namespace od global_ = global; } else { - std::cout << "FATAL: descriptor type not available."; + std::cout << "FATAL: descriptor type not available." << std::endl; } } @@ -168,18 +168,14 @@ namespace od { shared_ptr<ODDetections3D> detections = make_shared<ODDetections3D>(); - - typename pcl::PointCloud<PointT>::Ptr frame; - float Z_DIST_ = 1.25f; - - frame = scene->getPointCloud(); + typename pcl::PointCloud<PointT>::Ptr frame = scene->getPointCloud(); pcl::PointCloud<pcl::PointXYZ>::Ptr xyz_points(new pcl::PointCloud<pcl::PointXYZ>); pcl::copyPointCloud(*frame, *xyz_points); //Step 1 -> Segment pcl::apps::DominantPlaneSegmentation<pcl::PointXYZ> dps; dps.setInputCloud(xyz_points); - dps.setMaxZBounds(Z_DIST_); + dps.setMaxZBounds(1.25f); dps.setObjectMinHeight(0.005); dps.setMinClusterSize(1000); dps.setWSize(9); @@ -228,9 +224,7 @@ namespace od { shared_ptr<ODDetections> detections = make_shared<ODDetections>(); - typename pcl::PointCloud<PointT>::Ptr frame; - - frame = scene->getPointCloud(); + typename pcl::PointCloud<PointT>::Ptr frame = scene->getPointCloud(); pcl::PointCloud<pcl::PointXYZ>::Ptr xyz_points(new pcl::PointCloud<pcl::PointXYZ>); pcl::copyPointCloud(*frame, *xyz_points); diff --git a/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp b/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp index 0a412f61..618e0a17 100644 --- a/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp +++ b/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp @@ -40,7 +40,7 @@ namespace od public: ODDetectorMultiAlgo2D(const std::string & training_data_location_) : ODDetector2D(training_data_location_){} - shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene) ; + shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene); shared_ptr<ODDetections2D> detectOmni(shared_ptr<ODSceneImage> scene); shared_ptr<ODDetections> detect(shared_ptr<ODScenePointCloud<PointT> > scene); @@ -61,14 +61,14 @@ namespace od template<typename PointT> shared_ptr<ODDetections> ODDetectorMultiAlgo2D<PointT>::detectOmni(shared_ptr<ODScene> scene) { - std::cout << "not implemented, use with shared_ptr<ODSceneImage> or shared_ptr<ODScenePointCloud<PointT>>" <<std::endl; + std::cout << "not implemented, use with shared_ptr<ODSceneImage> or shared_ptr<ODScenePointCloud<PointT>>" << std::endl; return nullptr; } template<typename PointT> shared_ptr<ODDetections> ODDetectorMultiAlgo2D<PointT>::detect(shared_ptr<ODScene> scene) { - std::cout << "not implemented, use with shared_ptr<ODSceneImage> or shared_ptr<ODScenePointCloud<PointT>>" <<std::endl; + std::cout << "not implemented, use with shared_ptr<ODSceneImage> or shared_ptr<ODScenePointCloud<PointT>>" << std::endl; return nullptr; } @@ -77,7 +77,6 @@ namespace od shared_ptr<ODDetections> ODDetectorMultiAlgo2D<PointT>::detect(shared_ptr<ODSceneImage> scene) { shared_ptr<ODDetections> detections_all = make_shared<ODDetections>(); - for(auto & d : detectors_2d_) { detections_all->append(d->detect(scene)); @@ -122,7 +121,7 @@ namespace od ODDetectorMultiAlgo(const std::string & training_data_location_) : ODDetector(training_data_location_){} - shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene) ; + shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene); shared_ptr<ODDetections2D> detectOmni(shared_ptr<ODSceneImage> scene); shared_ptr<ODDetections> detect(shared_ptr<ODScenePointCloud<PointT> > scene); @@ -143,14 +142,14 @@ namespace od template<typename PointT> shared_ptr<ODDetections> ODDetectorMultiAlgo<PointT>::detectOmni(shared_ptr<ODScene> scene) { - std::cout << "not implemented, use with shared_ptr<ODSceneImage> or shared_ptr<ODScenePointCloud<PointT>>" <<std::endl; + std::cout << "not implemented, use with shared_ptr<ODSceneImage> or shared_ptr<ODScenePointCloud<PointT>>" << std::endl; return nullptr; } template<typename PointT> shared_ptr<ODDetections> ODDetectorMultiAlgo<PointT>::detect(shared_ptr<ODScene> scene) { - std::cout << "not implemented, use with shared_ptr<ODSceneImage> or shared_ptr<ODScenePointCloud<PointT>>" <<std::endl; + std::cout << "not implemented, use with shared_ptr<ODSceneImage> or shared_ptr<ODScenePointCloud<PointT>>" << std::endl; return nullptr; } diff --git a/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h b/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h index 0fb7f3fd..ba7631e8 100644 --- a/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h +++ b/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h @@ -51,15 +51,15 @@ namespace od { public: - ODCascadeDetector(const std::string & trained_data_location = "", double scale_factor = 1.1, int min_neighbors = 3, + ODCascadeDetector(const std::string & trained_data_location = std::string(""), double scale_factor = 1.1, int min_neighbors = 3, int flags = 0, const cv::Size & min_size = cv::Size(), const cv::Size & max_size = cv::Size()); void init(); - virtual shared_ptr<ODDetections2D> detectOmni(shared_ptr<ODSceneImage> scene); - virtual shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene); + shared_ptr<ODDetections2D> detectOmni(shared_ptr<ODSceneImage> scene); + shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene); - virtual shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene); + shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene); private: diff --git a/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h b/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h index 0bf367da..b2dbc420 100644 --- a/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h +++ b/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h @@ -60,7 +60,7 @@ namespace od { public: - ODHOGDetector(const std::string & trained_data_location_ = "", const cv::Size & win_size = cv::Size(64,128), + ODHOGDetector(const std::string & trained_data_location_ = std::string(""), const cv::Size & win_size = cv::Size(64,128), const cv::Size & block_size = cv::Size(16,16), const cv::Size & block_stride = cv::Size(8,8), const cv::Size & cell_size = cv::Size(8,8), float hit_threshold = 0.0); @@ -81,7 +81,7 @@ namespace od void setTrainedDataLocation(const std::string & trained_data_location); const SVMType & getSvmtype() const; - void setSvmtype(const SVMType & svm_type_); + void setSvmtype(const SVMType & svm_type); const cv::Size & getWinSize() const; void setWinSize(const cv::Size & win_size); @@ -100,7 +100,7 @@ namespace od void printParameters(); - virtual shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene) {return nullptr;}; + shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene); protected: //properteis diff --git a/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h b/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h index 39c0c4dd..8bdc9513 100644 --- a/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h +++ b/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h @@ -51,7 +51,7 @@ namespace od public: - ODHOGTrainer(const std::string & training_input_location_ = "", const std::string & trained_data_location_ = "", + ODHOGTrainer(const std::string & training_input_location_ = std::string(""), const std::string & trained_data_location_ = std::string(""), const cv::Size & win_size = cv::Size(64,128), const cv::Size & block_size = cv::Size(16,16), const cv::Size & block_stride = cv::Size(8,8), const cv::Size & cell_size = cv::Size(8,8), float hits_threshold = 0.0); @@ -225,5 +225,5 @@ namespace od }; } - + } diff --git a/detectors/include/od/detectors/local2D/ODImageLocalMatching.h b/detectors/include/od/detectors/local2D/ODImageLocalMatching.h index 08f51512..df347145 100644 --- a/detectors/include/od/detectors/local2D/ODImageLocalMatching.h +++ b/detectors/include/od/detectors/local2D/ODImageLocalMatching.h @@ -47,7 +47,7 @@ namespace od public: - ODImageLocalMatchingTrainer(const std::string & training_input_location_, const std::string & training_data_location_); + ODImageLocalMatchingTrainer(const std::string & training_input_location, const std::string & training_data_location); }; @@ -61,7 +61,7 @@ namespace od public: - ODImageLocalMatchingDetector(const std::string & training_data_location_); + ODImageLocalMatchingDetector(const std::string & training_data_location); }; @@ -77,11 +77,11 @@ namespace od shared_ptr<ODImageLocalMatchingTrainer> getTrainer() const; - void setTrainer(shared_ptr<ODImageLocalMatchingTrainer> trainer_); + void setTrainer(shared_ptr<ODImageLocalMatchingTrainer> trainer); shared_ptr<ODImageLocalMatchingDetector> getDetector() const; - void setDetector(shared_ptr<ODImageLocalMatchingDetector> detector_); + void setDetector(shared_ptr<ODImageLocalMatchingDetector> detector); ODImageLocalMatching(); diff --git a/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h b/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h index bc7a9015..00e169a0 100644 --- a/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h +++ b/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h @@ -54,7 +54,7 @@ namespace od { public: - ODCADRecognizer2DLocal(const std::string & trained_data_location_ = 0); + ODCADRecognizer2DLocal(const std::string & trained_data_location_ = std::string()); const std::string & getCameraIntrinsicFile() const; diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/ODCsvWriter.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/ODCsvWriter.h index 79491d25..94dc5fc6 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/ODCsvWriter.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/ODCsvWriter.h @@ -11,7 +11,7 @@ namespace od { class CsvWriter { public: - CsvWriter(const std::string & path, const std::string & separator = " "); + CsvWriter(const std::string & path, const std::string & separator = std::string(" ")); ~CsvWriter(); void writeXYZ(const std::vector<cv::Point3f> & list_points3d); void writeUVXYZ(const std::vector<cv::Point3f> & list_points3d, const std::vector<cv::Point2f> & list_points2d, const cv::Mat & descriptors); diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/ODMesh.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/ODMesh.h index 2cd0a0d4..148293a0 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/ODMesh.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/ODMesh.h @@ -21,11 +21,10 @@ namespace od { public: explicit Triangle(int id, const cv::Point3f & V0, const cv::Point3f & V1, const cv::Point3f & V2); - virtual ~Triangle(); - cv::Point3f getV0() const { return v0_; } - cv::Point3f getV1() const { return v1_; } - cv::Point3f getV2() const { return v2_; } + cv::Point3f getV0() const; + cv::Point3f getV1() const; + cv::Point3f getV2() const; private: /** The identifier number of the triangle */ @@ -43,12 +42,9 @@ namespace od { public: explicit Ray(const cv::Point3f & P0, const cv::Point3f & P1); - virtual ~Ray(); - cv::Point3f getP0() - { return p0_; } - cv::Point3f getP1() - { return p1_; } + cv::Point3f getP0(); + cv::Point3f getP1(); private: /** The two points that defines the ray */ @@ -65,12 +61,11 @@ namespace od { public: Mesh(); - virtual ~Mesh(); - std::vector<std::vector<int> > getTrianglesList() const { return list_triangles_; } - std::vector<cv::Point3f> getVertices() const { return list_vertex_; } - cv::Point3f getVertex(int pos) const { return list_vertex_[pos]; } - int getNumVertices() const { return num_vertexs_; } + std::vector<std::vector<int> > getTrianglesList() const; + std::vector<cv::Point3f> getVertices(); + cv::Point3f getVertex(int pos) const; + int getNumVertices() const; void load(const std::string & path_file); diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/ODPnPProblem.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/ODPnPProblem.h index a9d47542..90bd84f9 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/ODPnPProblem.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/ODPnPProblem.h @@ -35,7 +35,7 @@ namespace od { cv::Point3f SUB(const cv::Point3f & v1, const cv::Point3f & v2); cv::Point3f getNearest3DPoint(std::vector<cv::Point3f> & points_list, const cv::Point3f & origin); - bool backproject2DPoint(const Mesh * mesh, const cv::Point2f & point2d, cv::Point3f & point3d); + bool backproject2DPoint(shared_ptr<Mesh> mesh, const cv::Point2f & point2d, cv::Point3f & point3d); bool intersectMollerTrumbore(Ray & ray, Triangle & Triangle, double & out); std::vector<cv::Point2f> verifyPoints(shared_ptr<Mesh> mesh); cv::Point2f backproject3DPoint(const cv::Point3f & point3d); diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/ODRobustMatcher.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/ODRobustMatcher.h index 871e1783..28905059 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/ODRobustMatcher.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/ODRobustMatcher.h @@ -24,25 +24,26 @@ namespace od { class RobustMatcher { + public: RobustMatcher(Model const & model, bool use_gpu, bool b); virtual ~RobustMatcher(); // Set the feature detector - void setFeatureDetector(const cv::Ptr<cv::FeatureDetector> & detect); + void setFeatureDetector(shared_ptr<cv::FeatureDetector> detect); // Set the descriptor extractor - void setDescriptorExtractor(const cv::Ptr<cv::DescriptorExtractor> & desc); + void setDescriptorExtractor(shared_ptr<cv::DescriptorExtractor> desc); // Set the matcher - void setDescriptorMatcher(const cv::Ptr<cv::DescriptorMatcher> & match); + void setDescriptorMatcher(shared_ptr<cv::DescriptorMatcher> match); // Compute the keypoints of an image - void computeKeyPoints( const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints); + void computeKeyPoints(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints); // Compute the descriptors of an image given its keypoints - void computeDescriptors( const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints, cv::Mat & descriptors); + void computeDescriptors(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints, cv::Mat & descriptors); // Set ratio parameter for the ratio test void setRatio(float rat); @@ -54,19 +55,19 @@ namespace od { int ratioTest(std::vector<std::vector<cv::DMatch> > & matches); // Insert symmetrical matches in symMatches vector - void symmetryTest( const std::vector<std::vector<cv::DMatch> > & matches1, - const std::vector<std::vector<cv::DMatch> > & matches2, - std::vector<cv::DMatch> & symMatches ); + void symmetryTest(const std::vector<std::vector<cv::DMatch> > & matches1, + const std::vector<std::vector<cv::DMatch> > & matches2, + std::vector<cv::DMatch> & symMatches ); // Match feature points using ratio and symmetry test - void robustMatch( const cv::Mat & frame, std::vector<cv::DMatch> & good_matches, - std::vector<cv::KeyPoint> & keypoints_frame, - const cv::Mat & descriptors_model ); + void robustMatch(const cv::Mat & frame, std::vector<cv::DMatch> & good_matches, + std::vector<cv::KeyPoint> & keypoints_frame, + const cv::Mat & descriptors_model ); // Match feature points using ratio test - void fastRobustMatch( const cv::Mat & frame, std::vector<cv::DMatch> & good_matches, - std::vector<cv::KeyPoint> & keypoints_frame, - const cv::Mat & descriptors_model ); + void fastRobustMatch(const cv::Mat & frame, std::vector<cv::DMatch> & good_matches, + std::vector<cv::KeyPoint> & keypoints_frame, + const cv::Mat & descriptors_model ); void findFeatureAndMatch(const cv::Mat & frame, std::vector<cv::DMatch> & good_matches, std::vector<cv::KeyPoint> & keypoints_frame, const cv::Mat & descriptors_model); @@ -84,16 +85,16 @@ namespace od { void instantiateMatcher1(const Model & model, bool use_gpu); // pointer to the feature point detector object - cv::Ptr<od::ODFeatureDetector2D> featureDetector_; + shared_ptr<od::ODFeatureDetector2D> featureDetector_; - cv::Ptr<cv::FeatureDetector> detector_; + shared_ptr<cv::FeatureDetector> detector_; // pointer to the feature descriptor extractor object - cv::Ptr<cv::DescriptorExtractor> extractor_; + shared_ptr<cv::DescriptorExtractor> extractor_; // pointer to the matcher object //The important feature of this class starts here //DIFFERENT TYPES OF MATCHERS, add here in this list (based on the construction - cv::Ptr<cv::DescriptorMatcher> matcher_; + shared_ptr<cv::DescriptorMatcher> matcher_; cv::Ptr<cv::cuda::DescriptorMatcher> matcher_gpu_; // max ratio between 1st and 2nd NN float ratio_; diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/ODUtils.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/ODUtils.h index c2babe48..201479a2 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/ODUtils.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/ODUtils.h @@ -59,7 +59,7 @@ namespace od { void drawObjectMesh(cv::Mat & image, const Mesh & mesh, PnPProblem & pnpProblem, const cv::Scalar & color); void drawModel(cv::Mat & image, const Model & model, PnPProblem & pnpProblem, const cv::Scalar color); void drawModel(cv::Mat & image, const Model & model, cv::Mat r_vect, cv::Mat t_mat, cv::Mat a_mat, - cv::Mat dist, const cv::Scalar & color); + cv::Mat dist, const cv::Scalar & color); // Computes the norm of the translation error double getTranslationError(const cv::Mat & t_true, const cv::Mat & t); diff --git a/detectors/include/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h b/detectors/include/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h index 4e4a1eab..609766f0 100644 --- a/detectors/include/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h +++ b/detectors/include/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h @@ -69,9 +69,9 @@ namespace od public: - ODCADRecogTrainerSnapshotBased(const std::string & training_input_location_ = std::string(""), - const std::string & training_data_location_ = std::string("")) : - ODImageLocalMatchingTrainer(training_input_location_, training_data_location_){} + ODCADRecogTrainerSnapshotBased(const std::string & training_input_location = std::string(""), + const std::string & training_data_location = std::string("")) : + ODImageLocalMatchingTrainer(training_input_location, training_data_location){} int train(); void init() {} @@ -85,6 +85,14 @@ namespace od }; + struct fcomp3d_euclidian + { + bool operator()(const cv::Point3f & lhs, const cv::Point3f & rhs) const + { + return lhs.x < rhs.x; + } + }; + class vtkTimerCallbackSnapshot : public vtkCommand { public: @@ -118,12 +126,6 @@ namespace od //some local variables used - struct fcomp3d_euclidian - { - bool operator()(const cv::Point3f & lhs, const cv::Point3f & rhs) const - { return lhs.x < rhs.x; } - }; - std::vector<std::pair<cv::Point3f, cv::KeyPoint> > pairs_3d_2d; cv::Mat common_descriptors; std::map<cv::Point3f, cv::KeyPoint, fcomp3d_euclidian> map_3d_2d; diff --git a/detectors/src/global2D/detection/ODHOGDetector.cpp b/detectors/src/global2D/detection/ODHOGDetector.cpp index 1c3f6b1c..54c6d626 100644 --- a/detectors/src/global2D/detection/ODHOGDetector.cpp +++ b/detectors/src/global2D/detection/ODHOGDetector.cpp @@ -239,5 +239,10 @@ namespace od std::cout << "cellSize: " << cell_size_ << std::endl; std::cout << "hitThreshold: " << hit_threshold_ << std::endl; } + + shared_ptr<ODDetections> ODHOGDetector::detectOmni(shared_ptr<ODScene> scene) + { + return nullptr; + } } } diff --git a/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp b/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp index 4ec76d18..a7a2f61a 100644 --- a/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp +++ b/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp @@ -35,8 +35,8 @@ namespace od namespace l2d { - ODCADRecognizer2DLocal::ODCADRecognizer2DLocal(const std::string & trained_data_location_): - ODImageLocalMatchingDetector(trained_data_location_) + ODCADRecognizer2DLocal::ODCADRecognizer2DLocal(const std::string & trained_data_location): + ODImageLocalMatchingDetector(trained_data_location) { meta_info_ = true; diff --git a/detectors/src/local2D/simple_ransac_detection/ODMesh.cpp b/detectors/src/local2D/simple_ransac_detection/ODMesh.cpp index 72f66462..da633136 100644 --- a/detectors/src/local2D/simple_ransac_detection/ODMesh.cpp +++ b/detectors/src/local2D/simple_ransac_detection/ODMesh.cpp @@ -21,11 +21,16 @@ namespace od { id_ = id; v0_ = V0; v1_ = V1; v2_ = V2; } - /** The default destructor of the Class */ - Triangle::~Triangle() - { - // TODO Auto-generated destructor stub + cv::Point3f Triangle::getV0() const { + return v0_; + } + cv::Point3f Triangle::getV1() const { + return v1_; } + cv::Point3f Triangle::getV2() const { + return v2_; + } + // --------------------------------------------------- // @@ -37,13 +42,17 @@ namespace od { p0_ = P0; p1_ = P1; } - /** The default destructor of the Class */ - Ray::~Ray() + cv::Point3f Ray::getP0() + { + return p0_; + } + cv::Point3f Ray::getP1() { - // TODO Auto-generated destructor stub + return p1_; } + // --------------------------------------------------- // // OBJECT MESH CLASS // // --------------------------------------------------- // @@ -56,12 +65,27 @@ namespace od { num_triangles_ = 0; } - /** The default destructor of the ObjectMesh Class */ - Mesh::~Mesh() - { - // TODO Auto-generated destructor stub + + + std::vector<std::vector<int> > Mesh::getTrianglesList() const + { + return list_triangles_; + } + + std::vector<cv::Point3f> Mesh::getVertices() + { + return list_vertex_; } + cv::Point3f Mesh::getVertex(int pos) const + { + return list_vertex_[pos]; + } + + int Mesh::getNumVertices() const + { + return num_vertexs_; + } /** Load a CSV with *.ply format **/ void Mesh::load(const std::string & path) diff --git a/detectors/src/local2D/simple_ransac_detection/ODPnPProblem.cpp b/detectors/src/local2D/simple_ransac_detection/ODPnPProblem.cpp index 4c27add5..d6e2c51d 100644 --- a/detectors/src/local2D/simple_ransac_detection/ODPnPProblem.cpp +++ b/detectors/src/local2D/simple_ransac_detection/ODPnPProblem.cpp @@ -241,7 +241,7 @@ namespace od { } // Back project a 2D point to 3D and returns if it's on the object surface - bool PnPProblem::backproject2DPoint(const Mesh * mesh, const cv::Point2f & point2d, cv::Point3f & point3d) + bool PnPProblem::backproject2DPoint(shared_ptr<Mesh> mesh, const cv::Point2f & point2d, cv::Point3f & point3d) { // Triangles list of the object mesh std::vector<std::vector<int> > triangles_list = mesh->getTrianglesList(); diff --git a/detectors/src/local2D/simple_ransac_detection/ODRobustMatcher.cpp b/detectors/src/local2D/simple_ransac_detection/ODRobustMatcher.cpp index acb8e1ab..6a84f7bc 100644 --- a/detectors/src/local2D/simple_ransac_detection/ODRobustMatcher.cpp +++ b/detectors/src/local2D/simple_ransac_detection/ODRobustMatcher.cpp @@ -53,19 +53,19 @@ namespace od { */ // Set the feature detector - void RobustMatcher::setFeatureDetector(const cv::Ptr<cv::FeatureDetector> & detect) + void RobustMatcher::setFeatureDetector(shared_ptr<cv::FeatureDetector> detect) { detector_ = detect; } // Set the descriptor extractor - void RobustMatcher::setDescriptorExtractor(const cv::Ptr<cv::DescriptorExtractor> & desc) + void RobustMatcher::setDescriptorExtractor(shared_ptr<cv::DescriptorExtractor> desc) { extractor_ = desc; } // Set the matcher - void RobustMatcher::setDescriptorMatcher(const cv::Ptr<cv::DescriptorMatcher> & match) + void RobustMatcher::setDescriptorMatcher(shared_ptr<cv::DescriptorMatcher> match) { matcher_ = match; } @@ -86,7 +86,7 @@ namespace od { } else { - matcher_ = cv::makePtr<cv::FlannBasedMatcher>(); + matcher_ = make_shared<cv::FlannBasedMatcher>(); } } @@ -97,8 +97,8 @@ namespace od { // ORB is the default feature ratio_ = 0.8f; - detector_ = cv::ORB::create(); - extractor_ = cv::ORB::create(); + detector_ = shared_ptr<cv::FeatureDetector>(cv::ORB::create().get()); + extractor_ = shared_ptr<cv::DescriptorExtractor>(cv::ORB::create().get()); instantiateMatcher(model, use_gpu_match); @@ -137,22 +137,21 @@ namespace od { { int removed = 0; // for all matches - for(std::vector<std::vector<cv::DMatch> >::iterator - matchIterator= matches.begin(); matchIterator!= matches.end(); ++matchIterator) + for(auto & match : matches) { // if 2 NN have been identified - if(matchIterator->size() > 1) + if(match.size() > 1) { // check distance ratio - if((*matchIterator)[0].distance / (*matchIterator)[1].distance > ratio_) + if(match[0].distance / match[1].distance > ratio_) { - matchIterator->clear(); // remove match + match.clear(); // remove match removed++; } } else { // does not have 2 neighbours - matchIterator->clear(); // remove match + match.clear(); // remove match removed++; } } @@ -166,37 +165,28 @@ namespace od { void RobustMatcher::symmetryTest(const std::vector<std::vector<cv::DMatch> > & matches1, const std::vector<std::vector<cv::DMatch> >& matches2, - std::vector<cv::DMatch>& symMatches ) + std::vector<cv::DMatch> & symMatches ) { // for all matches image 1 -> image 2 - for(std::vector<std::vector<cv::DMatch> >::const_iterator - matchIterator1 = matches1.begin(); matchIterator1 != matches1.end(); ++matchIterator1) + for(auto & match1 : matches1) { - // ignore deleted matches - if(matchIterator1->empty() || matchIterator1->size() < 2) + if(match1.empty() || match1.size() < 2) continue; // for all matches image 2 -> image 1 - for(std::vector<std::vector<cv::DMatch> >::const_iterator - matchIterator2 = matches2.begin(); matchIterator2 != matches2.end(); ++matchIterator2) + for(auto & match2 : matches2) { // ignore deleted matches - if(matchIterator2->empty() || matchIterator2->size() < 2) + if(match2.empty() || match2.size() < 2) continue; // Match symmetry test - if((*matchIterator1)[0].queryIdx == - (*matchIterator2)[0].trainIdx && - (*matchIterator2)[0].queryIdx == - (*matchIterator1)[0].trainIdx) + if(match1[0].queryIdx == match2[0].trainIdx && match2[0].queryIdx == match1[0].trainIdx) { // add symmetrical match - symMatches.push_back( - cv::DMatch((*matchIterator1)[0].queryIdx, - (*matchIterator1)[0].trainIdx, - (*matchIterator1)[0].distance)); + symMatches.push_back(cv::DMatch(match1[0].queryIdx, match1[0].trainIdx, match1[0].distance)); break; // next match in image 1 -> image 2 } } From 39af4382f61405370c74d5c90c94b7f5cfd04199 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Tue, 21 Jun 2016 16:57:16 +0200 Subject: [PATCH 31/79] adds a first stub for the viewer and fixes some documentation bugs --- common/CMakeLists.txt | 1 + .../include/od/common/bindings/ODSvmlight.h | 1 - common/include/od/common/utils/ODViewer.h | 66 ++++++++++++ common/src/utils/ODViewer.cpp | 100 ++++++++++++++++++ doc/doxygen/doxyfile.in | 7 +- 5 files changed, 170 insertions(+), 5 deletions(-) create mode 100644 common/include/od/common/utils/ODViewer.h create mode 100644 common/src/utils/ODViewer.cpp diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index e5e93337..4e497a7e 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -23,6 +23,7 @@ if(build) "src/utils/ODUtils.cpp" "src/utils/ODFeatureDetector2D.cpp" "src/bindings/ODSvmlight.cpp" + "src/utils/ODViewer.cpp" ) include_directories(${COMMON_INCLUDE_DIR}) diff --git a/common/include/od/common/bindings/ODSvmlight.h b/common/include/od/common/bindings/ODSvmlight.h index 59530efb..e5666af6 100644 --- a/common/include/od/common/bindings/ODSvmlight.h +++ b/common/include/od/common/bindings/ODSvmlight.h @@ -41,7 +41,6 @@ Unless required by applicable law or agreed to in writing, software distributed */ /** - * @file: ODSvmlight.h * @author: Jan Hendriks (dahoc3150 [at] gmail.com) * @date: Created on 11. Mai 2011 * @brief: Wrapper interface for SVMlight, diff --git a/common/include/od/common/utils/ODViewer.h b/common/include/od/common/utils/ODViewer.h new file mode 100644 index 00000000..b68d2629 --- /dev/null +++ b/common/include/od/common/utils/ODViewer.h @@ -0,0 +1,66 @@ +#pragma once +#include <opencv2/core/core.hpp> +#include <opencv2/highgui/highgui.hpp> + +#include <pcl/visualization/cloud_viewer.h> +#include <pcl/point_cloud.h> +#include <pcl/point_types.h> + +#include <iostream> +#include <string> + +#include "od/common/utils/ODShared_pointers.h" + + +namespace od { + + enum odViewType { + UNDEFINED, + POINTCLOUD, + CVMAT + }; + + /** \brief The viewer class. + * + * This class is used to visualize all types handled by the OpenDetection Library. For now it is possible to display cv::Mat and pcl::PointCloud<PointT> >. + * + * \author Giacomo Dabisias + * + */ + class ODViewer { + + public: + + ODViewer(); + + template<typename PointT> + void render(shared_ptr<pcl::PointCloud<PointT>> to_display, const std::string & cloud_name); + + void render(const cv::Mat & to_display, const std::string & window_name); + + template<typename PointT> + void render(pcl::PointCloud<PointT> & to_display, const std::string & cloud_name); + + template<typename PointT> + void update(shared_ptr<pcl::PointCloud<PointT>> to_display, const std::string & cloud_name); + + void update(const cv::Mat & to_display, const std::string & window_name); + + template<typename PointT> + void update(pcl::PointCloud<PointT> & to_display, const std::string & cloud_name); + + void setColorHandler(const std::string & cloud_name); + void setBackGround(const cv::Scalar & color); + void setBackGround(unsigned int r, unsigned int g, unsigned int b); + + void registerCallback(void(*callback)(const pcl::visualization::KeyboardEvent &, void *), void * data = nullptr); + void registerCallback(const std::string & window_name, CvMouseCallback on_mouse, void * data = nullptr); + + private: + + odViewType status_; + shared_ptr<pcl::visualization::PCLVisualizer> viewer_; + + }; + +} \ No newline at end of file diff --git a/common/src/utils/ODViewer.cpp b/common/src/utils/ODViewer.cpp new file mode 100644 index 00000000..3c194725 --- /dev/null +++ b/common/src/utils/ODViewer.cpp @@ -0,0 +1,100 @@ +#include "od/common/utils/ODViewer.h" + +namespace od { + + ODViewer::ODViewer() : status_(UNDEFINED) {} + + template<typename PointT> + void ODViewer::render(shared_ptr<pcl::PointCloud<PointT>> to_display, const std::string & cloud_name){ + if(status_ != POINTCLOUD){ + std::cout << "Switching viewer to PointCloud mode" << std::endl; + } + + status_ = POINTCLOUD; + + if(!viewer_) + viewer_ = make_shared<pcl::visualization::PCLVisualizer>(cloud_name); + + viewer_->addPointCloud<PointT>(to_display, cloud_name); + + } + + void ODViewer::render(const cv::Mat & to_display, const std::string & window_name){ + if(status_ != CVMAT){ + std::cout << "Switching viewer to cv::Mat mode" << std::endl; + } + + status_ = CVMAT; + + } + + template<typename PointT> + void ODViewer::render(pcl::PointCloud<PointT> & to_display, const std::string & cloud_name){ + + if(status_ != POINTCLOUD){ + std::cout << "Switching viewer to PointCloud mode" << std::endl; + } + + status_ = POINTCLOUD; + + if(!viewer_) + viewer_ = make_shared<pcl::visualization::PCLVisualizer>(cloud_name); + + viewer_->addPointCloud<PointT>(to_display, cloud_name); + + } + + template<typename PointT> + void ODViewer::update(shared_ptr<pcl::PointCloud<PointT>> to_display, const std::string & cloud_name){ + if(status_ != POINTCLOUD){ + std::cout << "No PointCloud to render! use render(shared_ptr<pcl::PointCloud<PointT>>) first!" << std::endl; + return; + } + } + + void ODViewer::update(const cv::Mat & to_display, const std::string & window_name){ + if(status_ != CVMAT){ + std::cout << "No cv::Mat to render! use render(const cv::Mat & to_display) first!" << std::endl; + return; + } + + } + + template<typename PointT> + void ODViewer::update(pcl::PointCloud<PointT> & to_display, const std::string & cloud_name){ + if(status_ != POINTCLOUD){ + std::cout << "No PointCloud to render! use render(shared_ptr<pcl::PointCloud<PointT>>) first!" << std::endl; + return; + } + + } + + void ODViewer::setColorHandler(const std::string & cloud_name){ + if(status_ != POINTCLOUD){ + std::cout << "No PointCloud to render! use render(shared_ptr<pcl::PointCloud<PointT>>) first!" << std::endl; + return; + } + + } + + void ODViewer::setBackGround(const cv::Scalar & color){ + setBackGround(color[2], color[1], color[0]); + + } + + void ODViewer::setBackGround(unsigned int r, unsigned int g, unsigned int b){ + switch(status_){ + case POINTCLOUD : + viewer_->setBackgroundColor(r, g, b); + break; + case CVMAT : + //img_ = color; + break; + case UNDEFINED : + std::cout << "Render al element first before adding a backgroud!" << std::endl; + break; + } + + } + +} \ No newline at end of file diff --git a/doc/doxygen/doxyfile.in b/doc/doxygen/doxyfile.in index 7f97cd11..df7a034a 100644 --- a/doc/doxygen/doxyfile.in +++ b/doc/doxygen/doxyfile.in @@ -116,12 +116,11 @@ FILE_PATTERNS = *.h \ *.md RECURSIVE = YES EXCLUDE = "@OD_SOURCE_DIR@/cmake" \ + "@OD_SOURCE_DIR@/build" \ + "@OD_SOURCE_DIR@/3rdparty" EXCLUDE_SYMLINKS = YES -EXCLUDE_PATTERNS = "@OD_SOURCE_DIR@/detectors/*/detection/*/*" \ - "@OD_SOURCE_DIR@/detectors/*/training/*/*" \ - "@OD_SOURCE_DIR@/3rdparty/*" \ - "@OD_SOURCE_DIR@/README.md" +EXCLUDE_PATTERNS = "@OD_SOURCE_DIR@/README.md" EXCLUDE_SYMBOLS = EXAMPLE_PATH = "@OD_SOURCE_DIR@/examples" EXAMPLE_PATTERNS = * From 91002dc82c6db3ccd0a0c0662171a3f643e41048 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Wed, 22 Jun 2016 16:29:00 +0200 Subject: [PATCH 32/79] improves viewer and adds example. There is a bug in pcl when closing the viewer which is unresolved for now --- common/CMakeLists.txt | 7 ++ common/impl/od/common/utils/ODViewer.hpp | 56 +++++++++ common/include/od/common/utils/ODViewer.h | 78 +++++++------ common/src/utils/ODViewer.cpp | 110 +++++++++--------- examples/apps/CMakeLists.txt | 7 ++ .../visualization/ODVisualizationTest.cpp | 48 ++++++++ 6 files changed, 213 insertions(+), 93 deletions(-) create mode 100644 common/impl/od/common/utils/ODViewer.hpp create mode 100644 examples/apps/visualization/ODVisualizationTest.cpp diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 4e497a7e..90c0d43a 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -4,7 +4,11 @@ set(SUBSYS_DESC "The core module of OpenDetection having the pipeline logic") set(SUBSYS_DEPS ${OpenCV_LIBS} ${PCL_LIBRARIES}) set(COMMON_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) +set(COMMON_IMPL_DIR ${CMAKE_CURRENT_SOURCE_DIR}/impl) + set(COMMON_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include PARENT_SCOPE) +set(COMMON_IMPL_DIR ${CMAKE_CURRENT_SOURCE_DIR}/impl PARENT_SCOPE) + if(WITH_GPU) set(SUBSYS_DEPS ${SUBSYS_DEPS} siftgpu) @@ -27,10 +31,13 @@ if(build) ) include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) OD_ADD_LIBRARY(${SUBSYS_NAME} SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) install(DIRECTORY ${COMMON_INCLUDE_DIR}/od DESTINATION ${OD_INSTALL_INCLUDE_DIR} COMPONENT ${LIB_NAME}) + install(DIRECTORY ${COMMON_IMPL_DIR}/od DESTINATION ${OD_INSTALL_INCLUDE_DIR} COMPONENT ${LIB_NAME}) + endif(build) \ No newline at end of file diff --git a/common/impl/od/common/utils/ODViewer.hpp b/common/impl/od/common/utils/ODViewer.hpp new file mode 100644 index 00000000..37063bc9 --- /dev/null +++ b/common/impl/od/common/utils/ODViewer.hpp @@ -0,0 +1,56 @@ +#pragma once +#include "od/common/utils/ODViewer.h" + +namespace od { + + template<typename PointT> + void ODViewer::render(shared_ptr<pcl::PointCloud<PointT> > to_display, const std::string & cloud_name, bool colored){ + if(status_ != POINTCLOUD){ + std::cout << "Switching viewer to PointCloud mode" << std::endl; + if(status_ == CVMAT){ + cv::destroyWindow(mat_window_name_); + } + } + + status_ = POINTCLOUD; + + if(!viewer_ || pcl_window_name_ != cloud_name){ + viewer_ = make_shared<pcl::visualization::PCLVisualizer>(cloud_name); + viewer_->setBackgroundColor(0, 0, 0); + } + + pcl_window_name_ = cloud_name; + + if(colored) + { + pcl::visualization::PointCloudColorHandlerRGBField<PointT> rgb(to_display); + viewer_->addPointCloud<PointT>(to_display, rgb, cloud_name); + }else{ + viewer_->addPointCloud<PointT>(to_display, cloud_name); + } + } + + template<typename PointT> + void ODViewer::update(shared_ptr<pcl::PointCloud<PointT> > to_display, const std::string & cloud_name, bool colored){ + if(status_ != POINTCLOUD){ + std::cout << "No PointCloud to render! use render(shared_ptr<pcl::PointCloud<PointT>>) first!" << std::endl; + return; + } + + if(cloud_name != pcl_window_name_){ + std::cout << "No window " << cloud_name + << " present. Please first use render(shared_ptr<pcl::PointCloud<PointT> >, const std::string & ) to create the new window" + << std::endl; + return; + } + + if(colored) + { + pcl::visualization::PointCloudColorHandlerRGBField<PointT> rgb(to_display); + viewer_->updatePointCloud<PointT>(to_display, rgb, cloud_name); + }else{ + viewer_->updatePointCloud<PointT>(to_display, cloud_name); + } + } + +} \ No newline at end of file diff --git a/common/include/od/common/utils/ODViewer.h b/common/include/od/common/utils/ODViewer.h index b68d2629..f8bc2b04 100644 --- a/common/include/od/common/utils/ODViewer.h +++ b/common/include/od/common/utils/ODViewer.h @@ -14,53 +14,61 @@ namespace od { - enum odViewType { - UNDEFINED, - POINTCLOUD, - CVMAT - }; + enum odViewType { + UNDEFINED, + POINTCLOUD, + CVMAT + }; - /** \brief The viewer class. - * - * This class is used to visualize all types handled by the OpenDetection Library. For now it is possible to display cv::Mat and pcl::PointCloud<PointT> >. - * - * \author Giacomo Dabisias - * - */ - class ODViewer { + /** \brief The viewer class. + * + * This class is used to visualize all types handled by the OpenDetection Library. For now it is possible to display cv::Mat and pcl::PointCloud<PointT> >. + * + * \author Giacomo Dabisias + * + */ + class ODViewer { - public: + public: - ODViewer(); + ODViewer(); - template<typename PointT> - void render(shared_ptr<pcl::PointCloud<PointT>> to_display, const std::string & cloud_name); + template<typename PointT> + void render(shared_ptr<pcl::PointCloud<PointT> > to_display, const std::string & cloud_name, bool colored = true); - void render(const cv::Mat & to_display, const std::string & window_name); + void render(const cv::Mat & to_display, const std::string & window_name); - template<typename PointT> - void render(pcl::PointCloud<PointT> & to_display, const std::string & cloud_name); + template<typename PointT> + void update(shared_ptr<pcl::PointCloud<PointT> > to_display, const std::string & cloud_name, bool colored = true); - template<typename PointT> - void update(shared_ptr<pcl::PointCloud<PointT>> to_display, const std::string & cloud_name); + void update(const cv::Mat & to_display, const std::string & window_name); - void update(const cv::Mat & to_display, const std::string & window_name); + void setBackGround(const cv::Scalar & color); + void setBackGround(unsigned int r, unsigned int g, unsigned int b); - template<typename PointT> - void update(pcl::PointCloud<PointT> & to_display, const std::string & cloud_name); + void registerCallback(void(*callback)(const pcl::visualization::KeyboardEvent &, void *), void * data = nullptr); + void registerCallback(const std::string & window_name, CvMouseCallback on_mouse, void * data = nullptr); - void setColorHandler(const std::string & cloud_name); - void setBackGround(const cv::Scalar & color); - void setBackGround(unsigned int r, unsigned int g, unsigned int b); + void spin(); + bool toStop(); - void registerCallback(void(*callback)(const pcl::visualization::KeyboardEvent &, void *), void * data = nullptr); - void registerCallback(const std::string & window_name, CvMouseCallback on_mouse, void * data = nullptr); + private: - private: + odViewType status_; + shared_ptr<pcl::visualization::PCLVisualizer> viewer_; + std::string mat_window_name_, pcl_window_name_; + shared_ptr<cv::Mat> mat_; - odViewType status_; - shared_ptr<pcl::visualization::PCLVisualizer> viewer_; + }; - }; + extern template void ODViewer::render<pcl::PointXYZ>(shared_ptr<pcl::PointCloud<pcl::PointXYZ> >, const std::string &, bool); + extern template void ODViewer::render<pcl::PointXYZRGB>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> >, const std::string &, bool); + extern template void ODViewer::render<pcl::PointXYZRGBA>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA> >, const std::string &, bool); -} \ No newline at end of file + extern template void ODViewer::update<pcl::PointXYZ>(shared_ptr<pcl::PointCloud<pcl::PointXYZ> >, const std::string &, bool); + extern template void ODViewer::update<pcl::PointXYZRGB>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> >, const std::string &, bool); + extern template void ODViewer::update<pcl::PointXYZRGBA>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA> >, const std::string &, bool); + +} + +#include "od/common/utils/ODViewer.hpp" diff --git a/common/src/utils/ODViewer.cpp b/common/src/utils/ODViewer.cpp index 3c194725..ac6acbaa 100644 --- a/common/src/utils/ODViewer.cpp +++ b/common/src/utils/ODViewer.cpp @@ -4,97 +4,91 @@ namespace od { ODViewer::ODViewer() : status_(UNDEFINED) {} - template<typename PointT> - void ODViewer::render(shared_ptr<pcl::PointCloud<PointT>> to_display, const std::string & cloud_name){ - if(status_ != POINTCLOUD){ - std::cout << "Switching viewer to PointCloud mode" << std::endl; - } - - status_ = POINTCLOUD; - - if(!viewer_) - viewer_ = make_shared<pcl::visualization::PCLVisualizer>(cloud_name); - - viewer_->addPointCloud<PointT>(to_display, cloud_name); - - } - void ODViewer::render(const cv::Mat & to_display, const std::string & window_name){ if(status_ != CVMAT){ std::cout << "Switching viewer to cv::Mat mode" << std::endl; + if(status_ == POINTCLOUD){ + //BUGGED IN PCL, its not closing https://github.com/PointCloudLibrary/pcl/issues/172 + //viewer_->close(); + } } - status_ = CVMAT; + mat_window_name_ = window_name; + mat_ = make_shared<cv::Mat>(to_display); } - template<typename PointT> - void ODViewer::render(pcl::PointCloud<PointT> & to_display, const std::string & cloud_name){ - - if(status_ != POINTCLOUD){ - std::cout << "Switching viewer to PointCloud mode" << std::endl; - } - - status_ = POINTCLOUD; - - if(!viewer_) - viewer_ = make_shared<pcl::visualization::PCLVisualizer>(cloud_name); - - viewer_->addPointCloud<PointT>(to_display, cloud_name); - - } - - template<typename PointT> - void ODViewer::update(shared_ptr<pcl::PointCloud<PointT>> to_display, const std::string & cloud_name){ - if(status_ != POINTCLOUD){ - std::cout << "No PointCloud to render! use render(shared_ptr<pcl::PointCloud<PointT>>) first!" << std::endl; - return; - } - } - void ODViewer::update(const cv::Mat & to_display, const std::string & window_name){ if(status_ != CVMAT){ - std::cout << "No cv::Mat to render! use render(const cv::Mat & to_display) first!" << std::endl; + std::cout << "No cv::Mat to render! use render(const cv::Mat &) first!" << std::endl; return; } - - } - - template<typename PointT> - void ODViewer::update(pcl::PointCloud<PointT> & to_display, const std::string & cloud_name){ - if(status_ != POINTCLOUD){ - std::cout << "No PointCloud to render! use render(shared_ptr<pcl::PointCloud<PointT>>) first!" << std::endl; + if(window_name != mat_window_name_){ + std::cout << "No window " << window_name << " present. Please first use render(const cv::Mat &, const std::string &) to create the new window" << std::endl; return; } + mat_ = make_shared<cv::Mat>(to_display); + } - void ODViewer::setColorHandler(const std::string & cloud_name){ - if(status_ != POINTCLOUD){ - std::cout << "No PointCloud to render! use render(shared_ptr<pcl::PointCloud<PointT>>) first!" << std::endl; - return; + void ODViewer::setBackGround(const cv::Scalar & color){ + switch(status_){ + case POINTCLOUD : + viewer_->setBackgroundColor(color[2], color[1], color[0]); + break; + case CVMAT : + *mat_ = color; + break; + case UNDEFINED : + std::cout << "Render an element first before adding a backgroud!" << std::endl; + break; } } - void ODViewer::setBackGround(const cv::Scalar & color){ - setBackGround(color[2], color[1], color[0]); + bool ODViewer::toStop(){ + switch(status_){ + case POINTCLOUD : + return(viewer_->wasStopped()); + break; + case CVMAT : + return (cv::waitKey(10) == 'q'); + break; + case UNDEFINED : + break; + } + return false; + } void ODViewer::setBackGround(unsigned int r, unsigned int g, unsigned int b){ + setBackGround(cv::Scalar(b, g, r)); + } + + void ODViewer::spin(){ switch(status_){ case POINTCLOUD : - viewer_->setBackgroundColor(r, g, b); + if(viewer_) + viewer_->spinOnce(); break; case CVMAT : - //img_ = color; + cv::imshow(mat_window_name_, *mat_); + cv::waitKey(10); break; case UNDEFINED : - std::cout << "Render al element first before adding a backgroud!" << std::endl; + std::cout << "Nothing to render!" << std::endl; break; } - } + // Explicit template function instantiation + template void ODViewer::render<pcl::PointXYZ>(shared_ptr<pcl::PointCloud<pcl::PointXYZ> >, const std::string &, bool); + template void ODViewer::render<pcl::PointXYZRGB>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> >, const std::string &, bool); + template void ODViewer::render<pcl::PointXYZRGBA>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA> >, const std::string &, bool); + + template void ODViewer::update<pcl::PointXYZ>(shared_ptr<pcl::PointCloud<pcl::PointXYZ> >, const std::string &, bool); + template void ODViewer::update<pcl::PointXYZRGB>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> >, const std::string &, bool); + template void ODViewer::update<pcl::PointXYZRGBA>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA> >, const std::string &, bool); } \ No newline at end of file diff --git a/examples/apps/CMakeLists.txt b/examples/apps/CMakeLists.txt index d7f02065..07e8fcf5 100644 --- a/examples/apps/CMakeLists.txt +++ b/examples/apps/CMakeLists.txt @@ -1,5 +1,7 @@ include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) +include_directories(${COMMON_IMPL_DIR}) + include_directories(${CMAKE_3RDPARTY_DIR}/pugixml/src/) include_directories(${OD_BINARY_DIR}/generated) include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) @@ -17,3 +19,8 @@ OD_ADD_EXAMPLE(od_multihog_app FILES global2D/od_multihog_app.cpp LINK_WITH od_common od_global_image_detector ) + +OD_ADD_EXAMPLE(viewer_test + FILES visualization/ODVisualizationTest.cpp + LINK_WITH od_common +) diff --git a/examples/apps/visualization/ODVisualizationTest.cpp b/examples/apps/visualization/ODVisualizationTest.cpp new file mode 100644 index 00000000..ecab4b26 --- /dev/null +++ b/examples/apps/visualization/ODVisualizationTest.cpp @@ -0,0 +1,48 @@ +#include "od/common/utils/ODViewer.h" +#include <iostream> +#include <pcl/io/pcd_io.h> +#include <pcl/point_types.h> + +int main(int argc, char * argv[]){ + + if(argc < 3){ + std::cout << "Use pointcloud and image as input" << std::endl; + return -1; + } + + shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA>> cloud(new pcl::PointCloud<pcl::PointXYZRGBA>()); + + if(pcl::io::loadPCDFile<pcl::PointXYZRGBA> (argv[1], *cloud) == -1){ + std::cout << "Could not load " << argv[1] << std::endl; + return -1; + } + + od::ODViewer viewer; + viewer.render(cloud, std::string("test_cloud")); + + while(!viewer.toStop()){ + viewer.spin(); + } + + cv::Mat image = cv::imread(argv[2], CV_LOAD_IMAGE_COLOR); + viewer.render(image, std::string("test_image")); + + while(!viewer.toStop()){ + viewer.spin(); + } + + viewer.render(cloud, std::string("test_cloud2")); + + while(!viewer.toStop()){ + viewer.spin(); + } + + //This will fail since test_cloud has been overwritten by test_cloud2 + viewer.update(cloud, std::string("test_cloud")); + + while(!viewer.toStop()){ + viewer.spin(); + } + + return 0; +} \ No newline at end of file From 65f40d857b1e313fbc1b5e7a2efcd6685410df21 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Thu, 23 Jun 2016 15:15:33 +0200 Subject: [PATCH 33/79] fixes windows compilation flags --- CMakeLists.txt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ddbe1675..3afc66d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,16 @@ cmake_minimum_required(VERSION 2.8) project(OpenDetection) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall") + # Set correct flags depending on the OS and compiler +if(WIN32) + if(MSVC) + set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} /SUBSYSTEM:WINDOWS /DNOMINMAX") + elseif(CMAKE_COMPILER_IS_GNUCXX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mwindows -std=c++11 -Wall") + endif() +elseif(UNIX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall") +endif() # Initialize variables set(OD_SOURCE_DIR ${OpenDetection_SOURCE_DIR}) From 776f685c8e6809b2cc5b06d25e1dfa42dcd4049d Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Mon, 27 Jun 2016 15:26:21 +0200 Subject: [PATCH 34/79] adds template precompilation for most used types --- common/CMakeLists.txt | 2 - common/impl/od/common/utils/ODViewer.hpp | 2 +- common/include/od/common/utils/ODViewer.h | 17 +-- common/src/utils/ODUtils.cpp | 21 ++++ common/src/utils/ODViewer.cpp | 20 +++- detectors/CMakeLists.txt | 4 +- .../detection/ODCADDetector3DGlobal.hpp | 71 ++++++++++++ .../misc/detection/ODDetectorMultiAlgo.hpp | 109 ++++++++++++++++++ .../training/ODCADRecogTrainerSnapshotBased.h | 1 - detectors/src/global2D/CMakeLists.txt | 2 +- detectors/src/global3D/CMakeLists.txt | 5 +- .../detection/ODCADDetector3DGlobal.cpp | 59 ++++++++++ detectors/src/local2D/CMakeLists.txt | 2 +- detectors/src/misc/CMakeLists.txt | 20 ++++ .../misc/detection/ODDetectorMultiAlgo.cpp | 107 +++++++++++++++++ examples/apps/CMakeLists.txt | 4 +- examples/objectdetector/CMakeLists.txt | 26 ++--- 17 files changed, 438 insertions(+), 34 deletions(-) create mode 100644 detectors/src/global3D/detection/ODCADDetector3DGlobal.cpp create mode 100644 detectors/src/misc/CMakeLists.txt create mode 100644 detectors/src/misc/detection/ODDetectorMultiAlgo.cpp diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 90c0d43a..cf2c0d1d 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -38,6 +38,4 @@ if(build) install(DIRECTORY ${COMMON_INCLUDE_DIR}/od DESTINATION ${OD_INSTALL_INCLUDE_DIR} COMPONENT ${LIB_NAME}) install(DIRECTORY ${COMMON_IMPL_DIR}/od DESTINATION ${OD_INSTALL_INCLUDE_DIR} COMPONENT ${LIB_NAME}) - - endif(build) \ No newline at end of file diff --git a/common/impl/od/common/utils/ODViewer.hpp b/common/impl/od/common/utils/ODViewer.hpp index 37063bc9..63d3e84f 100644 --- a/common/impl/od/common/utils/ODViewer.hpp +++ b/common/impl/od/common/utils/ODViewer.hpp @@ -8,7 +8,7 @@ namespace od { if(status_ != POINTCLOUD){ std::cout << "Switching viewer to PointCloud mode" << std::endl; if(status_ == CVMAT){ - cv::destroyWindow(mat_window_name_); + cv::destroyWindow(mat_window_name_.c_str()); } } diff --git a/common/include/od/common/utils/ODViewer.h b/common/include/od/common/utils/ODViewer.h index f8bc2b04..3a20617e 100644 --- a/common/include/od/common/utils/ODViewer.h +++ b/common/include/od/common/utils/ODViewer.h @@ -9,17 +9,14 @@ #include <iostream> #include <string> + +#include <vtkRenderWindow.h> + #include "od/common/utils/ODShared_pointers.h" namespace od { - enum odViewType { - UNDEFINED, - POINTCLOUD, - CVMAT - }; - /** \brief The viewer class. * * This class is used to visualize all types handled by the OpenDetection Library. For now it is possible to display cv::Mat and pcl::PointCloud<PointT> >. @@ -29,6 +26,12 @@ namespace od { */ class ODViewer { + enum odViewType { + UNDEFINED, + POINTCLOUD, + CVMAT + }; + public: ODViewer(); @@ -58,7 +61,7 @@ namespace od { shared_ptr<pcl::visualization::PCLVisualizer> viewer_; std::string mat_window_name_, pcl_window_name_; shared_ptr<cv::Mat> mat_; - + }; extern template void ODViewer::render<pcl::PointXYZ>(shared_ptr<pcl::PointCloud<pcl::PointXYZ> >, const std::string &, bool); diff --git a/common/src/utils/ODUtils.cpp b/common/src/utils/ODUtils.cpp index 355ac05e..260d6141 100644 --- a/common/src/utils/ODUtils.cpp +++ b/common/src/utils/ODUtils.cpp @@ -7,6 +7,25 @@ namespace od { +#ifdef WIN32 + +std::vector<std::string> myglob(const std::string & pat) + { + HANDLE hFind; + WIN32_FIND_DATA data; + std::vector<std::string> ret; + + hFind = FindFirstFile(pat.c_str(), &data); + if (hFind != INVALID_HANDLE_VALUE) { + do { + ret.push_back(data.cFileName); + } while (FindNextFile(hFind, &data)); + FindClose(hFind); + } + + return ret; + } +#else std::vector<std::string> myglob(const std::string & pat) { glob_t glob_result; @@ -18,6 +37,8 @@ namespace od globfree(&glob_result); return ret; } +#endif + void normL2(cv::Mat & descriptors) { diff --git a/common/src/utils/ODViewer.cpp b/common/src/utils/ODViewer.cpp index ac6acbaa..94a5f93c 100644 --- a/common/src/utils/ODViewer.cpp +++ b/common/src/utils/ODViewer.cpp @@ -1,4 +1,7 @@ #include "od/common/utils/ODViewer.h" +#if(WIN32) + #include <Windows.h> +#endif namespace od { @@ -10,12 +13,23 @@ namespace od { if(status_ == POINTCLOUD){ //BUGGED IN PCL, its not closing https://github.com/PointCloudLibrary/pcl/issues/172 //viewer_->close(); + //Alternative solution +#if(WIN32) + HWND hWnd = (HWND)viewer3d->getRenderWindow()->GetGenericWindowId(); + viewer_.reset(); + DestroyWindow(hWnd); +#else + auto p = viewer_->getRenderWindow(); + viewer_.reset(); + p->Finalize(); +#endif } } status_ = CVMAT; mat_window_name_ = window_name; mat_ = make_shared<cv::Mat>(to_display); - + cvStartWindowThread(); + cvNamedWindow(mat_window_name_.c_str(), CV_WINDOW_AUTOSIZE); } void ODViewer::update(const cv::Mat & to_display, const std::string & window_name){ @@ -53,8 +67,8 @@ namespace od { case POINTCLOUD : return(viewer_->wasStopped()); break; - case CVMAT : - return (cv::waitKey(10) == 'q'); + case CVMAT : + return (cv::waitKey(50) == 27 || cv::waitKey(50) == 'q'); break; case UNDEFINED : break; diff --git a/detectors/CMakeLists.txt b/detectors/CMakeLists.txt index 2c0c8258..17e19fed 100644 --- a/detectors/CMakeLists.txt +++ b/detectors/CMakeLists.txt @@ -9,6 +9,8 @@ set(DETECTORS_IMPL_DIR ${DETECTORS_DIR}/impl PARENT_SCOPE) add_subdirectory("src/local2D") add_subdirectory("src/global2D") add_subdirectory("src/global3D") +add_subdirectory("src/misc") + install(DIRECTORY ${DETECTORS_INCLUDE_DIR}/od DESTINATION ${OD_INSTALL_INCLUDE_DIR}) -set(${OD_INSTALLED_LIBRARIES} PARENT_SCOPE) \ No newline at end of file +set(${OD_INSTALLED_LIBRARIES} PARENT_SCOPE) diff --git a/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp b/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp index 585177f5..b0ef4ca7 100644 --- a/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp +++ b/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp @@ -54,6 +54,7 @@ namespace od shared_ptr<ODDetections> detect(shared_ptr<ODScene> scene); shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene); + shared_ptr<ODDetections> detect(shared_ptr<ODScenePointCloud<PointT> > scene); shared_ptr<ODDetections3D> detectOmni(shared_ptr<ODScenePointCloud<PointT> > scene); @@ -245,5 +246,75 @@ namespace od return detections; } +#ifndef DOXYGEN_SHOULD_SKIP_THIS + + extern template + shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZ>::detect(shared_ptr<ODScene> scene); + + extern template + shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZ>::detectOmni(shared_ptr<ODScene> scene); + + extern template + void ODCADDetector3DGlobal<pcl::PointXYZ>::init(); + + extern template + shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZ>::detect(shared_ptr<ODScene> scene); + + extern template + shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZ>::detectOmni(shared_ptr<ODScene> scene); + + extern template + shared_ptr<ODDetections3D> ODCADDetector3DGlobal<pcl::PointXYZ>::detectOmni(shared_ptr<ODScenePointCloud<pcl::PointXYZ> > scene); + + extern template + shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZ>::detect(shared_ptr<ODScenePointCloud<pcl::PointXYZ> > scene); + + + + extern template + shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZRGB>::detect(shared_ptr<ODScene> scene); + + extern template + shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZRGB>::detectOmni(shared_ptr<ODScene> scene); + + extern template + void ODCADDetector3DGlobal<pcl::PointXYZRGB>::init(); + + extern template + shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZRGB>::detect(shared_ptr<ODScene> scene); + + extern template + shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZRGB>::detectOmni(shared_ptr<ODScene> scene); + + extern template + shared_ptr<ODDetections3D> ODCADDetector3DGlobal<pcl::PointXYZRGB>::detectOmni(shared_ptr<ODScenePointCloud<pcl::PointXYZRGB> > scene); + + extern template + shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZRGB>::detect(shared_ptr<ODScenePointCloud<pcl::PointXYZRGB> > scene); + + + + extern template + shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZRGBA>::detect(shared_ptr<ODScene> scene); + + extern template + shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZRGBA>::detectOmni(shared_ptr<ODScene> scene); + + extern template + void ODCADDetector3DGlobal<pcl::PointXYZRGBA>::init(); + + extern template + shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZRGBA>::detect(shared_ptr<ODScene> scene); + + extern template + shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZRGBA>::detectOmni(shared_ptr<ODScene> scene); + + extern template + shared_ptr<ODDetections3D> ODCADDetector3DGlobal<pcl::PointXYZRGBA>::detectOmni(shared_ptr<ODScenePointCloud<pcl::PointXYZRGBA> > scene); + + extern template + shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZRGBA>::detect(shared_ptr<ODScenePointCloud<pcl::PointXYZRGBA> > scene); + +#endif } } diff --git a/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp b/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp index 618e0a17..2c32292f 100644 --- a/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp +++ b/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp @@ -112,6 +112,59 @@ namespace od } } +#ifndef DOXYGEN_SHOULD_SKIP_THIS + + extern template + shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZ>::detectOmni(shared_ptr<ODScene> scene); + + extern template + shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZ>::detect(shared_ptr<ODScene> scene); + + extern template + shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZ>::detect(shared_ptr<ODSceneImage> scene); + + extern template + shared_ptr<ODDetections2D> ODDetectorMultiAlgo2D<pcl::PointXYZ>::detectOmni(shared_ptr<ODSceneImage > scene); + + extern template + void ODDetectorMultiAlgo2D<pcl::PointXYZ>::init(); + + + + extern template + shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZRGB>::detectOmni(shared_ptr<ODScene> scene); + + extern template + shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZRGB>::detect(shared_ptr<ODScene> scene); + + extern template + shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZRGB>::detect(shared_ptr<ODSceneImage> scene); + + extern template + shared_ptr<ODDetections2D> ODDetectorMultiAlgo2D<pcl::PointXYZRGB>::detectOmni(shared_ptr<ODSceneImage > scene); + + extern template + void ODDetectorMultiAlgo2D<pcl::PointXYZRGB>::init(); + + + + + extern template + shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZRGBA>::detectOmni(shared_ptr<ODScene> scene); + + extern template + shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZRGBA>::detect(shared_ptr<ODScene> scene); + + extern template + shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZRGBA>::detect(shared_ptr<ODSceneImage> scene); + + extern template + shared_ptr<ODDetections2D> ODDetectorMultiAlgo2D<pcl::PointXYZRGBA>::detectOmni(shared_ptr<ODSceneImage > scene); + + extern template + void ODDetectorMultiAlgo2D<pcl::PointXYZRGBA>::init(); + +#endif template<typename PointT> class ODDetectorMultiAlgo : public ODDetector @@ -190,4 +243,60 @@ namespace od return detections_all; } + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + + + extern template + shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZ>::detectOmni(shared_ptr<ODScene> scene); + + extern template + shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZ>::detect(shared_ptr<ODScene> scene); + + extern template + void ODDetectorMultiAlgo<pcl::PointXYZ>::init(); + + extern template + shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZ>::detect(shared_ptr<ODScenePointCloud<pcl::PointXYZ> > scene); + + extern template + shared_ptr<ODDetections3D> ODDetectorMultiAlgo<pcl::PointXYZ>::detectOmni(shared_ptr<ODScenePointCloud<pcl::PointXYZ> > scene); + + + + extern template + shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZRGB>::detectOmni(shared_ptr<ODScene> scene); + + extern template + shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZRGB>::detect(shared_ptr<ODScene> scene); + + extern template + void ODDetectorMultiAlgo<pcl::PointXYZRGB>::init(); + + extern template + shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZRGB>::detect(shared_ptr<ODScenePointCloud<pcl::PointXYZRGB> > scene); + + extern template + shared_ptr<ODDetections3D> ODDetectorMultiAlgo<pcl::PointXYZRGB>::detectOmni(shared_ptr<ODScenePointCloud<pcl::PointXYZRGB> > scene); + + + + extern template + shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZRGBA>::detectOmni(shared_ptr<ODScene> scene); + + extern template + shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZRGBA>::detect(shared_ptr<ODScene> scene); + + extern template + void ODDetectorMultiAlgo<pcl::PointXYZRGBA>::init(); + + extern template + shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZRGBA>::detect(shared_ptr<ODScenePointCloud<pcl::PointXYZRGBA> > scene); + + extern template + shared_ptr<ODDetections3D> ODDetectorMultiAlgo<pcl::PointXYZRGBA>::detectOmni(shared_ptr<ODScenePointCloud<pcl::PointXYZRGBA> > scene); + + +#endif + } \ No newline at end of file diff --git a/detectors/include/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h b/detectors/include/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h index 609766f0..377a0a41 100644 --- a/detectors/include/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h +++ b/detectors/include/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h @@ -50,7 +50,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <opencv2/xfeatures2d.hpp> - #define VIEW_ANGLE 30 #define NO_SNAPSHOTS 30 diff --git a/detectors/src/global2D/CMakeLists.txt b/detectors/src/global2D/CMakeLists.txt index 4443e437..4a4f09f8 100644 --- a/detectors/src/global2D/CMakeLists.txt +++ b/detectors/src/global2D/CMakeLists.txt @@ -2,7 +2,7 @@ set(SUBSYS_NAME global_image_detector) set(LIB_NAME od_${SUBSYS_NAME}) set(SUBSYS_DESC "global feature based detection in 2D images") -set(SUBSYS_DEPS od_common ${OpenCV_LIBS} svmlight) +set(SUBSYS_DEPS od_common svmlight) set(build TRUE) diff --git a/detectors/src/global3D/CMakeLists.txt b/detectors/src/global3D/CMakeLists.txt index 91dcd783..c169aacb 100644 --- a/detectors/src/global3D/CMakeLists.txt +++ b/detectors/src/global3D/CMakeLists.txt @@ -1,7 +1,7 @@ set(SUBSYS_NAME pointcloud_global_detector) set(LIB_NAME od_${SUBSYS_NAME}) -set(SUBSYS_DESC "The global detector for point cloouds") -set(SUBSYS_DEPS od_common ${PCL_LIBRARIES} ${VTK_LIBRARIES} ${OpenCV_LIBS} siftgpu) +set(SUBSYS_DESC "The global detector for point clouds") +set(SUBSYS_DEPS od_common siftgpu) set(build TRUE) @@ -10,6 +10,7 @@ if(build) set(SOURCES "ODPointCloudGlobalMatching.cpp" "training/ODCADDetectTrainer3DGlobal.cpp" + "detection/ODCADDetector3DGlobal.cpp" ) include_directories(${DETECTORS_INCLUDE_DIR}) diff --git a/detectors/src/global3D/detection/ODCADDetector3DGlobal.cpp b/detectors/src/global3D/detection/ODCADDetector3DGlobal.cpp new file mode 100644 index 00000000..a7e07984 --- /dev/null +++ b/detectors/src/global3D/detection/ODCADDetector3DGlobal.cpp @@ -0,0 +1,59 @@ +#include "od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp" +#include "od/common/pipeline/ODDetection.h" + +namespace od { + + namespace g3d { + + template + shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZ>::detect(shared_ptr<ODScene> scene); + + template + shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZ>::detectOmni(shared_ptr<ODScene> scene); + + template + void ODCADDetector3DGlobal<pcl::PointXYZ>::init(); + + template + shared_ptr<ODDetections3D> ODCADDetector3DGlobal<pcl::PointXYZ>::detectOmni(shared_ptr<ODScenePointCloud<pcl::PointXYZ> > scene); + + template + shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZ>::detect(shared_ptr<ODScenePointCloud<pcl::PointXYZ> > scene); + + + + template + shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZRGB>::detect(shared_ptr<ODScene> scene); + + template + shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZRGB>::detectOmni(shared_ptr<ODScene> scene); + + template + void ODCADDetector3DGlobal<pcl::PointXYZRGB>::init(); + + template + shared_ptr<ODDetections3D> ODCADDetector3DGlobal<pcl::PointXYZRGB>::detectOmni(shared_ptr<ODScenePointCloud<pcl::PointXYZRGB> > scene); + + template + shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZRGB>::detect(shared_ptr<ODScenePointCloud<pcl::PointXYZRGB> > scene); + + + + template + shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZRGBA>::detect(shared_ptr<ODScene> scene); + + template + shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZRGBA>::detectOmni(shared_ptr<ODScene> scene); + + template + void ODCADDetector3DGlobal<pcl::PointXYZRGBA>::init(); + + template + shared_ptr<ODDetections3D> ODCADDetector3DGlobal<pcl::PointXYZRGBA>::detectOmni(shared_ptr<ODScenePointCloud<pcl::PointXYZRGBA> > scene); + + template + shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZRGBA>::detect(shared_ptr<ODScenePointCloud<pcl::PointXYZRGBA> > scene); + + } + +} \ No newline at end of file diff --git a/detectors/src/local2D/CMakeLists.txt b/detectors/src/local2D/CMakeLists.txt index 7816b64d..2d8d036c 100644 --- a/detectors/src/local2D/CMakeLists.txt +++ b/detectors/src/local2D/CMakeLists.txt @@ -1,7 +1,7 @@ set(SUBSYS_NAME local_image_detector) set(LIB_NAME od_${SUBSYS_NAME}) set(SUBSYS_DESC "The local feature matching based detector") -set(SUBSYS_DEPS od_common ${PCL_LIBRARIES} ${VTK_LIBRARIES} ${OpenCV_LIBS} pugixml) +set(SUBSYS_DEPS od_common pugixml) set(build TRUE) diff --git a/detectors/src/misc/CMakeLists.txt b/detectors/src/misc/CMakeLists.txt new file mode 100644 index 00000000..3939a36b --- /dev/null +++ b/detectors/src/misc/CMakeLists.txt @@ -0,0 +1,20 @@ +set(SUBSYS_NAME misc_detector) +set(LIB_NAME od_${SUBSYS_NAME}) +set(SUBSYS_DESC "Multi algorithm detector") +set(SUBSYS_DEPS od_common od_global_image_detector od_pointcloud_global_detector) + +set(build TRUE) + +if(build) + + set(SOURCES + "detection/ODDetectorMultiAlgo.cpp" + ) + + include_directories(${DETECTORS_IMPL_DIR}) + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + + OD_ADD_LIBRARY("${SUBSYS_NAME}" SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) + +endif(build) \ No newline at end of file diff --git a/detectors/src/misc/detection/ODDetectorMultiAlgo.cpp b/detectors/src/misc/detection/ODDetectorMultiAlgo.cpp new file mode 100644 index 00000000..09012d0e --- /dev/null +++ b/detectors/src/misc/detection/ODDetectorMultiAlgo.cpp @@ -0,0 +1,107 @@ +#include "od/detectors/misc/detection/ODDetectorMultiAlgo.hpp" + + +namespace od +{ + + template + shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZ>::detectOmni(shared_ptr<ODScene> scene); + + template + shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZ>::detect(shared_ptr<ODScene> scene); + + template + shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZ>::detect(shared_ptr<ODSceneImage> scene); + + template + shared_ptr<ODDetections2D> ODDetectorMultiAlgo2D<pcl::PointXYZ>::detectOmni(shared_ptr<ODSceneImage > scene); + + template + void ODDetectorMultiAlgo2D<pcl::PointXYZ>::init(); + + + + template + shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZRGB>::detectOmni(shared_ptr<ODScene> scene); + + template + shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZRGB>::detect(shared_ptr<ODScene> scene); + + template + shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZRGB>::detect(shared_ptr<ODSceneImage> scene); + + template + shared_ptr<ODDetections2D> ODDetectorMultiAlgo2D<pcl::PointXYZRGB>::detectOmni(shared_ptr<ODSceneImage > scene); + + template + void ODDetectorMultiAlgo2D<pcl::PointXYZRGB>::init(); + + + + template + shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZRGBA>::detectOmni(shared_ptr<ODScene> scene); + + template + shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZRGBA>::detect(shared_ptr<ODScene> scene); + + template + shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZRGBA>::detect(shared_ptr<ODSceneImage> scene); + + template + shared_ptr<ODDetections2D> ODDetectorMultiAlgo2D<pcl::PointXYZRGBA>::detectOmni(shared_ptr<ODSceneImage > scene); + + template + void ODDetectorMultiAlgo2D<pcl::PointXYZRGBA>::init(); + + + + template + shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZ>::detectOmni(shared_ptr<ODScene> scene); + + template + shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZ>::detect(shared_ptr<ODScene> scene); + + template + void ODDetectorMultiAlgo<pcl::PointXYZ>::init(); + + template + shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZ>::detect(shared_ptr<ODScenePointCloud<pcl::PointXYZ> > scene); + + template + shared_ptr<ODDetections3D> ODDetectorMultiAlgo<pcl::PointXYZ>::detectOmni(shared_ptr<ODScenePointCloud<pcl::PointXYZ> > scene); + + + + template + shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZRGB>::detectOmni(shared_ptr<ODScene> scene); + + template + shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZRGB>::detect(shared_ptr<ODScene> scene); + + template + void ODDetectorMultiAlgo<pcl::PointXYZRGB>::init(); + + template + shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZRGB>::detect(shared_ptr<ODScenePointCloud<pcl::PointXYZRGB> > scene); + + template + shared_ptr<ODDetections3D> ODDetectorMultiAlgo<pcl::PointXYZRGB>::detectOmni(shared_ptr<ODScenePointCloud<pcl::PointXYZRGB> > scene); + + + + template + shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZRGBA>::detectOmni(shared_ptr<ODScene> scene); + + template + shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZRGBA>::detect(shared_ptr<ODScene> scene); + + template + void ODDetectorMultiAlgo<pcl::PointXYZRGBA>::init(); + + template + shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZRGBA>::detect(shared_ptr<ODScenePointCloud<pcl::PointXYZRGBA> > scene); + + template + shared_ptr<ODDetections3D> ODDetectorMultiAlgo<pcl::PointXYZRGBA>::detectOmni(shared_ptr<ODScenePointCloud<pcl::PointXYZRGBA> > scene); + +} \ No newline at end of file diff --git a/examples/apps/CMakeLists.txt b/examples/apps/CMakeLists.txt index 07e8fcf5..08262193 100644 --- a/examples/apps/CMakeLists.txt +++ b/examples/apps/CMakeLists.txt @@ -8,7 +8,7 @@ include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) OD_ADD_EXAMPLE(odcadrecog_test_single_model FILES cadrecog2D/od_test_single_db_single_model.cpp - LINK_WITH od_common od_local_image_detector + LINK_WITH od_local_image_detector ) @@ -17,7 +17,7 @@ add_executable(opendetection ${SOURCE_FILES}) OD_ADD_EXAMPLE(od_multihog_app FILES global2D/od_multihog_app.cpp - LINK_WITH od_common od_global_image_detector + LINK_WITH od_global_image_detector ) OD_ADD_EXAMPLE(viewer_test diff --git a/examples/objectdetector/CMakeLists.txt b/examples/objectdetector/CMakeLists.txt index 05e07036..f4d9897c 100644 --- a/examples/objectdetector/CMakeLists.txt +++ b/examples/objectdetector/CMakeLists.txt @@ -10,91 +10,91 @@ option(camera_recognition_example "Build the camera recognition example" ON) if(od_image_camera_example) OD_ADD_EXAMPLE(od_image_camera FILES od_image_cadrecog_camera.cpp - LINK_WITH od_common od_local_image_detector) + LINK_WITH od_local_image_detector) endif() option(image_recognition_example "Build the image recognition example" ON) if(image_recognition_example) OD_ADD_EXAMPLE(od_example_files FILES od_image_cadrecog_files.cpp - LINK_WITH od_common od_local_image_detector) + LINK_WITH od_local_image_detector) endif() option(hog_train_example "Build the hog train example" ON) if(hog_train_example) OD_ADD_EXAMPLE(od_hog_train FILES od_hog_train.cpp - LINK_WITH od_common od_global_image_detector) + LINK_WITH od_global_image_detector) endif() option(image_hog_files_example "Build the hog image example" ON) if(image_hog_files_example) OD_ADD_EXAMPLE(od_image_hog_files FILES od_image_hog_files.cpp - LINK_WITH od_common od_global_image_detector) + LINK_WITH od_global_image_detector) endif() option(image_customhog_example "Build the custom hog example" ON) if(image_customhog_example) OD_ADD_EXAMPLE(od_image_customhog FILES od_image_customhog.cpp - LINK_WITH od_common od_global_image_detector) + LINK_WITH od_global_image_detector) endif() option(multialgo_files_example "Build the multialgo_files example" ON) if(multialgo_files_example) OD_ADD_EXAMPLE(od_multialgo_files FILES od_multialgo_files.cpp - LINK_WITH od_common od_global_image_detector) + LINK_WITH od_global_image_detector od_misc_detector) endif() option(multialgo_pc_example "Build the multialgo_pc example" ON) if(multialgo_pc_example) OD_ADD_EXAMPLE(od_multialgo_pc FILES od_multialgo_pc.cpp - LINK_WITH od_common od_global_image_detector) + LINK_WITH od_global_image_detector od_misc_detector od_pointcloud_global_detector) endif() option(cascade_cam_example "Build the cascade camera example" ON) if(cascade_cam_example) OD_ADD_EXAMPLE(od_cascade_cam FILES od_cascade_cam.cpp - LINK_WITH od_common od_global_image_detector) + LINK_WITH od_global_image_detector) endif() option(cascade_files_example "Build the cascade file example" ON) if(cascade_files_example) OD_ADD_EXAMPLE(od_cascade_files FILES od_cascade_files.cpp - LINK_WITH od_common od_global_image_detector) + LINK_WITH od_global_image_detector) endif() option(image_facerecog_example "Build the face recognition example" ON) if(image_facerecog_example) OD_ADD_EXAMPLE(od_image_facerecog FILES od_image_facerecog.cpp - LINK_WITH od_common od_global_image_detector) + LINK_WITH od_global_image_detector) endif() option(pc_global_example "Build the global detector example" ON) if(pc_global_example) OD_ADD_EXAMPLE(od_example_pc_global FILES od_pc_global.cpp - LINK_WITH od_common od_pointcloud_global_detector) + LINK_WITH od_pointcloud_global_detector) endif() option(pc_global_files_example "Build the global detector file example" ON) if(pc_global_files_example) OD_ADD_EXAMPLE(od_example_pc_global_files FILES od_pc_global_files.cpp - LINK_WITH od_common od_pointcloud_global_detector) + LINK_WITH od_pointcloud_global_detector) endif() option(pc_global_real_time_example "Build the global detector real time example" ON) if(pc_global_real_time_example) OD_ADD_EXAMPLE(od_example_pc_global_real_time FILES od_pc_global_real_time.cpp - LINK_WITH od_common od_pointcloud_global_detector) + LINK_WITH od_pointcloud_global_detector) endif() option(framegenerator_example "Build the frame generator example" ON) From 657123522ef0e9649dbe97c99f11c9ca6c2dc231 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Mon, 27 Jun 2016 16:04:46 +0200 Subject: [PATCH 35/79] adds blogpost --- .../gsoc2016_blog_giacomo.md | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md index 4edba641..75964c36 100644 --- a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md +++ b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md @@ -174,7 +174,7 @@ The following step took quite a bit in order to have everything work well. Until - Substituting a pointer with a shared pointer requirest also to use another set of functions for the creation and the casting. to create a **shared_ptr** you either pass the pointer to the constructor or use the **make_shared** template passing to the function the constructor arguments and as template argument the pointer type. Also static and dynamic casting have specific template functions which are **dynamic_pointer_cast** and **static_pointer_casto**. -- It is important to remove all delete which are around the code since **shared_ptr* automatically destroy the pointed objects in the destructor if not referenced anymore. +- It is important to remove all delete which are around the code since **shared_ptr** automatically destroy the pointed objects in the destructor if not referenced anymore. While restructuring the code I also fixed again some coding style issues, but I still have to go through the code a few more times in order to finish fixing everything. The next step is as previously mentioned, to check the compilation with the svmlight option. @@ -195,10 +195,26 @@ link_directories(${OD_LIBRARY_PATH}) target_link_libraries(example_target ${OD_LIBRARIES}) @endcode -The **FindOD.cmake** is generated automatically starting from the **${CMAKE_INSTALL_PREFIX}** variable so there is no absolute path and it is installed in ****${CMAKE_INSTALL_PREFIX}/lib/cmake/** . +The **FindOD.cmake** is generated automatically starting from the **${CMAKE_INSTALL_PREFIX}** variable so there is no absolute path and it is installed in **${CMAKE_INSTALL_PREFIX}/lib/cmake/** . I added then an **uninstall** target to delete all the installed files if necessary. This custom target is standard and can be found easily on the internet. Another important missing thing was the creation of a .deb package which can be useful to install the library on other systems. This package has been created using the **Cpack** utility provided by cmake. By just setting some basic variables it is possible to create e complete .deb file which can then be installed using the usual *dpkg -i package.deb* command. -All files in the library have been renamed according to a common scheme which consists in a Prefix "OD" followed by the first charachter of the file name uppercase. \ No newline at end of file +All files in the library have been renamed according to a common scheme which consists in a Prefix "OD" followed by the first charachter of the file name uppercase. + +##Templates and Windows compilation## + +To improve compilation time of projects using the **OpenDetection** library I decided to precompile all the templates for the most common types. In this case almost all templates depend on *pcl* point types. The most commonly used point type for which I precompiled the library are: + +- *pcl::PointXYZ* +- *pcl::PointXYZRGB* +- *pcl::PointXYZRGBA* + +It is important to create for each .hpp header file containing the templates a .cpp file in which templates are instantiated with the different types. Explicit template instantiation is done by just writing *template* followed by the template declaration with the instantiated type. +The most important part is to insert in the header file the same declaration with the *extern* keyword at the beginning. This informs the compiler that the defined symbol is already present in another compilation unit and that it can be found at linking time. Without this the compiler will just reinstantiate the templates and recompile them since it has no knowledge about templates instantiated and compiled in other compilation units. + +I then fixed all the linking in the whole library since there where some useless linked libraries in order to speed up even more the compilation. + +After that I tested compilation of the library under Windows, but without success. There have been several issues which are ut of the Open Deteciton library. I tested compilation using **Visual Studio 2015** and **MinGW**. Pcl has been installed by using the prebuilt version while opencv has to be built manually since *opencv_contrib* is necessary while not present in the prebuilt binaries. Building opencv3 with visual studio and cuda is not possible at the moment since cuda 7.5 is not compatible with visual studio 2015, so version 8 is necessary which will be available soon. MinGW is also not usable since compilation of opencv3 with cuda is disabled as flagged as incompatible by opencv. + From 3addef626674269381ed161d8e9277cc2d8e5e42 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Tue, 28 Jun 2016 14:54:20 +0200 Subject: [PATCH 36/79] some minor changes to split better cpp and h --- .../od/detectors/global2D/ODFaceRecognizer.h | 41 ++--- .../global2D/training/ODHOGTrainer.h | 142 +++++------------- .../training/ODCADDetectTrainer3DGlobal.h | 1 + detectors/src/global2D/ODFaceRecognizer.cpp | 30 ++++ .../src/global2D/training/ODHOGTrainer.cpp | 105 +++++++++++++ 5 files changed, 180 insertions(+), 139 deletions(-) diff --git a/detectors/include/od/detectors/global2D/ODFaceRecognizer.h b/detectors/include/od/detectors/global2D/ODFaceRecognizer.h index 081eba75..98f83ebf 100644 --- a/detectors/include/od/detectors/global2D/ODFaceRecognizer.h +++ b/detectors/include/od/detectors/global2D/ODFaceRecognizer.h @@ -70,35 +70,14 @@ namespace od virtual int detect(shared_ptr<ODScene> scene, const std::vector<shared_ptr<ODDetection> > & detections); - const FaceRecogType & getRecogtype() const - { - return recog_type_; - } - - void setRecogtype(const FaceRecogType & recog_type) - { - recog_type_ = recog_type; - } - - int getThreshold() const - { - return threshold_; - } - - void setThreshold(int threshold) - { - threshold_ = threshold; - } - - int getNumComponents() const - { - return num_components_; - } - - void setNumComponents(int num_components) - { - num_components_ = num_components; - } + const FaceRecogType & getRecogtype() const; + void setRecogtype(const FaceRecogType & recog_type); + + int getThreshold() const; + void setThreshold(int threshold); + + int getNumComponents() const; + void setNumComponents(int num_components); protected: @@ -110,10 +89,10 @@ namespace od unsigned int im_height_; unsigned int im_width_; - private: - void read_csv(const std::string & file_name, std::vector<cv::Mat> & images, std::vector<int> & labels, const std::string & separator = std::string(";")); + void read_csv(const std::string & file_name, std::vector<cv::Mat> & images, std::vector<int> & labels, + const std::string & separator = std::string(";")); }; diff --git a/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h b/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h index 8bdc9513..d93c2645 100644 --- a/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h +++ b/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h @@ -59,110 +59,37 @@ namespace od void init(){} - const std::string & getPosSamplesDir() const - { - return pos_samples_dir_; - } - - void setPosSamplesDir(const std::string & pos_samples_dir) - { - pos_samples_dir_ = pos_samples_dir; - } - - const std::string & getNegSamplesDir() const - { - return neg_samples_dir_; - } - - void setNegSamplesDir(const std::string & neg_samples_dir) - { - neg_samples_dir_ = neg_samples_dir; - } - - int getNOFeaturesNeg() const - { - return no_features_neg_; - } - - void setNOFeaturesNeg(int featno) - { - no_features_neg_ = featno; - } - - const cv::Point & getStartHogPos() const - { - return start_hog_pos_; - } - - void setStartHogPos(const cv::Point & start_hog_pos) - { - start_hog_pos_ = start_hog_pos; - } - - const cv::Size & getWinSize() const - { - return win_size_; - } - - void setWinSize(const cv::Size & win_size) - { - win_size_ = win_size; - } - - const cv::Size & getBlockSize() const - { - return block_size_; - } - - void setBlockSize(const cv::Size & block_size) - { - block_size_ = block_size; - } - - const cv::Size & getBlockStride() const - { - return block_stride_; - } - - void setBlockStride(const cv::Size & block_stride) - { - block_stride_ = block_stride; - } - - const cv::Size & getCellSize() const - { - return cell_size_; - } - - void setCellSize(const cv::Size & cell_size) - { - cell_size_ = cell_size; - } - - const cv::Size & getTrainingPadding() const - { - return training_padding_; - } - - void setTrainingPadding(const cv::Size & training_padding) - { - training_padding_ = training_padding; - } - - bool isTrainHardNegetive() const - { - return train_hard_negative_; - } - - void setTrainHardNegetive(bool train_hard_negative) - { - train_hard_negative_ = train_hard_negative; - } - - double getHitThreshold() const - { - return hit_threshold_; - } + const std::string & getPosSamplesDir() const; + void setPosSamplesDir(const std::string & pos_samples_dir); + + const std::string & getNegSamplesDir() const; + void setNegSamplesDir(const std::string & neg_samples_dir); + + int getNOFeaturesNeg() const; + void setNOFeaturesNeg(int featno); + + const cv::Point & getStartHogPos() const; + void setStartHogPos(const cv::Point & start_hog_pos); + + const cv::Size & getWinSize() const; + void setWinSize(const cv::Size & win_size); + + const cv::Size & getBlockSize() const; + void setBlockSize(const cv::Size & block_size); + + const cv::Size & getBlockStride() const; + void setBlockStride(const cv::Size & block_stride); + + const cv::Size & getCellSize() const; + void setCellSize(const cv::Size & cell_size); + + const cv::Size & getTrainingPadding() const; + void setTrainingPadding(const cv::Size & training_padding); + + bool isTrainHardNegetive() const; + void setTrainHardNegetive(bool train_hard_negative); + + double getHitThreshold() const; protected: //hog specific @@ -170,19 +97,18 @@ namespace od cv::Size block_size_; cv::Size block_stride_; cv::Size cell_size_; + cv::Size win_stride_; + cv::Size training_padding_; cv::HOGDescriptor hog_; //algo specific - cv::Size training_padding_; cv::Point start_hog_pos_; unsigned int no_features_neg_; - cv::Size win_stride_; bool train_hard_negative_; //directories - std::string pos_samples_dir_; - std::string neg_samples_dir_; + std::string pos_samples_dir_, neg_samples_dir_; //properties retained double hit_threshold_; diff --git a/detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h b/detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h index 928d15a1..d58a66f4 100644 --- a/detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h +++ b/detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h @@ -69,6 +69,7 @@ namespace od { public: + ODCADDetectTrainer3DGlobal(const std::string & training_input_location_ = std::string(""), const std::string & training_data_location_ = std::string("")); diff --git a/detectors/src/global2D/ODFaceRecognizer.cpp b/detectors/src/global2D/ODFaceRecognizer.cpp index 653b9f7e..03fb0b9c 100644 --- a/detectors/src/global2D/ODFaceRecognizer.cpp +++ b/detectors/src/global2D/ODFaceRecognizer.cpp @@ -178,6 +178,36 @@ namespace od } } } + + const ODFaceRecognizer::FaceRecogType & ODFaceRecognizer::getRecogtype() const + { + return recog_type_; + } + + void ODFaceRecognizer::setRecogtype(const ODFaceRecognizer::FaceRecogType & recog_type) + { + recog_type_ = recog_type; + } + + int ODFaceRecognizer::getThreshold() const + { + return threshold_; + } + + void ODFaceRecognizer::setThreshold(int threshold) + { + threshold_ = threshold; + } + + int ODFaceRecognizer::getNumComponents() const + { + return num_components_; + } + + void ODFaceRecognizer::setNumComponents(int num_components) + { + num_components_ = num_components; + } } } \ No newline at end of file diff --git a/detectors/src/global2D/training/ODHOGTrainer.cpp b/detectors/src/global2D/training/ODHOGTrainer.cpp index 55e81b9a..c1bb848f 100644 --- a/detectors/src/global2D/training/ODHOGTrainer.cpp +++ b/detectors/src/global2D/training/ODHOGTrainer.cpp @@ -406,5 +406,110 @@ namespace od hog_.write(fs, cv::FileStorage::getDefaultObjectName(file_name)); } + const std::string & ODHOGTrainer::getPosSamplesDir() const + { + return pos_samples_dir_; + } + + void ODHOGTrainer::setPosSamplesDir(const std::string & pos_samples_dir) + { + pos_samples_dir_ = pos_samples_dir; + } + + const std::string & ODHOGTrainer::getNegSamplesDir() const + { + return neg_samples_dir_; + } + + void ODHOGTrainer::setNegSamplesDir(const std::string & neg_samples_dir) + { + neg_samples_dir_ = neg_samples_dir; + } + + int ODHOGTrainer::getNOFeaturesNeg() const + { + return no_features_neg_; + } + + void ODHOGTrainer::setNOFeaturesNeg(int featno) + { + no_features_neg_ = featno; + } + + const cv::Point & ODHOGTrainer::getStartHogPos() const + { + return start_hog_pos_; + } + + void ODHOGTrainer::setStartHogPos(const cv::Point & start_hog_pos) + { + start_hog_pos_ = start_hog_pos; + } + + const cv::Size & ODHOGTrainer::getWinSize() const + { + return win_size_; + } + + void ODHOGTrainer::setWinSize(const cv::Size & win_size) + { + win_size_ = win_size; + } + + const cv::Size & ODHOGTrainer::getBlockSize() const + { + return block_size_; + } + + void ODHOGTrainer::setBlockSize(const cv::Size & block_size) + { + block_size_ = block_size; + } + + const cv::Size & ODHOGTrainer::getBlockStride() const + { + return block_stride_; + } + + void ODHOGTrainer::setBlockStride(const cv::Size & block_stride) + { + block_stride_ = block_stride; + } + + const cv::Size & ODHOGTrainer::getCellSize() const + { + return cell_size_; + } + + void ODHOGTrainer::setCellSize(const cv::Size & cell_size) + { + cell_size_ = cell_size; + } + + const cv::Size & ODHOGTrainer::getTrainingPadding() const + { + return training_padding_; + } + + void ODHOGTrainer::setTrainingPadding(const cv::Size & training_padding) + { + training_padding_ = training_padding; + } + + bool ODHOGTrainer::isTrainHardNegetive() const + { + return train_hard_negative_; + } + + void ODHOGTrainer::setTrainHardNegetive(bool train_hard_negative) + { + train_hard_negative_ = train_hard_negative; + } + + double ODHOGTrainer::getHitThreshold() const + { + return hit_threshold_; + } + } } From 2f373006cc4b3cba20cc4ce125effcfcb6e2045e Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Thu, 30 Jun 2016 15:32:50 +0200 Subject: [PATCH 37/79] fixes detector types and adds face detection --- CMakeLists.txt | 4 + .../include/od/common/pipeline/ODDetection.h | 7 +- .../include/od/common/pipeline/ODDetector.h | 5 +- .../od/common/pipeline/ODObjectDetector.h | 1 + .../detection/ODCADDetector3DGlobal.hpp | 4 +- .../global2D/detection/ODFaceDetector.h | 61 ++++++++++ .../training/ODCADDetectTrainer3DGlobal.h | 1 - .../detectors/local2D/ODImageLocalMatching.h | 1 - detectors/src/global2D/CMakeLists.txt | 1 + detectors/src/global2D/ODFaceRecognizer.cpp | 2 +- .../global2D/detection/ODCascadeDetector.cpp | 4 +- .../src/global2D/detection/ODFaceDetector.cpp | 107 ++++++++++++++++++ .../src/global2D/detection/ODHOGDetector.cpp | 4 +- .../detection/ODCADRecognizer2DLocal.cpp | 2 +- 14 files changed, 188 insertions(+), 16 deletions(-) create mode 100644 detectors/include/od/detectors/global2D/detection/ODFaceDetector.h create mode 100644 detectors/src/global2D/detection/ODFaceDetector.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 3afc66d5..f2e19202 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,6 +45,10 @@ if(WITH_BOOST_SHARED_PTR) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWITH_BOOST_SHARED_PTR") endif() +if(WITH_GPU) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWITH_GPU") +endif() + # Set modules set(OD_MODULES_NAMES 3rdparty common detectors doc examples) set(OD_MODULES_DIRS ${OD_MODULES_NAMES}) diff --git a/common/include/od/common/pipeline/ODDetection.h b/common/include/od/common/pipeline/ODDetection.h index 4b24a559..756f6d9e 100644 --- a/common/include/od/common/pipeline/ODDetection.h +++ b/common/include/od/common/pipeline/ODDetection.h @@ -41,8 +41,8 @@ namespace od /** \brief The base class of all the detection. * - * This is the base class of all the detection classes containing the detection information. All the ODDetector s return a collection of this class (in the form of ODDetections). - * Supports two modes: recognition (with type OD_DETECTION_RECOG) and classification (with type OD_DETECTION_CLASS). Along with the type, ODDetector sets an ID to identify what class or what instance of recognition is detected/recognied. + * This is the base class of all the detection classes containing the detection information. All the ODDetectors return a collection of this class (in the form of ODDetections). + * Supports three modes: recognition (with type OD_RECOGNITION) and classification (with type OD_CLASSIFICATION) and detection (with type OD_DETECTION). Along with the type, ODDetector sets an ID to identify what class or what instance of recognition is detected/recognied. * * \author Kripasindhu Sarkar * @@ -52,7 +52,7 @@ namespace od public: - OD_DEFINE_ENUM_WITH_STRING_CONVERSIONS(DetectionType, (OD_DETECTION_RECOG)(OD_DETECTION_CLASS)(OD_DETECTION_NULL)) + OD_DEFINE_ENUM_WITH_STRING_CONVERSIONS(DetectionType, (OD_RECOGNITION)(OD_CLASSIFICATION)(OD_DETECTION)(OD_DETECTION_NULL)) virtual ~ODDetection() {} @@ -105,7 +105,6 @@ namespace od const cv::Mat & getMetainfoImage() const; void setMetainfoImage(const cv::Mat & metainfo_image); - Eigen::Vector3d location_2d_; cv::Rect bounding_box_2d_; cv::Mat metainfo_image_; diff --git a/common/include/od/common/pipeline/ODDetector.h b/common/include/od/common/pipeline/ODDetector.h index 0adc97c8..3e97f0f6 100644 --- a/common/include/od/common/pipeline/ODDetector.h +++ b/common/include/od/common/pipeline/ODDetector.h @@ -46,8 +46,8 @@ namespace od { public: - ODDetector(const std::string & training_data_location) : ODDetectorCommon(training_data_location) - {} + ODDetector(const std::string & training_data_location) : ODDetectorCommon(training_data_location) {} + ODDetector() {} virtual shared_ptr<ODDetections> detect(shared_ptr<ODScene> scene) = 0; virtual shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene) = 0; @@ -67,6 +67,7 @@ namespace od { public: ODDetector2D(const std::string & trained_data_location) : ODDetector(trained_data_location) {} + ODDetector2D() {} shared_ptr<ODDetections> detect(shared_ptr<ODScene> scene) { diff --git a/common/include/od/common/pipeline/ODObjectDetector.h b/common/include/od/common/pipeline/ODObjectDetector.h index b6aff287..b7363f73 100644 --- a/common/include/od/common/pipeline/ODObjectDetector.h +++ b/common/include/od/common/pipeline/ODObjectDetector.h @@ -56,6 +56,7 @@ namespace od public: ODDetectorCommon(const std::string & trained_data_location); + ODDetectorCommon() {} virtual void init() = 0; diff --git a/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp b/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp index b0ef4ca7..29eddd8e 100644 --- a/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp +++ b/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp @@ -210,7 +210,7 @@ namespace od //now fill up the detection: shared_ptr<ODDetection3D> detection = make_shared<ODDetection3D>(); - detection->setType(ODDetection::OD_DETECTION_CLASS); + detection->setType(ODDetection::OD_CLASSIFICATION); detection->setId(categories[0]); detection->setLocation(centroid); detection->setMetainfoCluster(clusters[i]); @@ -240,7 +240,7 @@ namespace od //detection done! //now fill up the detection: - shared_ptr<ODDetection> detection = make_shared<ODDetection3D>(ODDetection::OD_DETECTION_CLASS, categories[0], conf[0]); + shared_ptr<ODDetection> detection = make_shared<ODDetection3D>(ODDetection::OD_CLASSIFICATION, categories[0], conf[0]); detections->push_back(detection); return detections; diff --git a/detectors/include/od/detectors/global2D/detection/ODFaceDetector.h b/detectors/include/od/detectors/global2D/detection/ODFaceDetector.h new file mode 100644 index 00000000..70fd0740 --- /dev/null +++ b/detectors/include/od/detectors/global2D/detection/ODFaceDetector.h @@ -0,0 +1,61 @@ + +#pragma once +#include "opencv2/highgui/highgui.hpp" +#if WITH_GPU +#include "opencv2/cudaobjdetect.hpp" +#include "opencv2/cudaimgproc.hpp" +#include "opencv2/cudawarping.hpp" +#endif +#include "od/common/utils/ODShared_pointers.h" +#include "od/common/pipeline/ODDetector.h" +#include "opencv2/objdetect/objdetect.hpp" +#include "opencv2/imgproc/imgproc.hpp" + +namespace od +{ + namespace g2d + { + + class ODFaceDetector2D: public ODDetector2D { + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + + OD_DEFINE_ENUM_WITH_STRING_CONVERSIONS(FaceRecogType, (GPU)(CPU)) +#endif + + public: + + ODFaceDetector2D(ODFaceDetector2D::FaceRecogType type, const std::string & path = std::string()); + + shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene); + shared_ptr<ODDetections2D> detectOmni(shared_ptr<ODSceneImage> scene); + + + void setScale(const float scale); + float getScale() const; + + void setMinNeighbors(const unsigned int mn); + unsigned int getMinNeighbors() const; + + void setMinSize(const cv::Size & size); + cv::Size getMinSize() const; + + void setMaxSize(const cv::Size & size); + cv::Size getMaxSize() const; + + private: + FaceRecogType type_; + std::vector<cv::Rect> faces; + cv::Ptr<cv::cuda::CascadeClassifier> face_cascade_gpu_; + float scale_; + unsigned int mn_; + cv::Size min_size_, max_size_; + +#ifdef WITH_GPU + shared_ptr<cv::CascadeClassifier> face_cascade_; + cv::cuda::GpuMat gray_gpu_, faces_buf_gpu_, color_gpu_; +#endif + + }; + } +} \ No newline at end of file diff --git a/detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h b/detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h index d58a66f4..7c859835 100644 --- a/detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h +++ b/detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h @@ -28,7 +28,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // #pragma once #include <pcl/apps/3d_rec_framework/pc_source/mesh_source.h> -#include <pcl/console/parse.h> #include "od/common/pipeline/ODTrainer.h" namespace od diff --git a/detectors/include/od/detectors/local2D/ODImageLocalMatching.h b/detectors/include/od/detectors/local2D/ODImageLocalMatching.h index df347145..96fbee21 100644 --- a/detectors/include/od/detectors/local2D/ODImageLocalMatching.h +++ b/detectors/include/od/detectors/local2D/ODImageLocalMatching.h @@ -83,7 +83,6 @@ namespace od void setDetector(shared_ptr<ODImageLocalMatchingDetector> detector); - ODImageLocalMatching(); void init() {} diff --git a/detectors/src/global2D/CMakeLists.txt b/detectors/src/global2D/CMakeLists.txt index 4a4f09f8..6cf0069c 100644 --- a/detectors/src/global2D/CMakeLists.txt +++ b/detectors/src/global2D/CMakeLists.txt @@ -12,6 +12,7 @@ if(build) "ODFaceRecognizer.cpp" "detection/ODCascadeDetector.cpp" "detection/ODHOGDetector.cpp" + "detection/ODFaceDetector.cpp" "training/ODHOGTrainer.cpp" ) diff --git a/detectors/src/global2D/ODFaceRecognizer.cpp b/detectors/src/global2D/ODFaceRecognizer.cpp index 03fb0b9c..4da16336 100644 --- a/detectors/src/global2D/ODFaceRecognizer.cpp +++ b/detectors/src/global2D/ODFaceRecognizer.cpp @@ -148,7 +148,7 @@ namespace od cv_recognizer_->predict(face_edited, label, confidence); //fill in the detection - shared_ptr<ODDetection2D> detection = make_shared<ODDetection2D>(ODDetection::OD_DETECTION_CLASS, std::to_string(label), confidence); + shared_ptr<ODDetection2D> detection = make_shared<ODDetection2D>(ODDetection::OD_CLASSIFICATION, std::to_string(label), confidence); shared_ptr<ODDetections2D> detections = make_shared<ODDetections2D>(); detections->push_back(detection); diff --git a/detectors/src/global2D/detection/ODCascadeDetector.cpp b/detectors/src/global2D/detection/ODCascadeDetector.cpp index fcfe9573..3e75e951 100644 --- a/detectors/src/global2D/detection/ODCascadeDetector.cpp +++ b/detectors/src/global2D/detection/ODCascadeDetector.cpp @@ -70,7 +70,7 @@ namespace od for(auto & face : faces) { // Process face by face: - shared_ptr<ODDetection2D> detection2D = make_shared<ODDetection2D>(ODDetection::OD_DETECTION_CLASS, "FACE", 1); + shared_ptr<ODDetection2D> detection2D = make_shared<ODDetection2D>(ODDetection::OD_CLASSIFICATION, "FACE", 1); detection2D->setBoundingBox(face); detections->push_back(detection2D); @@ -100,7 +100,7 @@ namespace od haar_cascade_->detectMultiScale(gray, faces, 5, min_neighbors_, 0, gray.size(), gray.size()); if(faces.size() > 0) { - detections->push_back(make_shared<ODDetection>(ODDetection::OD_DETECTION_CLASS, "FACE", 1)); + detections->push_back(make_shared<ODDetection>(ODDetection::OD_CLASSIFICATION, "FACE", 1)); } return detections; } diff --git a/detectors/src/global2D/detection/ODFaceDetector.cpp b/detectors/src/global2D/detection/ODFaceDetector.cpp new file mode 100644 index 00000000..ddf35bbd --- /dev/null +++ b/detectors/src/global2D/detection/ODFaceDetector.cpp @@ -0,0 +1,107 @@ +#include "od/detectors/global2D/detection/ODFaceDetector.h" + +namespace od +{ + namespace g2d + { + + ODFaceDetector2D::ODFaceDetector2D(ODFaceDetector2D::FaceRecogType type, const std::string & path) : + type_(type), scale_(1.1), mn_(2) + { + + std::string face_cascade = path.empty() ? "haarcascade_frontalface_alt.xml" : path; + if(type_ == GPU){ + face_cascade_gpu_ = cv::cuda::CascadeClassifier::create(face_cascade); + face_cascade_gpu_->setFindLargestObject(false); + + + }else{ + face_cascade_ = make_shared<cv::CascadeClassifier>(face_cascade); + } + } + + shared_ptr<ODDetections2D> ODFaceDetector2D::detectOmni(shared_ptr<ODSceneImage> scene) + { + + cv::Mat input = scene->getCVImage(); + faces.clear(); + + if(type_ == GPU){ + + color_gpu_.upload(input); + cv::cuda::cvtColor(color_gpu_, gray_gpu_, CV_BGR2GRAY); + + face_cascade_gpu_->setScaleFactor(scale_); + face_cascade_gpu_->detectMultiScale(gray_gpu_, faces_buf_gpu_); + face_cascade_gpu_->convert(faces_buf_gpu_, faces); + + }else{ + + cv::Mat frame_gray; + + cv::cvtColor(input, frame_gray, CV_BGR2GRAY); + cv::equalizeHist(frame_gray, frame_gray); + + face_cascade_->detectMultiScale(frame_gray, faces, scale_, mn_, 0|CV_HAAR_SCALE_IMAGE, min_size_, max_size_); + + } + + shared_ptr<ODDetections2D> detections = make_shared<ODDetections2D>(); + + for(auto & face : faces){ + shared_ptr<ODDetection2D> detection = make_shared<ODDetection2D>(ODDetection::OD_DETECTION, std::string("face")); + detection->bounding_box_2d_ = face; + detections->push_back(detection); + } + + return detections; + } + + void ODFaceDetector2D::setScale(const float scale) + { + scale_ = scale; + } + + float ODFaceDetector2D::getScale() const + { + return scale_; + } + + void ODFaceDetector2D::setMinNeighbors(const unsigned int mn) + { + mn_ = mn; + } + + unsigned int ODFaceDetector2D::getMinNeighbors() const + { + return mn_; + } + + void ODFaceDetector2D::setMinSize(const cv::Size & size) + { + min_size_ = size; + } + + cv::Size ODFaceDetector2D::getMinSize() const + { + return min_size_; + } + + void ODFaceDetector2D::setMaxSize(const cv::Size & size) + { + max_size_ = size; + } + + cv::Size ODFaceDetector2D::getMaxSize() const + { + return max_size_; + } + + + shared_ptr<ODDetections> ODFaceDetector2D::detect(shared_ptr<ODSceneImage> scene) + { + std::cout << "Not implemented. Use detectOmi(shared_ptr<ODSceneImage>) instead." << std::endl; + return nullptr; + } + } +} \ No newline at end of file diff --git a/detectors/src/global2D/detection/ODHOGDetector.cpp b/detectors/src/global2D/detection/ODHOGDetector.cpp index 54c6d626..ac3da854 100644 --- a/detectors/src/global2D/detection/ODHOGDetector.cpp +++ b/detectors/src/global2D/detection/ODHOGDetector.cpp @@ -104,7 +104,7 @@ namespace od shared_ptr<ODDetection2D> detection2D = make_shared<ODDetection2D>(); detection2D->setBoundingBox(found[i]); detection2D->setId("PEOPLE"); - detection2D->setType(ODDetection::OD_DETECTION_CLASS); + detection2D->setType(ODDetection::OD_CLASSIFICATION); detections->push_back(detection2D); if(meta_info_) @@ -137,7 +137,7 @@ namespace od { shared_ptr<ODDetection2D> detection2D = make_shared<ODDetection2D>(); detection2D->setId("PEOPLE"); - detection2D->setType(ODDetection::OD_DETECTION_CLASS); + detection2D->setType(ODDetection::OD_CLASSIFICATION); detections->push_back(detection2D); } diff --git a/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp b/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp index a7a2f61a..2a7103b6 100644 --- a/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp +++ b/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp @@ -330,7 +330,7 @@ namespace od detection3D = make_shared<ODDetection3D>(); detection3D->setLocation(pnp_detection_.getTMatrix()); detection3D->setPose(pnp_detection_.getRMatrix()); - detection3D->setType(ODDetection::OD_DETECTION_RECOG); + detection3D->setType(ODDetection::OD_RECOGNITION); detection3D->setId(model.id_); return true; From c34aa67ba889f5a7ed256399cff0b054c2cfa390 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Mon, 4 Jul 2016 13:43:05 +0200 Subject: [PATCH 38/79] adds working cpu/gpu face detector with examples --- .../include/od/common/pipeline/ODDetection.h | 5 +- .../od/common/utils/ODFrameGenerator.h | 2 + common/src/pipeline/ODDetection.cpp | 21 + .../global2D/detection/ODFaceDetector.h | 18 +- .../src/global2D/detection/ODFaceDetector.cpp | 46 +- examples/data/haarcascade_frontalface_alt.xml | 26161 ++++++++++++++++ .../data/haarcascade_frontalface_alt_gpu.xml | 23550 ++++++++++++++ examples/data/lbpcascade_frontalcatface.xml | 3336 ++ examples/objectdetector/CMakeLists.txt | 14 + .../objectdetector/od_face_detector_cam.cpp | 47 + .../objectdetector/od_face_detector_files.cpp | 42 + 11 files changed, 53219 insertions(+), 23 deletions(-) create mode 100644 examples/data/haarcascade_frontalface_alt.xml create mode 100644 examples/data/haarcascade_frontalface_alt_gpu.xml create mode 100644 examples/data/lbpcascade_frontalcatface.xml create mode 100644 examples/objectdetector/od_face_detector_cam.cpp create mode 100644 examples/objectdetector/od_face_detector_files.cpp diff --git a/common/include/od/common/pipeline/ODDetection.h b/common/include/od/common/pipeline/ODDetection.h index 756f6d9e..dd5a1081 100644 --- a/common/include/od/common/pipeline/ODDetection.h +++ b/common/include/od/common/pipeline/ODDetection.h @@ -171,7 +171,7 @@ namespace od { public: - ODDetections (unsigned int n = 0); + ODDetections(unsigned int n = 0); virtual ~ODDetections(); @@ -213,6 +213,7 @@ namespace od /** \brief Draws rectangles over the input image using the bounding box information present in all the 2D detections. This is a quick function to render and verify the detections made. */ ODSceneImage renderMetainfo(ODSceneImage & input); + ODSceneImage renderMetainfo(shared_ptr<ODSceneImage> input); shared_ptr<ODDetection2D> operator[](unsigned int i); shared_ptr<ODDetection2D> at(unsigned int i); @@ -261,7 +262,7 @@ namespace od { public: - shared_ptr<ODDetectionComplete> operator[](unsigned int i); + shared_ptr<ODDetectionComplete> operator[](unsigned int i); shared_ptr<ODDetectionComplete> at(unsigned int i); }; diff --git a/common/include/od/common/utils/ODFrameGenerator.h b/common/include/od/common/utils/ODFrameGenerator.h index c59f1274..f5caa86e 100644 --- a/common/include/od/common/utils/ODFrameGenerator.h +++ b/common/include/od/common/utils/ODFrameGenerator.h @@ -99,6 +99,7 @@ namespace od ODFrameGenerator(const std::string & input = std::string("")) { + std::cout << "Opening :" << input << std::endl; input_capture_.open(input); if(!input_capture_.isOpened()) {std::cout << "FATAL: Cannot open video capture!" << std::endl;} @@ -106,6 +107,7 @@ namespace od ODFrameGenerator(int input = 0) { + std::cout << "Opening cam :" << input << std::endl; input_capture_.open(input); if(!input_capture_.isOpened()) {std::cout << "FATAL: Cannot open video capture!" << std::endl;} diff --git a/common/src/pipeline/ODDetection.cpp b/common/src/pipeline/ODDetection.cpp index 7f323195..496f16cc 100644 --- a/common/src/pipeline/ODDetection.cpp +++ b/common/src/pipeline/ODDetection.cpp @@ -234,6 +234,27 @@ namespace od return ODSceneImage(image); } + ODSceneImage ODDetections2D::renderMetainfo(shared_ptr<ODSceneImage> input) + { + + //picking up random colors for different detection algorithm, if exist + /*std::map<std::string, cv::Scalar> color_map; + for(int i = 0; i < detections_.size(); i++) + { + if(color_map.find(detections_[i]->getId()) == color_map.end()) + color_map[detections_[i]->getId()] = CV_RGB(rand()%255, rand()%255, rand()%255); + }*/ + + cv::Mat image = input->getCVImage().clone(); + for(size_t i = 0; i < detections_.size(); ++i) + { + shared_ptr<ODDetection2D> detection = dynamic_pointer_cast<ODDetection2D>(detections_[i]); + if(detection) + cv::rectangle(image, detection->bounding_box_2d_, getHashedColor(detections_[i]->getId(), 100), 2); + } + return ODSceneImage(image); + } + shared_ptr<ODDetection2D> ODDetections2D::operator[](unsigned int i) { diff --git a/detectors/include/od/detectors/global2D/detection/ODFaceDetector.h b/detectors/include/od/detectors/global2D/detection/ODFaceDetector.h index 70fd0740..af8e4d08 100644 --- a/detectors/include/od/detectors/global2D/detection/ODFaceDetector.h +++ b/detectors/include/od/detectors/global2D/detection/ODFaceDetector.h @@ -18,18 +18,18 @@ namespace od class ODFaceDetector2D: public ODDetector2D { + public: + #ifndef DOXYGEN_SHOULD_SKIP_THIS OD_DEFINE_ENUM_WITH_STRING_CONVERSIONS(FaceRecogType, (GPU)(CPU)) #endif - public: - ODFaceDetector2D(ODFaceDetector2D::FaceRecogType type, const std::string & path = std::string()); shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene); shared_ptr<ODDetections2D> detectOmni(shared_ptr<ODSceneImage> scene); - + shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene); void setScale(const float scale); float getScale() const; @@ -43,16 +43,24 @@ namespace od void setMaxSize(const cv::Size & size); cv::Size getMaxSize() const; + void setFindLargest(bool largest); + bool getFindLargest() const; + + void init(); + private: + FaceRecogType type_; std::vector<cv::Rect> faces; - cv::Ptr<cv::cuda::CascadeClassifier> face_cascade_gpu_; float scale_; unsigned int mn_; + bool largest_; cv::Size min_size_, max_size_; + cv::Mat frame_gray_; + shared_ptr<cv::CascadeClassifier> face_cascade_; #ifdef WITH_GPU - shared_ptr<cv::CascadeClassifier> face_cascade_; + cv::Ptr<cv::cuda::CascadeClassifier> face_cascade_gpu_; cv::cuda::GpuMat gray_gpu_, faces_buf_gpu_, color_gpu_; #endif diff --git a/detectors/src/global2D/detection/ODFaceDetector.cpp b/detectors/src/global2D/detection/ODFaceDetector.cpp index ddf35bbd..4292f42c 100644 --- a/detectors/src/global2D/detection/ODFaceDetector.cpp +++ b/detectors/src/global2D/detection/ODFaceDetector.cpp @@ -6,15 +6,13 @@ namespace od { ODFaceDetector2D::ODFaceDetector2D(ODFaceDetector2D::FaceRecogType type, const std::string & path) : - type_(type), scale_(1.1), mn_(2) + type_(type), scale_(1.1), mn_(2), largest_(true) { std::string face_cascade = path.empty() ? "haarcascade_frontalface_alt.xml" : path; if(type_ == GPU){ face_cascade_gpu_ = cv::cuda::CascadeClassifier::create(face_cascade); - face_cascade_gpu_->setFindLargestObject(false); - - + face_cascade_gpu_->setFindLargestObject(largest_); }else{ face_cascade_ = make_shared<cv::CascadeClassifier>(face_cascade); } @@ -31,23 +29,19 @@ namespace od color_gpu_.upload(input); cv::cuda::cvtColor(color_gpu_, gray_gpu_, CV_BGR2GRAY); + face_cascade_gpu_->setFindLargestObject(largest_); + face_cascade_gpu_->setScaleFactor(scale_); face_cascade_gpu_->detectMultiScale(gray_gpu_, faces_buf_gpu_); face_cascade_gpu_->convert(faces_buf_gpu_, faces); }else{ - - cv::Mat frame_gray; - - cv::cvtColor(input, frame_gray, CV_BGR2GRAY); - cv::equalizeHist(frame_gray, frame_gray); - - face_cascade_->detectMultiScale(frame_gray, faces, scale_, mn_, 0|CV_HAAR_SCALE_IMAGE, min_size_, max_size_); - + cv::cvtColor(input, frame_gray_, CV_BGR2GRAY); + //cv::equalizeHist(frame_gray_, frame_gray_); + face_cascade_->detectMultiScale(frame_gray_, faces, scale_, mn_, 0|CV_HAAR_SCALE_IMAGE, min_size_, max_size_); } shared_ptr<ODDetections2D> detections = make_shared<ODDetections2D>(); - for(auto & face : faces){ shared_ptr<ODDetection2D> detection = make_shared<ODDetection2D>(ODDetection::OD_DETECTION, std::string("face")); detection->bounding_box_2d_ = face; @@ -57,6 +51,18 @@ namespace od return detections; } + shared_ptr<ODDetections> ODFaceDetector2D::detect(shared_ptr<ODSceneImage> scene) + { + std::cout << "Not implemented. Use detectOmni(shared_ptr<ODSceneImage>) instead." << std::endl; + return nullptr; + } + + shared_ptr<ODDetections> ODFaceDetector2D::detectOmni(shared_ptr<ODScene> scene) + { + std::cout << "Not implemented. Use detectOmni(shared_ptr<ODSceneImage>) instead." << std::endl; + return nullptr; + } + void ODFaceDetector2D::setScale(const float scale) { scale_ = scale; @@ -97,11 +103,19 @@ namespace od return max_size_; } + void ODFaceDetector2D::init(){ + std::cout << "Unused method" << std::endl; + } + + void ODFaceDetector2D::setFindLargest(bool largest) + { + largest_ = largest; + } - shared_ptr<ODDetections> ODFaceDetector2D::detect(shared_ptr<ODSceneImage> scene) + bool ODFaceDetector2D::getFindLargest() const { - std::cout << "Not implemented. Use detectOmi(shared_ptr<ODSceneImage>) instead." << std::endl; - return nullptr; + return largest_; } + } } \ No newline at end of file diff --git a/examples/data/haarcascade_frontalface_alt.xml b/examples/data/haarcascade_frontalface_alt.xml new file mode 100644 index 00000000..5a6f2754 --- /dev/null +++ b/examples/data/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/examples/data/haarcascade_frontalface_alt_gpu.xml b/examples/data/haarcascade_frontalface_alt_gpu.xml new file mode 100644 index 00000000..caa86f6c --- /dev/null +++ b/examples/data/haarcascade_frontalface_alt_gpu.xml @@ -0,0 +1,23550 @@ +<?xml version="1.0"?> +<!-- + Tree-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_alt2 type_id="opencv-haar-classifier"> + <size>20 20</size> + <stages> + <_> + <!-- stage 0 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 7 16 4 -1.</_> + <_>2 9 16 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.3272329494357109e-003</threshold> + <left_val>0.0383819006383419</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 4 3 14 -1.</_> + <_>8 11 3 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0130761601030827</threshold> + <left_val>0.8965256810188294</left_val> + <right_val>0.2629314064979553</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 6 1 6 -1.</_> + <_>13 9 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.2434601821005344e-004</threshold> + <left_val>0.1021663025021553</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 2 12 8 -1.</_> + <_>8 2 4 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.4573000632226467e-003</threshold> + <left_val>0.1238401979207993</left_val> + <right_val>0.6910383105278015</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 3 1 9 -1.</_> + <_>6 6 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.2708261217921972e-004</threshold> + <left_node>1</left_node> + <right_val>0.1953697055578232</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 7 14 9 -1.</_> + <_>3 10 14 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.3989109215326607e-004</threshold> + <left_val>0.2101441025733948</left_val> + <right_val>0.8258674740791321</right_val></_></_></trees> + <stage_threshold>0.3506923019886017</stage_threshold> + <parent>-1</parent> + <next>-1</next></_> + <_> + <!-- stage 1 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 7 4 4 -1.</_> + <_>4 9 4 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.3025739938020706e-003</threshold> + <left_val>0.1018375977873802</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 4 2 16 -1.</_> + <_>9 12 2 8 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.4174338690936565e-003</threshold> + <left_val>0.8219057917594910</left_val> + <right_val>0.1956554949283600</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 1 18 5 -1.</_> + <_>7 1 6 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0222032107412815</threshold> + <left_val>0.2205407023429871</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 5 13 8 -1.</_> + <_>4 9 13 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.7283110355492681e-004</threshold> + <left_val>0.0732632577419281</left_val> + <right_val>0.5931484103202820</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 7 16 9 -1.</_> + <_>1 10 16 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.3567270040512085e-003</threshold> + <left_val>0.1844114959239960</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 0 15 4 -1.</_> + <_>2 2 15 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.6032889727503061e-003</threshold> + <left_val>0.4032213985919952</left_val> + <right_val>0.8066521286964417</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 5 6 4 -1.</_> + <_>9 5 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7309630056843162e-003</threshold> + <left_val>0.2548328042030335</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 3 8 9 -1.</_> + <_>6 6 8 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.8146401792764664e-003</threshold> + <left_val>0.6057069897651672</left_val> + <right_val>0.2779063880443573</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 12 3 8 -1.</_> + <_>8 16 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.7343417108058929e-003</threshold> + <left_val>0.2889980077743530</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 16 2 2 -1.</_> + <_>3 17 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.4522320432588458e-004</threshold> + <left_val>0.7616587281227112</left_val> + <right_val>0.3495643138885498</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 1 6 12 -1.</_> + <_>14 1 3 12 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0494148582220078</threshold> + <left_node>1</left_node> + <right_val>0.8151652812957764</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 4 12 6 -1.</_> + <_>8 4 4 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.4891750440001488e-003</threshold> + <left_val>0.2808783054351807</left_val> + <right_val>0.6027774810791016</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 6 15 -1.</_> + <_>3 2 3 15 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0603136196732521</threshold> + <left_node>1</left_node> + <right_val>0.7607501745223999</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 4 9 6 -1.</_> + <_>5 6 9 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0762850288301706e-003</threshold> + <left_val>0.4444035887718201</left_val> + <right_val>0.1437312066555023</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 11 6 3 -1.</_> + <_>13 12 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.5083238556981087e-003</threshold> + <left_node>1</left_node> + <right_val>0.5318170189857483</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 12 6 4 -1.</_> + <_>12 14 6 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.6601309701800346e-003</threshold> + <left_val>0.5411052107810974</left_val> + <right_val>0.2180687040090561</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 11 6 3 -1.</_> + <_>1 12 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.6467678882181644e-003</threshold> + <left_node>1</left_node> + <right_val>0.1158960014581680</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 5 5 8 -1.</_> + <_>2 9 5 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.4662932204082608e-004</threshold> + <left_val>0.2340679019689560</left_val> + <right_val>0.5990381836891174</right_val></_></_></trees> + <stage_threshold>3.4721779823303223</stage_threshold> + <parent>0</parent> + <next>-1</next></_> + <_> + <!-- stage 2 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 4 10 4 -1.</_> + <_>5 6 10 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.8506218008697033e-003</threshold> + <left_node>1</left_node> + <right_val>0.1805496066808701</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 4 16 12 -1.</_> + <_>2 8 16 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.6141650527715683e-003</threshold> + <left_val>0.2177893966436386</left_val> + <right_val>0.8018236756324768</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 5 12 6 -1.</_> + <_>8 5 4 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.4301309604197741e-003</threshold> + <left_val>0.1141354963183403</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>13 7 2 9 -1.</_> + <_>13 10 2 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.1787960799410939e-004</threshold> + <left_val>0.1203093975782394</left_val> + <right_val>0.6108530759811401</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 2 9 -1.</_> + <_>5 10 2 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0010929545387626e-003</threshold> + <left_val>0.2079959958791733</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 1 6 8 -1.</_> + <_>9 1 2 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0577100329101086e-003</threshold> + <left_val>0.3302054107189179</left_val> + <right_val>0.7511094212532044</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 0 4 12 -1.</_> + <_>14 0 2 6 2.</_> + <_>12 6 2 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2376549420878291e-003</threshold> + <left_node>1</left_node> + <right_val>0.2768222093582153</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 8 10 2 -1.</_> + <_>5 9 10 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.5315038985572755e-004</threshold> + <left_val>0.1668293029069901</left_val> + <right_val>0.5829476714134216</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- 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.1508788019418716</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 3 9 12 -1.</_> + <_>3 3 3 12 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4182999730110168e-003</threshold> + <left_val>0.4391227960586548</left_val> + <right_val>0.7646595239639282</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 8 3 12 -1.</_> + <_>9 12 3 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.4642980899661779e-003</threshold> + <left_node>1</left_node> + <right_val>0.2651556134223938</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 5 20 15 -1.</_> + <_>0 10 20 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0149489501491189</threshold> + <left_val>0.2298053056001663</left_val> + <right_val>0.5442165732383728</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>-1.0506849503144622e-003</threshold> + <left_node>1</left_node> + <right_val>0.3622843921184540</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 1 6 2 -1.</_> + <_>2 2 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.0782918222248554e-003</threshold> + <left_val>0.2601259946823120</left_val> + <right_val>0.7233657836914063</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 15 6 4 -1.</_> + <_>13 15 3 2 2.</_> + <_>10 17 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.4242828628048301e-004</threshold> + <left_val>0.3849678933620453</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 14 2 6 -1.</_> + <_>12 16 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.3204059153795242e-003</threshold> + <left_val>0.2965512871742249</left_val> + <right_val>0.5480309128761292</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- 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>1.1421289527788758e-003</threshold> + <left_val>0.4104770123958588</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 18 1 2 -1.</_> + <_>7 19 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1783400550484657e-003</threshold> + <left_val>0.7239024043083191</left_val> + <right_val>0.2787283957004547</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 5 12 10 -1.</_> + <_>10 5 6 5 2.</_> + <_>4 10 6 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0440771095454693</threshold> + <left_val>0.5640516281127930</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 4 8 12 -1.</_> + <_>11 4 4 6 2.</_> + <_>7 10 4 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.7900090683251619e-003</threshold> + <left_val>0.5947548151016235</left_val> + <right_val>0.3312020003795624</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.4291418958455324e-003</threshold> + <left_val>0.6603232026100159</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 3 12 12 -1.</_> + <_>3 3 6 6 2.</_> + <_>9 9 6 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.4262324273586273e-003</threshold> + <left_val>0.4680665135383606</left_val> + <right_val>0.2064338028430939</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 11 5 3 -1.</_> + <_>15 12 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.0630257725715637e-003</threshold> + <left_val>0.5298851132392883</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 18 3 2 -1.</_> + <_>11 18 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.2240812219679356e-003</threshold> + <left_val>0.5281602740287781</left_val> + <right_val>0.1909549981355667</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 11 5 3 -1.</_> + <_>0 12 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.0630568079650402e-003</threshold> + <left_val>0.1380645930767059</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 18 3 2 -1.</_> + <_>8 18 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.6897541508078575e-003</threshold> + <left_val>0.5490636825561523</left_val> + <right_val>0.1260281056165695</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 8 16 2 -1.</_> + <_>2 9 16 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2472929665818810e-003</threshold> + <left_val>0.2372663021087647</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 6 5 12 -1.</_> + <_>9 12 5 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0495434887707233</threshold> + <left_val>0.5240166187286377</left_val> + <right_val>0.1769216060638428</right_val></_></_></trees> + <stage_threshold>5.9844889640808105</stage_threshold> + <parent>1</parent> + <next>-1</next></_> + <_> + <!-- stage 3 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 3 8 6 -1.</_> + <_>6 6 8 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.9326149746775627e-003</threshold> + <left_node>1</left_node> + <right_val>0.1998064965009689</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 7 12 2 -1.</_> + <_>8 7 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.7918140403926373e-005</threshold> + <left_val>0.2299380004405975</left_val> + <right_val>0.7393211126327515</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 9 6 8 -1.</_> + <_>10 13 6 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.0876200180500746e-003</threshold> + <left_node>1</left_node> + <right_val>0.1533840000629425</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 5 3 10 -1.</_> + <_>12 10 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.4669660534709692e-006</threshold> + <left_val>0.2036858946084976</left_val> + <right_val>0.5854915976524353</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 6 3 9 -1.</_> + <_>4 9 3 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.8739729421213269e-003</threshold> + <left_val>0.2049895972013474</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 4 6 4 -1.</_> + <_>9 4 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.3380251200869679e-004</threshold> + <left_val>0.3234199881553650</left_val> + <right_val>0.7323014140129089</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 3 8 3 -1.</_> + <_>12 3 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.9151850137859583e-003</threshold> + <left_val>0.3045149147510529</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>15 0 3 6 -1.</_> + <_>15 3 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.9683797881007195e-003</threshold> + <left_val>0.2932133972644806</left_val> + <right_val>0.5621296167373657</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 12 10 8 -1.</_> + <_>2 12 5 4 2.</_> + <_>7 16 5 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.2115601506084204e-004</threshold> + <left_val>0.3658036887645721</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 5 6 8 -1.</_> + <_>5 9 6 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.9663117863237858e-003</threshold> + <left_val>0.2712155878543854</left_val> + <right_val>0.7226334810256958</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 3 8 3 -1.</_> + <_>12 3 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0308741796761751</threshold> + <left_val>0.4419837892055512</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>15 0 3 6 -1.</_> + <_>15 3 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0110997101292014</threshold> + <left_val>0.3612976968288422</left_val> + <right_val>0.5251451134681702</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 3 8 3 -1.</_> + <_>4 3 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1164179779589176e-003</threshold> + <left_val>0.3628616929054260</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 1 4 4 -1.</_> + <_>2 3 4 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.4317439943552017e-003</threshold> + <left_val>0.1601095050573349</left_val> + <right_val>0.7052276730537415</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 2 3 2 -1.</_> + <_>11 2 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.5266019403934479e-003</threshold> + <left_val>0.1301288008689880</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 3 3 1 -1.</_> + <_>11 3 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.6907559474930167e-003</threshold> + <left_val>0.1786323934793472</left_val> + <right_val>0.5521529912948608</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 15 3 4 -1.</_> + <_>7 17 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.6470930101349950e-004</threshold> + <left_val>0.3487383127212524</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 13 3 6 -1.</_> + <_>4 15 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0102155702188611</threshold> + <left_val>0.2673991024494171</left_val> + <right_val>0.6667919158935547</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 5 1 14 -1.</_> + <_>10 12 1 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2634709710255265e-003</threshold> + <left_node>1</left_node> + <right_val>0.3437863886356354</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 4 10 6 -1.</_> + <_>5 6 10 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0118752997368574</threshold> + <left_val>0.5995336174964905</left_val> + <right_val>0.3497717976570129</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 0 6 3 -1.</_> + <_>7 0 2 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0107323396950960</threshold> + <left_val>0.2150489985942841</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 0 3 5 -1.</_> + <_>7 0 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.1836481802165508e-003</threshold> + <left_val>0.6271436214447022</left_val> + <right_val>0.2519541978836060</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 15 6 5 -1.</_> + <_>9 15 2 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0283408891409636</threshold> + <left_val>0.0824118927121162</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 10 2 6 -1.</_> + <_>9 12 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.5813230099156499e-004</threshold> + <left_val>0.5910056829452515</left_val> + <right_val>0.3705201148986816</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 17 3 2 -1.</_> + <_>9 17 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.2940340936183929e-003</threshold> + <left_node>1</left_node> + <right_val>0.1594727933406830</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 12 7 6 -1.</_> + <_>1 14 7 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0107510797679424</threshold> + <left_val>0.5980480909347534</left_val> + <right_val>0.2832508087158203</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 6 3 7 -1.</_> + <_>10 6 1 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0224651191383600</threshold> + <left_node>1</left_node> + <right_val>0.7877091169357300</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>16 3 4 9 -1.</_> + <_>16 6 4 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0579885393381119</threshold> + <left_val>0.1555740982294083</left_val> + <right_val>0.5239657163619995</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 6 3 7 -1.</_> + <_>9 6 1 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.2110891342163086e-003</threshold> + <left_node>1</left_node> + <right_val>0.6620365977287293</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 5 18 8 -1.</_> + <_>0 5 9 4 2.</_> + <_>9 9 9 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0483675710856915</threshold> + <left_val>0.1424719989299774</left_val> + <right_val>0.4429833889007568</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 5 2 10 -1.</_> + <_>13 10 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0144180599600077</threshold> + <left_val>0.1588540971279144</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 10 2 6 -1.</_> + <_>12 13 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0231563895940781</threshold> + <left_val>0.2375798970460892</left_val> + <right_val>0.5217134952545166</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 0 3 5 -1.</_> + <_>8 0 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.6985340565443039e-003</threshold> + <left_node>1</left_node> + <right_val>0.1941725015640259</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 5 8 6 -1.</_> + <_>6 7 8 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.6248619221150875e-003</threshold> + <left_val>0.6278405785560608</left_val> + <right_val>0.3746044933795929</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 3 6 14 -1.</_> + <_>13 3 3 7 2.</_> + <_>10 10 3 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.2936748620122671e-004</threshold> + <left_node>1</left_node> + <right_val>0.3840922117233276</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>13 5 1 8 -1.</_> + <_>13 9 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.1783898854628205e-004</threshold> + <left_val>0.3106493055820465</left_val> + <right_val>0.5537847280502319</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 3 6 14 -1.</_> + <_>4 3 3 7 2.</_> + <_>7 10 3 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.5803939428878948e-005</threshold> + <left_node>1</left_node> + <right_val>0.3444449007511139</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 5 1 8 -1.</_> + <_>6 9 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4719359569426160e-005</threshold> + <left_val>0.2729552090167999</left_val> + <right_val>0.6428951025009155</right_val></_></_></trees> + <stage_threshold>8.5117864608764648</stage_threshold> + <parent>2</parent> + <next>-1</next></_> + <_> + <!-- stage 4 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 1 1 6 -1.</_> + <_>8 3 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.3469370314851403e-003</threshold> + <left_val>0.1657086014747620</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 0 15 2 -1.</_> + <_>2 1 15 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.4774789344519377e-003</threshold> + <left_val>0.2273851037025452</left_val> + <right_val>0.6989349722862244</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 7 20 6 -1.</_> + <_>0 9 20 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.2632777951657772e-003</threshold> + <left_val>0.1512074023485184</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 10 6 8 -1.</_> + <_>10 14 6 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.9075339920818806e-003</threshold> + <left_val>0.5564470291137695</left_val> + <right_val>0.1605442017316818</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 1 3 2 -1.</_> + <_>8 1 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.3254349362105131e-003</threshold> + <left_val>0.1880259066820145</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 1 2 2 -1.</_> + <_>9 1 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4665479538962245e-003</threshold> + <left_val>0.3122498989105225</left_val> + <right_val>0.7165396213531494</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 3 12 9 -1.</_> + <_>4 6 12 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.1231169030070305</threshold> + <left_node>1</left_node> + <right_val>0.3859583139419556</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 5 9 5 -1.</_> + <_>9 5 3 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.2108340635895729e-003</threshold> + <left_val>0.2455293983221054</left_val> + <right_val>0.5695710182189941</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 9 5 -1.</_> + <_>8 5 3 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.0661531016230583e-003</threshold> + <left_val>0.2716520130634308</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 6 6 12 -1.</_> + <_>4 10 6 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.6130280932411551e-004</threshold> + <left_val>0.2293362021446228</left_val> + <right_val>0.7208629846572876</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 0 6 18 -1.</_> + <_>13 0 3 18 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0799578726291656</threshold> + <left_node>1</left_node> + <right_val>0.7833620905876160</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 8 1 12 -1.</_> + <_>10 12 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.6064720004796982e-003</threshold> + <left_val>0.5545232295989990</left_val> + <right_val>0.2550689876079559</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>6.5699010156095028e-003</threshold> + <left_node>1</left_node> + <right_val>0.1819390058517456</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 2 4 6 -1.</_> + <_>3 2 2 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6259610420092940e-003</threshold> + <left_val>0.3529875874519348</left_val> + <right_val>0.6552819013595581</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 18 3 2 -1.</_> + <_>10 18 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.6204981151968241e-003</threshold> + <left_val>0.5462309718132019</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 18 3 2 -1.</_> + <_>11 18 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.4391951523721218e-003</threshold> + <left_val>0.1359843015670776</left_val> + <right_val>0.5415815114974976</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 8 2 6 -1.</_> + <_>2 10 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.0540945529937744e-003</threshold> + <left_val>0.1115119978785515</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 5 6 6 -1.</_> + <_>7 7 6 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.6067481162026525e-004</threshold> + <left_val>0.5846719741821289</left_val> + <right_val>0.2598348855972290</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 19 6 1 -1.</_> + <_>9 19 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.6621041148900986e-003</threshold> + <left_val>0.1610569059848785</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 18 3 2 -1.</_> + <_>11 18 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.1165837794542313e-003</threshold> + <left_val>0.5376678705215454</left_val> + <right_val>0.1739455014467239</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 3 3 1 -1.</_> + <_>9 3 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.1362339612096548e-003</threshold> + <left_val>0.1902073025703430</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 2 16 2 -1.</_> + <_>2 2 8 1 2.</_> + <_>10 3 8 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.4809921421110630e-003</threshold> + <left_val>0.3272008001804352</left_val> + <right_val>0.6364840865135193</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 11 5 3 -1.</_> + <_>8 12 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.1061907112598419e-003</threshold> + <left_val>0.6914852857589722</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 13 6 3 -1.</_> + <_>7 14 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.0048708692193031e-003</threshold> + <left_val>0.4327326118946075</left_val> + <right_val>0.6963843107223511</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 1 6 15 -1.</_> + <_>2 1 2 15 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0870285481214523</threshold> + <left_val>0.8594133853912354</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 12 2 3 -1.</_> + <_>2 13 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.7809639945626259e-003</threshold> + <left_val>0.0973944664001465</left_val> + <right_val>0.4587030112743378</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 13 1 3 -1.</_> + <_>16 14 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.2166660055518150e-003</threshold> + <left_val>0.2554625868797302</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>13 7 6 4 -1.</_> + <_>16 7 3 2 2.</_> + <_>13 9 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3642730191349983e-003</threshold> + <left_val>0.3319090902805328</left_val> + <right_val>0.5964102745056152</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 13 3 6 -1.</_> + <_>7 16 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.0077864006161690e-003</threshold> + <left_val>0.2666594982147217</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 5 1 14 -1.</_> + <_>7 12 1 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0154941203072667</threshold> + <left_val>0.1848185956478119</left_val> + <right_val>0.6245970726013184</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 12 2 3 -1.</_> + <_>15 13 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.2165028862655163e-003</threshold> + <left_node>1</left_node> + <right_val>0.5379927158355713</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 5 3 14 -1.</_> + <_>10 12 3 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0432497598230839</threshold> + <left_val>0.5183029174804688</left_val> + <right_val>0.2170419991016388</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 10 2 6 -1.</_> + <_>6 13 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.8786511393263936e-004</threshold> + <left_node>1</left_node> + <right_val>0.2613384127616882</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 5 1 8 -1.</_> + <_>6 9 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2373150093480945e-003</threshold> + <left_val>0.2786532044410706</left_val> + <right_val>0.5908988118171692</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 11 2 1 -1.</_> + <_>13 11 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.9528300035744905e-003</threshold> + <left_node>1</left_node> + <right_val>0.2612869143486023</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 1 6 10 -1.</_> + <_>15 1 3 5 2.</_> + <_>12 6 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4947060262784362e-003</threshold> + <left_val>0.5915412902832031</left_val> + <right_val>0.3455781936645508</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 12 2 3 -1.</_> + <_>3 13 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.5878680646419525e-003</threshold> + <left_node>1</left_node> + <right_val>0.1587052047252655</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 18 2 1 -1.</_> + <_>10 18 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.5938691105693579e-003</threshold> + <left_val>0.1270411014556885</left_val> + <right_val>0.5979428887367249</right_val></_></_></trees> + <stage_threshold>8.4680156707763672</stage_threshold> + <parent>3</parent> + <next>-1</next></_> + <_> + <!-- stage 5 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 0 17 9 -1.</_> + <_>1 3 17 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.5810680128633976e-003</threshold> + <left_val>0.1995104998350143</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 2 8 8 -1.</_> + <_>1 2 4 4 2.</_> + <_>5 6 4 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.8552350122481585e-003</threshold> + <left_val>0.7373070120811462</left_val> + <right_val>0.2921737134456635</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 6 4 -1.</_> + <_>9 5 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.9758539274334908e-003</threshold> + <left_val>0.1956419944763184</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 9 7 10 -1.</_> + <_>10 14 7 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.2583118882030249e-003</threshold> + <left_val>0.5692046880722046</left_val> + <right_val>0.1839064955711365</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 6 4 -1.</_> + <_>8 5 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.3711679386906326e-004</threshold> + <left_val>0.2171667069196701</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 7 20 6 -1.</_> + <_>0 9 20 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.5942500215023756e-003</threshold> + <left_val>0.2719989120960236</left_val> + <right_val>0.7150244116783142</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 5 9 10 -1.</_> + <_>6 10 9 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0250324495136738</threshold> + <left_val>0.1825183928012848</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 4 4 12 -1.</_> + <_>8 10 4 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.3087949529290199e-003</threshold> + <left_val>0.5699837803840637</left_val> + <right_val>0.3509852886199951</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 6 8 3 -1.</_> + <_>6 7 8 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.2494920305907726e-003</threshold> + <left_node>1</left_node> + <right_val>0.4023926854133606</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 13 10 6 -1.</_> + <_>3 13 5 3 2.</_> + <_>8 16 5 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0148857301101089</threshold> + <left_val>0.3604095876216888</left_val> + <right_val>0.7291995286941528</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 1 4 11 -1.</_> + <_>15 1 2 11 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.0623216927051544e-003</threshold> + <left_node>1</left_node> + <right_val>0.6491490006446838</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 7 10 10 -1.</_> + <_>10 7 5 5 2.</_> + <_>5 12 5 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0274056792259216</threshold> + <left_val>0.5518993139266968</left_val> + <right_val>0.2659681141376495</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 1 4 11 -1.</_> + <_>3 1 2 11 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0343686006963253</threshold> + <left_node>1</left_node> + <right_val>0.6712512969970703</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 5 8 12 -1.</_> + <_>1 11 8 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0272929705679417</threshold> + <left_val>0.1691378057003021</left_val> + <right_val>0.4326277971267700</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- 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.4452121043577790e-004</threshold> + <left_val>0.3405100107192993</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 10 7 4 -1.</_> + <_>11 12 7 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.0336280623450875e-004</threshold> + <left_val>0.5516793131828308</left_val> + <right_val>0.3311387896537781</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 4 20 12 -1.</_> + <_>0 4 10 6 2.</_> + <_>10 10 10 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.1227546036243439</threshold> + <left_val>0.1675315052270889</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 5 6 15 -1.</_> + <_>1 10 6 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.2559928949922323e-003</threshold> + <left_val>0.3615751862525940</left_val> + <right_val>0.6420782804489136</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 10 3 8 -1.</_> + <_>11 14 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0320903994143009</threshold> + <left_val>0.2921079099178314</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 12 7 6 -1.</_> + <_>11 14 7 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.2957999501377344e-003</threshold> + <left_val>0.5613031983375549</left_val> + <right_val>0.3357860147953033</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 11 2 3 -1.</_> + <_>9 12 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.2273170072585344e-003</threshold> + <left_val>0.6970642805099487</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 13 4 3 -1.</_> + <_>8 14 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1171669466421008e-003</threshold> + <left_val>0.3541150093078613</left_val> + <right_val>0.6144006252288818</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- 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.0172799509018660</threshold> + <left_node>1</left_node> + <right_val>0.5537180900573731</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>18 7 2 4 -1.</_> + <_>18 9 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0117412004619837</threshold> + <left_val>0.5341957211494446</left_val> + <right_val>0.2757104933261871</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 12 6 6 -1.</_> + <_>3 14 6 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.6405228786170483e-003</threshold> + <left_node>1</left_node> + <right_val>0.2489521056413651</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 4 3 6 -1.</_> + <_>0 6 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0169130302965641</threshold> + <left_val>0.1711928993463516</left_val> + <right_val>0.5523952841758728</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 14 3 3 -1.</_> + <_>9 15 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0100601697340608</threshold> + <left_node>1</left_node> + <right_val>0.8273450732231140</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 7 10 4 -1.</_> + <_>15 7 5 2 2.</_> + <_>10 9 5 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.0715491417795420e-004</threshold> + <left_val>0.3779391050338745</left_val> + <right_val>0.5476251840591431</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 2 6 8 -1.</_> + <_>7 6 6 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0865400545299053e-003</threshold> + <left_node>1</left_node> + <right_val>0.3296540975570679</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 3 6 2 -1.</_> + <_>8 3 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.9362077414989471e-003</threshold> + <left_val>0.6062883734703064</left_val> + <right_val>0.2434220016002655</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 6 3 5 -1.</_> + <_>11 6 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.6372660067863762e-004</threshold> + <left_node>1</left_node> + <right_val>0.3814094960689545</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 0 6 19 -1.</_> + <_>11 0 2 19 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0131100500002503</threshold> + <left_val>0.5517616271972656</left_val> + <right_val>0.3726893067359924</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 12 1 2 -1.</_> + <_>3 13 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.9806280508637428e-003</threshold> + <left_val>0.1229664012789726</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 14 5 3 -1.</_> + <_>7 15 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.1619571857154369e-003</threshold> + <left_val>0.7252274751663208</left_val> + <right_val>0.4973455071449280</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- 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.0338423289358616</threshold> + <left_val>0.5348312854766846</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 5 3 8 -1.</_> + <_>11 5 1 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.2564560165628791e-003</threshold> + <left_val>0.5851914882659912</left_val> + <right_val>0.4384166896343231</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 1 18 4 -1.</_> + <_>0 1 9 2 2.</_> + <_>9 3 9 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0196352303028107</threshold> + <left_val>0.2297834008932114</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 5 3 8 -1.</_> + <_>8 5 1 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.9625496659427881e-004</threshold> + <left_val>0.6295937895774841</left_val> + <right_val>0.4131599068641663</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 2 6 -1.</_> + <_>9 7 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0231271106749773</threshold> + <left_val>0.1695459038019180</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 8 5 2 -1.</_> + <_>10 9 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0235257092863321</threshold> + <left_val>0.5174130201339722</left_val> + <right_val>0.0595193915069103</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 10 15 1 -1.</_> + <_>7 10 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0193565208464861</threshold> + <left_val>0.1357247978448868</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 7 2 6 -1.</_> + <_>2 9 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.1787112131714821e-003</threshold> + <left_val>0.2996628880500794</left_val> + <right_val>0.5791695117950440</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 14 3 3 -1.</_> + <_>9 15 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.1488779932260513e-003</threshold> + <left_node>1</left_node> + <right_val>0.6592589020729065</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 7 4 10 -1.</_> + <_>9 12 4 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.3972279205918312e-003</threshold> + <left_val>0.5307171940803528</left_val> + <right_val>0.3795121014118195</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 8 8 2 -1.</_> + <_>0 8 4 1 2.</_> + <_>4 9 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.1955118983169086e-006</threshold> + <left_val>0.3128314912319183</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 9 10 8 -1.</_> + <_>5 9 5 4 2.</_> + <_>10 13 5 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0471144095063210</threshold> + <left_val>0.5537893176078796</left_val> + <right_val>0.1027309000492096</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 2 4 -1.</_> + <_>9 7 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.2878710925579071e-003</threshold> + <left_val>0.4660859107971191</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 6 3 4 -1.</_> + <_>10 6 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.1887511983513832e-003</threshold> + <left_val>0.7158858180046082</left_val> + <right_val>0.4724448919296265</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 3 2 1 -1.</_> + <_>9 3 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.9757320880889893e-003</threshold> + <left_node>1</left_node> + <right_val>0.0593456886708736</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 6 3 4 -1.</_> + <_>9 6 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.8449809867888689e-003</threshold> + <left_val>0.7027301788330078</left_val> + <right_val>0.4718731045722961</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 0 4 14 -1.</_> + <_>14 0 2 7 2.</_> + <_>12 7 2 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0239540279144421e-004</threshold> + <left_val>0.5894734263420105</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 5 6 9 -1.</_> + <_>12 5 3 9 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.4277009069919586e-003</threshold> + <left_val>0.4862355887889862</left_val> + <right_val>0.5247588157653809</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 6 16 -1.</_> + <_>3 2 3 16 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0647513121366501</threshold> + <left_val>0.6917471289634705</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 12 4 2 -1.</_> + <_>1 13 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.9380151429213583e-004</threshold> + <left_val>0.4669617116451263</left_val> + <right_val>0.2382405996322632</right_val></_></_></trees> + <stage_threshold>12.5784997940063480</stage_threshold> + <parent>4</parent> + <next>-1</next></_> + <_> + <!-- stage 6 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 7 6 1 -1.</_> + <_>9 7 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4397440245375037e-003</threshold> + <left_val>0.2773470878601074</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 3 4 9 -1.</_> + <_>8 6 4 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.4068560712039471e-004</threshold> + <left_val>0.7427154779434204</left_val> + <right_val>0.2479735016822815</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 10 4 6 -1.</_> + <_>12 13 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.1237959673453588e-006</threshold> + <left_node>1</left_node> + <right_val>0.2199503034353256</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 1 8 16 -1.</_> + <_>12 1 4 8 2.</_> + <_>8 9 4 8 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.3661039303988218e-003</threshold> + <left_val>0.5889989733695984</left_val> + <right_val>0.2595716118812561</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 6 3 6 -1.</_> + <_>4 9 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7343269428238273e-003</threshold> + <left_val>0.1860125958919525</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 3 6 2 -1.</_> + <_>4 3 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5874590026214719e-003</threshold> + <left_val>0.4151870906352997</left_val> + <right_val>0.7103474140167236</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 8 3 12 -1.</_> + <_>9 12 3 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.7285638973116875e-003</threshold> + <left_node>1</left_node> + <right_val>0.2527967095375061</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 9 7 10 -1.</_> + <_>10 14 7 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.1288381963968277</threshold> + <left_val>0.1393000930547714</left_val> + <right_val>0.5254514813423157</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 9 7 10 -1.</_> + <_>3 14 7 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.9412180930376053e-003</threshold> + <left_node>1</left_node> + <right_val>0.2487729042768478</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 5 1 14 -1.</_> + <_>7 12 1 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0126617299392819</threshold> + <left_val>0.2710700035095215</left_val> + <right_val>0.6618837714195252</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 14 1 6 -1.</_> + <_>13 16 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.0146789868013002e-005</threshold> + <left_val>0.3812825977802277</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>14 12 3 6 -1.</_> + <_>14 14 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0163301602005959</threshold> + <left_val>0.2326432019472122</left_val> + <right_val>0.5263010859489441</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 14 1 6 -1.</_> + <_>6 16 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4622770322603174e-005</threshold> + <left_val>0.4293332099914551</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 12 3 6 -1.</_> + <_>3 14 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0208586603403091</threshold> + <left_val>0.1600403934717178</left_val> + <right_val>0.6782314777374268</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 13 5 3 -1.</_> + <_>8 14 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.8194559272378683e-003</threshold> + <left_node>1</left_node> + <right_val>0.6679294109344482</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 14 2 3 -1.</_> + <_>9 15 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.7899368908256292e-003</threshold> + <left_val>0.4587705135345459</left_val> + <right_val>0.7176238894462585</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 1 10 8 -1.</_> + <_>5 1 5 4 2.</_> + <_>10 5 5 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0353446416556835</threshold> + <left_node>1</left_node> + <right_val>0.1864075064659119</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 4 5 4 -1.</_> + <_>6 6 5 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1571600334718823e-003</threshold> + <left_val>0.5538259744644165</left_val> + <right_val>0.3150450885295868</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 10 18 1 -1.</_> + <_>7 10 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.8742752298712730e-003</threshold> + <left_val>0.2828791141510010</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 10 4 3 -1.</_> + <_>11 10 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5201780115603469e-005</threshold> + <left_val>0.5870224237442017</left_val> + <right_val>0.3704823851585388</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 11 6 1 -1.</_> + <_>7 11 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.2681879636365920e-004</threshold> + <left_node>1</left_node> + <right_val>0.4218930900096893</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 13 2 3 -1.</_> + <_>3 14 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.7845689803361893e-003</threshold> + <left_val>0.6667001247406006</left_val> + <right_val>0.2461182028055191</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 12 3 4 -1.</_> + <_>12 14 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.5295992903411388e-005</threshold> + <left_node>1</left_node> + <right_val>0.3557587862014771</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 10 5 6 -1.</_> + <_>11 12 5 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0443948917090893</threshold> + <left_val>0.1665547043085098</left_val> + <right_val>0.5234848856925964</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 8 16 2 -1.</_> + <_>0 9 16 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0126030538231134e-003</threshold> + <left_val>0.2884612977504730</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 1 3 4 -1.</_> + <_>2 3 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.6327780261635780e-003</threshold> + <left_val>0.2969340085983276</left_val> + <right_val>0.6080111265182495</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 3 3 -1.</_> + <_>10 7 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.0330411866307259e-003</threshold> + <left_val>0.4536390006542206</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 6 12 6 -1.</_> + <_>9 6 4 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.1367668956518173</threshold> + <left_val>0.5177264213562012</left_val> + <right_val>0.1449182033538818</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 3 3 -1.</_> + <_>9 7 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.0060478970408440e-003</threshold> + <left_val>0.7616909742355347</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 6 12 6 -1.</_> + <_>7 6 4 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0124758398160338</threshold> + <left_val>0.2159706056118012</left_val> + <right_val>0.5460187792778015</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 5 6 5 -1.</_> + <_>12 5 2 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.4012258341535926e-004</threshold> + <left_node>1</left_node> + <right_val>0.3926295936107636</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 7 10 2 -1.</_> + <_>5 7 5 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0121919801458716</threshold> + <left_val>0.3478881120681763</left_val> + <right_val>0.5542662739753723</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 5 6 5 -1.</_> + <_>6 5 2 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.4959481349214911e-004</threshold> + <left_val>0.6064276099205017</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 3 2 10 -1.</_> + <_>9 8 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.1802430273965001e-004</threshold> + <left_val>0.5697407126426697</left_val> + <right_val>0.1779713928699493</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 1 16 2 -1.</_> + <_>11 1 8 1 2.</_> + <_>3 2 8 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.9115799851715565e-003</threshold> + <left_val>0.5379372239112854</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 9 3 2 -1.</_> + <_>9 10 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.7631698008626699e-004</threshold> + <left_val>0.3327839076519013</left_val> + <right_val>0.5461531281471252</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 1 16 2 -1.</_> + <_>1 1 8 1 2.</_> + <_>9 2 8 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.7870173156261444e-003</threshold> + <left_val>0.2116160988807678</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 14 1 3 -1.</_> + <_>8 15 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.6761029837653041e-003</threshold> + <left_val>0.6635823249816895</left_val> + <right_val>0.4365859031677246</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 5 12 10 -1.</_> + <_>10 5 6 5 2.</_> + <_>4 10 6 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0556949488818645</threshold> + <left_node>1</left_node> + <right_val>0.5387424826622009</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 13 6 6 -1.</_> + <_>10 13 3 3 2.</_> + <_>7 16 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0198443792760372</threshold> + <left_val>0.1602804958820343</left_val> + <right_val>0.5330458879470825</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 9 3 2 -1.</_> + <_>8 10 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.4751611100509763e-004</threshold> + <left_val>0.2917476892471314</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 2 6 4 -1.</_> + <_>9 2 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0230328906327486</threshold> + <left_val>0.5608124136924744</left_val> + <right_val>0.1997981071472168</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 6 9 3 -1.</_> + <_>6 7 9 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.0700280331075191e-003</threshold> + <left_node>1</left_node> + <right_val>0.3938314020633698</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 7 6 1 -1.</_> + <_>12 7 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1636839481070638e-003</threshold> + <left_val>0.5757436156272888</left_val> + <right_val>0.4239456951618195</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 18 6 -1.</_> + <_>6 0 6 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.2246433943510056</threshold> + <left_node>1</left_node> + <right_val>0.7676553130149841</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 10 2 6 -1.</_> + <_>6 13 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4412109740078449e-003</threshold> + <left_val>0.5353866219520569</left_val> + <right_val>0.2514776885509491</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 12 3 6 -1.</_> + <_>11 15 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0300112497061491</threshold> + <left_val>0.2364903986454010</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 4 12 12 -1.</_> + <_>10 4 6 6 2.</_> + <_>4 10 6 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0530789606273174</threshold> + <left_val>0.2385863959789276</left_val> + <right_val>0.5414664745330811</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 2 3 6 -1.</_> + <_>2 2 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.0800929050892591e-003</threshold> + <left_node>1</left_node> + <right_val>0.6511614918708801</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 5 3 7 -1.</_> + <_>2 5 1 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.0738182142376900e-003</threshold> + <left_val>0.6030414104461670</left_val> + <right_val>0.3587701022624970</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 13 12 4 -1.</_> + <_>10 13 6 2 2.</_> + <_>4 15 6 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0195293705910444</threshold> + <left_node>1</left_node> + <right_val>0.5423592925071716</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 3 17 12 -1.</_> + <_>3 9 17 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0533094704151154</threshold> + <left_val>0.2360953986644745</left_val> + <right_val>0.5401757955551148</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 3 14 12 -1.</_> + <_>3 3 7 6 2.</_> + <_>10 9 7 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0348495617508888</threshold> + <left_val>0.2836985886096954</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 11 16 9 -1.</_> + <_>2 14 16 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.1265845000743866</threshold> + <left_val>0.1813516020774841</left_val> + <right_val>0.5421046018600464</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 14 3 6 -1.</_> + <_>9 17 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.3325118137290701e-006</threshold> + <left_val>0.3980365991592407</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 14 4 6 -1.</_> + <_>10 14 2 3 2.</_> + <_>8 17 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0118438703939319</threshold> + <left_val>0.2616384923458099</left_val> + <right_val>0.5237730145454407</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 2 6 1 -1.</_> + <_>8 2 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.8470678739249706e-003</threshold> + <left_val>0.2438108026981354</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 5 2 5 -1.</_> + <_>10 5 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.1693977117538452e-003</threshold> + <left_val>0.5327146053314209</left_val> + <right_val>0.8190376758575440</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 8 3 5 -1.</_> + <_>10 8 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.4716790802776814e-003</threshold> + <left_node>1</left_node> + <right_val>0.4679693877696991</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 12 6 1 -1.</_> + <_>9 12 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5188479665084742e-005</threshold> + <left_val>0.5563911795616150</left_val> + <right_val>0.4367586076259613</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 8 3 5 -1.</_> + <_>9 8 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.0696711037307978e-003</threshold> + <left_node>1</left_node> + <right_val>0.6664348840713501</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 10 4 3 -1.</_> + <_>8 10 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.6296720423270017e-004</threshold> + <left_val>0.5594611167907715</left_val> + <right_val>0.3042711913585663</right_val></_></_></trees> + <stage_threshold>14.5467500686645510</stage_threshold> + <parent>5</parent> + <next>-1</next></_> + <_> + <!-- stage 7 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 4 20 6 -1.</_> + <_>0 6 20 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.8275858908891678e-003</threshold> + <left_node>1</left_node> + <right_val>0.2116018980741501</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 3 8 6 -1.</_> + <_>1 3 4 3 2.</_> + <_>5 6 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.1693858802318573e-003</threshold> + <left_val>0.6924685239791870</left_val> + <right_val>0.3043777048587799</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 15 6 4 -1.</_> + <_>7 17 6 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.5341319744475186e-004</threshold> + <left_val>0.3183285892009735</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 10 14 10 -1.</_> + <_>3 15 14 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.8054549843072891e-003</threshold> + <left_val>0.5456559062004089</left_val> + <right_val>0.2522268891334534</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 4 4 4 -1.</_> + <_>8 4 2 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1071180526632816e-004</threshold> + <left_val>0.2902618050575256</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 4 20 10 -1.</_> + <_>0 9 20 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.8318869881331921e-003</threshold> + <left_val>0.3130455911159515</left_val> + <right_val>0.6884937286376953</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 4 2 14 -1.</_> + <_>9 11 2 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.5633679443853907e-006</threshold> + <left_node>1</left_node> + <right_val>0.2962465882301331</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 0 16 4 -1.</_> + <_>2 2 16 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.2888139877468348e-004</threshold> + <left_val>0.3099626004695892</left_val> + <right_val>0.5752515196800232</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 12 6 8 -1.</_> + <_>4 12 3 4 2.</_> + <_>7 16 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6209259629249573e-003</threshold> + <left_val>0.3993195891380310</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 5 6 7 -1.</_> + <_>3 5 3 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.1338958591222763e-003</threshold> + <left_val>0.4827372133731842</left_val> + <right_val>0.7537832856178284</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 7 10 4 -1.</_> + <_>15 7 5 2 2.</_> + <_>10 9 5 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.1212290525436401e-003</threshold> + <left_val>0.2616927027702332</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 8 12 1 -1.</_> + <_>9 8 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.5447290390729904e-003</threshold> + <left_val>0.3108702898025513</left_val> + <right_val>0.5491235852241516</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 9 2 2 -1.</_> + <_>9 10 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.2652782071381807e-004</threshold> + <left_val>0.3239691853523254</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 4 2 4 -1.</_> + <_>9 6 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.6596331483451650e-005</threshold> + <left_val>0.6517410874366760</left_val> + <right_val>0.4178912043571472</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 6 3 6 -1.</_> + <_>10 6 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0138827199116349</threshold> + <left_node>1</left_node> + <right_val>0.6771203875541687</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 7 6 4 -1.</_> + <_>15 7 3 2 2.</_> + <_>12 9 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0493700392544270e-003</threshold> + <left_val>0.4159511029720306</left_val> + <right_val>0.5652891993522644</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 6 3 6 -1.</_> + <_>9 6 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0182153601199389</threshold> + <left_node>1</left_node> + <right_val>0.7689601182937622</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 6 18 6 -1.</_> + <_>1 6 9 3 2.</_> + <_>10 9 9 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0113345803692937</threshold> + <left_val>0.2873323857784271</left_val> + <right_val>0.4988932907581329</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 1 3 3 -1.</_> + <_>10 1 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.1097560897469521e-003</threshold> + <left_node>1</left_node> + <right_val>0.5463008284568787</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 8 5 2 -1.</_> + <_>10 9 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.2612891411408782e-004</threshold> + <left_val>0.3631235063076019</left_val> + <right_val>0.5512552261352539</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 1 3 3 -1.</_> + <_>9 1 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.0301548801362514e-003</threshold> + <left_node>1</left_node> + <right_val>0.1143767014145851</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 8 5 2 -1.</_> + <_>5 9 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.3587709185667336e-004</threshold> + <left_val>0.2891078889369965</left_val> + <right_val>0.5447341799736023</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 6 8 8 -1.</_> + <_>12 6 4 4 2.</_> + <_>8 10 4 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.2279507983475924e-004</threshold> + <left_node>1</left_node> + <right_val>0.3023431897163391</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 7 10 2 -1.</_> + <_>5 7 5 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0258371196687222</threshold> + <left_val>0.2167005985975266</left_val> + <right_val>0.5278152823448181</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 5 12 10 -1.</_> + <_>4 5 6 5 2.</_> + <_>10 10 6 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0217749103903770</threshold> + <left_node>1</left_node> + <right_val>0.3254834115505219</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 5 2 3 -1.</_> + <_>5 6 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7682299949228764e-003</threshold> + <left_val>0.5263050794601440</left_val> + <right_val>0.7526329159736633</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 14 6 3 -1.</_> + <_>7 15 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0137938102707267</threshold> + <left_val>0.7410330176353455</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 14 3 3 -1.</_> + <_>9 15 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.0852829590439796e-003</threshold> + <left_val>0.6836609840393066</left_val> + <right_val>0.4579071104526520</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_node>1</left_node> + <right_val>0.7449936270713806</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 10 8 9 -1.</_> + <_>1 13 8 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0100303199142218</threshold> + <left_val>0.4860779941082001</left_val> + <right_val>0.2361457049846649</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 2 3 -1.</_> + <_>9 8 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.4201927743852139e-003</threshold> + <left_val>0.1467327028512955</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 3 3 3 -1.</_> + <_>13 3 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.6961281225085258e-003</threshold> + <left_val>0.2347819954156876</left_val> + <right_val>0.5323377251625061</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 3 3 3 -1.</_> + <_>6 3 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.1498160250484943e-003</threshold> + <left_val>0.1477057039737701</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 6 2 12 -1.</_> + <_>5 10 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.4450740311294794e-003</threshold> + <left_val>0.3498533964157105</left_val> + <right_val>0.5803561806678772</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 11 18 4 -1.</_> + <_>10 11 9 2 2.</_> + <_>1 13 9 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0375034101307392</threshold> + <left_node>1</left_node> + <right_val>0.5259550809860230</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 12 6 2 -1.</_> + <_>7 13 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.7799441381357610e-004</threshold> + <left_val>0.4362882971763611</left_val> + <right_val>0.6208922863006592</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 0 3 6 -1.</_> + <_>7 0 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.0806080475449562e-003</threshold> + <left_val>0.2039460986852646</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 11 18 4 -1.</_> + <_>0 11 9 2 2.</_> + <_>9 13 9 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0328180007636547</threshold> + <left_val>0.5198358893394470</left_val> + <right_val>0.1371196061372757</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 12 6 2 -1.</_> + <_>7 13 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.5188988810405135e-004</threshold> + <left_node>1</left_node> + <right_val>0.6323429942131043</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 12 3 3 -1.</_> + <_>9 13 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.6485587954521179e-003</threshold> + <left_val>0.4720163047313690</left_val> + <right_val>0.6567087173461914</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 12 2 3 -1.</_> + <_>9 13 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.9827929791063070e-003</threshold> + <left_val>0.6053060293197632</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 11 4 3 -1.</_> + <_>8 12 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.6011310508474708e-003</threshold> + <left_val>0.5090519189834595</left_val> + <right_val>0.3116933107376099</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 3 4 2 -1.</_> + <_>13 4 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.0539939180016518e-003</threshold> + <left_val>0.3429804146289825</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 0 12 2 -1.</_> + <_>4 1 12 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.3212040327489376e-004</threshold> + <left_val>0.3838402926921845</left_val> + <right_val>0.5775598287582398</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- 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.0274521205574274</threshold> + <left_val>0.2143469005823135</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 11 6 2 -1.</_> + <_>1 12 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.3099439982324839e-004</threshold> + <left_val>0.5952966213226318</left_val> + <right_val>0.3760158121585846</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- 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>6.7144189961254597e-003</threshold> + <left_val>0.5692626833915710</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 1 6 10 -1.</_> + <_>7 6 6 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.3701690845191479e-003</threshold> + <left_val>0.5784304141998291</left_val> + <right_val>0.3974282145500183</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 3 3 6 -1.</_> + <_>0 5 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0189039595425129</threshold> + <left_val>0.1818892955780029</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 5 4 3 -1.</_> + <_>4 6 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.5850871615111828e-003</threshold> + <left_val>0.6849110126495361</left_val> + <right_val>0.4351584017276764</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>19 3 1 6 -1.</_> + <_>19 5 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.8810501359403133e-003</threshold> + <left_node>1</left_node> + <right_val>0.2726660966873169</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 15 8 2 -1.</_> + <_>6 16 8 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.0092082498595119e-004</threshold> + <left_val>0.4236431121826172</left_val> + <right_val>0.5844675898551941</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 3 1 6 -1.</_> + <_>0 5 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.8510579830035567e-003</threshold> + <left_node>1</left_node> + <right_val>0.3371320962905884</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 5 3 3 -1.</_> + <_>5 6 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.3273650594055653e-003</threshold> + <left_val>0.5270221829414368</left_val> + <right_val>0.8053650856018066</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 8 4 3 -1.</_> + <_>8 9 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.3820930402725935e-003</threshold> + <left_val>0.2866018116474152</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 6 6 3 -1.</_> + <_>12 6 2 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.9292969955131412e-003</threshold> + <left_val>0.5888946056365967</left_val> + <right_val>0.3895787000656128</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 13 2 6 -1.</_> + <_>8 16 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0149952201172709</threshold> + <left_node>1</left_node> + <right_val>0.2177816927433014</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 11 2 8 -1.</_> + <_>9 15 2 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0263307504355907</threshold> + <left_val>0.1775317043066025</left_val> + <right_val>0.5671470165252686</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 6 6 3 -1.</_> + <_>12 6 2 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.1734222322702408e-003</threshold> + <left_node>1</left_node> + <right_val>0.4652962088584900</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 15 15 5 -1.</_> + <_>10 15 5 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0272683501243591</threshold> + <left_val>0.4768311083316803</left_val> + <right_val>0.5695238709449768</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 14 2 2 -1.</_> + <_>2 15 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.8880263976752758e-004</threshold> + <left_node>1</left_node> + <right_val>0.3397401869297028</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 7 6 2 -1.</_> + <_>6 7 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0528849670663476e-003</threshold> + <left_val>0.6250041127204895</left_val> + <right_val>0.4288412034511566</right_val></_></_> + <_> + <!-- tree 31 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 3 6 1 -1.</_> + <_>10 3 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.2288072183728218e-003</threshold> + <left_val>0.5347762107849121</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 0 18 12 -1.</_> + <_>7 0 6 12 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0303954593837261</threshold> + <left_val>0.4115518927574158</left_val> + <right_val>0.5660753846168518</right_val></_></_> + <_> + <!-- tree 32 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 14 8 6 -1.</_> + <_>4 14 4 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0791139304637909</threshold> + <left_val>0.7881323099136353</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 15 15 5 -1.</_> + <_>5 15 5 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0182316694408655</threshold> + <left_val>0.3604339957237244</left_val> + <right_val>0.5569505095481873</right_val></_></_> + <_> + <!-- tree 33 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 3 6 1 -1.</_> + <_>10 3 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.2288072183728218e-003</threshold> + <left_val>0.5416644215583801</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 11 3 6 -1.</_> + <_>11 14 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.3922828626818955e-004</threshold> + <left_val>0.5507156848907471</left_val> + <right_val>0.3882277011871338</right_val></_></_> + <_> + <!-- tree 34 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 3 6 1 -1.</_> + <_>8 3 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.6501962505280972e-004</threshold> + <left_val>0.3185850977897644</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 11 3 6 -1.</_> + <_>6 14 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0326979681849480e-003</threshold> + <left_val>0.5578364133834839</left_val> + <right_val>0.3219245970249176</right_val></_></_> + <_> + <!-- tree 35 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 6 3 4 -1.</_> + <_>10 6 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.2997747920453548e-003</threshold> + <left_val>0.7073233127593994</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 10 4 7 -1.</_> + <_>12 10 2 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.3629042385146022e-004</threshold> + <left_val>0.5558015704154968</left_val> + <right_val>0.4613842070102692</right_val></_></_> + <_> + <!-- tree 36 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 6 3 4 -1.</_> + <_>9 6 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.0483231209218502e-003</threshold> + <left_val>0.6869289875030518</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 6 4 7 -1.</_> + <_>6 6 2 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.7529221996665001e-003</threshold> + <left_val>0.4870317876338959</left_val> + <right_val>0.2650370895862579</right_val></_></_> + <_> + <!-- tree 37 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 3 4 12 -1.</_> + <_>10 3 2 12 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0530780293047428</threshold> + <left_val>0.5281515121459961</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 8 3 4 -1.</_> + <_>11 8 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0225810110569000e-003</threshold> + <left_val>0.6085882186889648</left_val> + <right_val>0.4304867982864380</right_val></_></_> + <_> + <!-- tree 38 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 0 18 14 -1.</_> + <_>7 0 6 14 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0312706492841244</threshold> + <left_node>1</left_node> + <right_val>0.5445832014083862</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 8 6 11 -1.</_> + <_>5 8 3 11 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.3522169366478920e-003</threshold> + <left_val>0.5328335762023926</left_val> + <right_val>0.2364324033260346</right_val></_></_></trees> + <stage_threshold>18.5722503662109380</stage_threshold> + <parent>6</parent> + <next>-1</next></_> + <_> + <!-- stage 8 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 4 15 4 -1.</_> + <_>1 6 15 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.2215630896389484e-003</threshold> + <left_node>1</left_node> + <right_val>0.2625581026077271</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 5 10 8 -1.</_> + <_>5 9 10 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1097389981150627e-003</threshold> + <left_val>0.1564992964267731</left_val> + <right_val>0.6792883276939392</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 2 6 8 -1.</_> + <_>14 2 3 8 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0108458595350385</threshold> + <left_val>0.3485808968544006</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 6 6 14 -1.</_> + <_>14 6 3 7 2.</_> + <_>11 13 3 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.4230401767417789e-004</threshold> + <left_val>0.3698255121707916</left_val> + <right_val>0.5921658277511597</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 2 12 -1.</_> + <_>9 11 2 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.3311722371727228e-004</threshold> + <left_node>1</left_node> + <right_val>0.3007084131240845</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 7 4 6 -1.</_> + <_>3 9 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0134200565516949e-003</threshold> + <left_val>0.3624922931194305</left_val> + <right_val>0.7072426080703735</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 3 6 6 -1.</_> + <_>14 3 3 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0110935596749187</threshold> + <left_val>0.4416702091693878</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>15 2 4 4 -1.</_> + <_>15 4 4 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.9127531498670578e-003</threshold> + <left_val>0.3028708100318909</left_val> + <right_val>0.5417376160621643</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 6 7 -1.</_> + <_>3 2 3 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0129053099080920</threshold> + <left_val>0.4374504089355469</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 6 6 14 -1.</_> + <_>3 6 3 7 2.</_> + <_>6 13 3 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.2430912144482136e-003</threshold> + <left_val>0.4401589930057526</left_val> + <right_val>0.7565190792083740</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 6 16 8 -1.</_> + <_>4 10 16 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.1304309484548867e-004</threshold> + <left_val>0.2310786992311478</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 12 2 8 -1.</_> + <_>10 16 2 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.2308640182018280e-003</threshold> + <left_val>0.3568195998668671</left_val> + <right_val>0.5749999284744263</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 0 6 20 -1.</_> + <_>9 0 2 20 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.6400520000606775e-003</threshold> + <left_val>0.3593688905239105</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 7 16 12 -1.</_> + <_>1 7 8 6 2.</_> + <_>9 13 8 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0751010328531265</threshold> + <left_val>0.6363567709922791</left_val> + <right_val>0.2327028959989548</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 11 3 3 -1.</_> + <_>9 12 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.7012968249619007e-003</threshold> + <left_val>0.7074623703956604</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 9 4 5 -1.</_> + <_>11 9 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5588370151817799e-003</threshold> + <left_val>0.5700237154960632</left_val> + <right_val>0.3590450882911682</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 3 1 2 -1.</_> + <_>3 4 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.7687938786111772e-004</threshold> + <left_val>0.2805441021919251</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 17 5 3 -1.</_> + <_>7 18 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.4234727546572685e-004</threshold> + <left_val>0.4125418961048126</left_val> + <right_val>0.6177995800971985</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- 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.0128251099959016</threshold> + <left_node>1</left_node> + <right_val>0.5403078198432922</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 4 10 12 -1.</_> + <_>12 4 5 6 2.</_> + <_>7 10 5 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.5156567143276334e-004</threshold> + <left_val>0.5633643865585327</left_val> + <right_val>0.3356539011001587</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 4 3 -1.</_> + <_>8 15 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0120061598718166</threshold> + <left_val>0.7109510898590088</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 9 4 5 -1.</_> + <_>7 9 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3213419588282704e-003</threshold> + <left_val>0.4903850853443146</left_val> + <right_val>0.2824583053588867</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 9 8 2 -1.</_> + <_>9 9 4 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0203074403107166</threshold> + <left_val>0.1891369968652725</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>14 15 5 2 -1.</_> + <_>14 16 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.0180929936468601e-003</threshold> + <left_val>0.5377966165542603</left_val> + <right_val>0.3119494915008545</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 14 2 3 -1.</_> + <_>9 15 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.5315311290323734e-003</threshold> + <left_node>1</left_node> + <right_val>0.7206758260726929</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 7 8 4 -1.</_> + <_>1 7 4 2 2.</_> + <_>5 9 4 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.4381739571690559e-003</threshold> + <left_val>0.1854667961597443</left_val> + <right_val>0.4981732964515686</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>19 3 1 2 -1.</_> + <_>19 4 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5692010056227446e-003</threshold> + <left_node>1</left_node> + <right_val>0.2638274133205414</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 12 2 3 -1.</_> + <_>9 13 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.9516442231833935e-003</threshold> + <left_val>0.6871067285537720</left_val> + <right_val>0.4714686870574951</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 14 14 4 -1.</_> + <_>3 14 7 2 2.</_> + <_>10 16 7 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0274296794086695</threshold> + <left_val>0.1548285037279129</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 0 10 2 -1.</_> + <_>5 1 10 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4181969454512000e-003</threshold> + <left_val>0.4376842975616455</left_val> + <right_val>0.6327368021011353</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 14 4 6 -1.</_> + <_>11 16 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0130789401009679</threshold> + <left_val>0.3166814148426056</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 14 6 3 -1.</_> + <_>7 15 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.5092779435217381e-003</threshold> + <left_val>0.6199743747711182</left_val> + <right_val>0.4379687011241913</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 13 6 6 -1.</_> + <_>7 13 3 3 2.</_> + <_>10 16 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0189207307994366</threshold> + <left_node>1</left_node> + <right_val>0.1470714062452316</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 2 1 6 -1.</_> + <_>0 4 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1683350205421448e-003</threshold> + <left_val>0.5809459090232849</left_val> + <right_val>0.3431949019432068</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 7 8 2 -1.</_> + <_>6 8 8 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6401590546593070e-003</threshold> + <left_val>0.3959457874298096</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 7 6 1 -1.</_> + <_>9 7 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4005920093040913e-004</threshold> + <left_val>0.3240025043487549</left_val> + <right_val>0.5646647214889526</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 1 6 10 -1.</_> + <_>7 6 6 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.3137591090053320e-003</threshold> + <left_node>1</left_node> + <right_val>0.4274528026580811</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 2 6 2 -1.</_> + <_>0 3 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.9459029901772738e-003</threshold> + <left_val>0.3341667950153351</left_val> + <right_val>0.6627960205078125</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 4 2 4 -1.</_> + <_>11 4 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3612229668069631e-004</threshold> + <left_val>0.4046927988529205</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 10 3 6 -1.</_> + <_>11 13 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.0512032359838486e-004</threshold> + <left_val>0.5484058260917664</left_val> + <right_val>0.3569940924644470</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 9 8 2 -1.</_> + <_>7 9 4 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0175139904022217</threshold> + <left_val>0.1824150979518890</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 0 4 6 -1.</_> + <_>2 0 2 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0187350306659937</threshold> + <left_val>0.7971820235252380</left_val> + <right_val>0.5068569183349609</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 0 6 2 -1.</_> + <_>9 0 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0120656499639153</threshold> + <left_node>1</left_node> + <right_val>0.2167007029056549</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 15 2 3 -1.</_> + <_>9 16 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.6544178836047649e-003</threshold> + <left_val>0.6584178805351257</left_val> + <right_val>0.4628243148326874</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 12 1 2 -1.</_> + <_>3 13 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4501289697363973e-003</threshold> + <left_node>1</left_node> + <right_val>0.2090252041816711</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 5 11 3 -1.</_> + <_>4 6 11 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0109540196135640</threshold> + <left_val>0.5112305283546448</left_val> + <right_val>0.7784575819969177</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 4 2 4 -1.</_> + <_>11 4 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0157717093825340</threshold> + <left_val>0.5132359266281128</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 3 6 3 -1.</_> + <_>10 3 2 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0142526896670461</threshold> + <left_val>0.1742414981126785</left_val> + <right_val>0.5267148017883301</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 4 2 4 -1.</_> + <_>8 4 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.0411860279855318e-005</threshold> + <left_val>0.3418447971343994</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 3 6 3 -1.</_> + <_>8 3 2 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0234862994402647</threshold> + <left_val>0.5631265044212341</left_val> + <right_val>0.2006393969058991</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 4 4 3 -1.</_> + <_>11 5 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.2205449901521206e-003</threshold> + <left_node>1</left_node> + <right_val>0.6249648928642273</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 8 2 8 -1.</_> + <_>11 12 2 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0258124303072691</threshold> + <left_val>0.3203228116035461</left_val> + <right_val>0.5199329853057861</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 3 5 -1.</_> + <_>9 7 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.9526650430634618e-003</threshold> + <left_val>0.6140705943107605</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 7 2 5 -1.</_> + <_>10 7 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.1470049917697906e-003</threshold> + <left_val>0.6592895984649658</left_val> + <right_val>0.3711124956607819</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 11 1 6 -1.</_> + <_>14 13 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.2962448894977570e-003</threshold> + <left_node>1</left_node> + <right_val>0.2952111959457398</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 8 4 3 -1.</_> + <_>8 9 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.3961310032755136e-003</threshold> + <left_val>0.3320803940296173</left_val> + <right_val>0.5528414845466614</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 3 2 2 -1.</_> + <_>0 4 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.1055441834032536e-003</threshold> + <left_val>0.1710550040006638</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 14 5 6 -1.</_> + <_>4 16 5 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0108887795358896</threshold> + <left_val>0.3359434902667999</left_val> + <right_val>0.5674905180931091</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 4 4 3 -1.</_> + <_>11 5 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.6768421567976475e-003</threshold> + <left_node>1</left_node> + <right_val>0.4773241877555847</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 4 3 3 -1.</_> + <_>12 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.7729787230491638e-003</threshold> + <left_val>0.8081045150756836</left_val> + <right_val>0.4845828115940094</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 4 4 3 -1.</_> + <_>5 5 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.0439710505306721e-003</threshold> + <left_node>1</left_node> + <right_val>0.6784002184867859</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 15 4 2 -1.</_> + <_>7 15 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.6134641161188483e-004</threshold> + <left_val>0.5514639019966126</left_val> + <right_val>0.3642359972000122</right_val></_></_> + <_> + <!-- tree 31 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 1 5 9 -1.</_> + <_>15 4 5 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0579923614859581</threshold> + <left_node>1</left_node> + <right_val>0.1254435032606125</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 10 3 3 -1.</_> + <_>9 11 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.9384980704635382e-004</threshold> + <left_val>0.4424878954887390</left_val> + <right_val>0.5728461742401123</right_val></_></_> + <_> + <!-- tree 32 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 6 2 6 -1.</_> + <_>1 8 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.2353480607271194e-003</threshold> + <left_val>0.2805041968822479</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 4 8 15 -1.</_> + <_>2 9 8 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0127849299460649</threshold> + <left_val>0.1950912028551102</left_val> + <right_val>0.5652924776077271</right_val></_></_> + <_> + <!-- tree 33 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 12 3 2 -1.</_> + <_>9 13 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.1973669431172311e-004</threshold> + <left_node>1</left_node> + <right_val>0.6166483759880066</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 12 3 3 -1.</_> + <_>9 13 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.0646801507100463e-004</threshold> + <left_val>0.4526579976081848</left_val> + <right_val>0.5944486856460571</right_val></_></_> + <_> + <!-- tree 34 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 6 3 5 -1.</_> + <_>8 6 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.6339010326191783e-003</threshold> + <left_node>1</left_node> + <right_val>0.4086942076683044</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 3 6 2 -1.</_> + <_>7 3 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.8299999907612801e-003</threshold> + <left_val>0.2793526947498322</left_val> + <right_val>0.6444935202598572</right_val></_></_> + <_> + <!-- tree 35 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 1 8 10 -1.</_> + <_>10 1 4 5 2.</_> + <_>6 6 4 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.3992068171501160e-003</threshold> + <left_node>1</left_node> + <right_val>0.5671656131744385</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 0 20 10 -1.</_> + <_>10 0 10 5 2.</_> + <_>0 5 10 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.1081919968128204</threshold> + <left_val>0.5311812162399292</left_val> + <right_val>0.2614356875419617</right_val></_></_> + <_> + <!-- tree 36 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 3 3 1 -1.</_> + <_>7 3 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.5056560561060905e-004</threshold> + <left_node>1</left_node> + <right_val>0.2996774017810822</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 2 6 8 -1.</_> + <_>2 2 2 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0206112507730722</threshold> + <left_val>0.4489943087100983</left_val> + <right_val>0.6888279914855957</right_val></_></_> + <_> + <!-- tree 37 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 10 3 4 -1.</_> + <_>11 12 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0251290500164032</threshold> + <left_node>1</left_node> + <right_val>0.5196864008903503</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 6 3 8 -1.</_> + <_>12 10 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7922939732670784e-003</threshold> + <left_val>0.3466995954513550</left_val> + <right_val>0.5533587932586670</right_val></_></_> + <_> + <!-- tree 38 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 10 3 4 -1.</_> + <_>6 12 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5626220265403390e-003</threshold> + <left_node>1</left_node> + <right_val>0.3081440031528473</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 6 3 8 -1.</_> + <_>5 10 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.1898730928078294e-004</threshold> + <left_val>0.2693870961666107</left_val> + <right_val>0.5544489026069641</right_val></_></_> + <_> + <!-- tree 39 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 6 18 6 -1.</_> + <_>11 6 9 3 2.</_> + <_>2 9 9 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.8111421056091785e-003</threshold> + <left_val>0.5587847828865051</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 14 7 3 -1.</_> + <_>7 15 7 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.2484229411929846e-003</threshold> + <left_val>0.4672113060951233</left_val> + <right_val>0.6090825200080872</right_val></_></_> + <_> + <!-- tree 40 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 2 12 -1.</_> + <_>1 0 1 12 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0301472395658493</threshold> + <left_val>0.9027591943740845</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 2 18 16 -1.</_> + <_>1 10 18 8 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.2754867970943451</threshold> + <left_val>0.4719834923744202</left_val> + <right_val>0.2196920067071915</right_val></_></_> + <_> + <!-- tree 41 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 13 5 3 -1.</_> + <_>9 14 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.6894630175083876e-003</threshold> + <left_node>1</left_node> + <right_val>0.6273009181022644</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 13 4 3 -1.</_> + <_>8 14 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.2957701049745083e-003</threshold> + <left_val>0.4839217960834503</left_val> + <right_val>0.6909062266349793</right_val></_></_> + <_> + <!-- tree 42 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 6 18 6 -1.</_> + <_>0 6 9 3 2.</_> + <_>9 9 9 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0562110692262650</threshold> + <left_val>0.1738487929105759</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 13 6 3 -1.</_> + <_>7 14 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.6478560175746679e-003</threshold> + <left_val>0.6304144859313965</left_val> + <right_val>0.4474301934242249</right_val></_></_> + <_> + <!-- tree 43 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>17 4 1 3 -1.</_> + <_>17 5 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4534000074490905e-003</threshold> + <left_node>1</left_node> + <right_val>0.5302538275718689</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 11 1 9 -1.</_> + <_>12 14 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.8540920466184616e-003</threshold> + <left_val>0.5338397026062012</left_val> + <right_val>0.3796882927417755</right_val></_></_> + <_> + <!-- tree 44 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 4 1 3 -1.</_> + <_>2 5 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.8243022067472339e-004</threshold> + <left_node>1</left_node> + <right_val>0.3269836902618408</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 4 2 3 -1.</_> + <_>5 5 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.2509482055902481e-004</threshold> + <left_val>0.4554812014102936</left_val> + <right_val>0.6358348131179810</right_val></_></_></trees> + <stage_threshold>21.5781192779541020</stage_threshold> + <parent>7</parent> + <next>-1</next></_> + <_> + <!-- stage 9 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 2 18 3 -1.</_> + <_>7 2 6 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0198064409196377</threshold> + <left_val>0.2809725105762482</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 1 20 6 -1.</_> + <_>0 3 20 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.0395611692219973e-004</threshold> + <left_val>0.3119826018810272</left_val> + <right_val>0.7090306282043457</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.5563780218362808e-003</threshold> + <left_val>0.2981947958469391</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>13 7 6 4 -1.</_> + <_>16 7 3 2 2.</_> + <_>13 9 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0824160417541862e-003</threshold> + <left_val>0.3020560145378113</left_val> + <right_val>0.5808811187744141</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- 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.2893769033253193e-004</threshold> + <left_node>1</left_node> + <right_val>0.3738102912902832</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 4 19 10 -1.</_> + <_>0 9 19 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0180097296833992</threshold> + <left_val>0.2163126021623612</left_val> + <right_val>0.6619253754615784</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 8 3 12 -1.</_> + <_>9 12 3 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.3500190582126379e-003</threshold> + <left_node>1</left_node> + <right_val>0.2910403907299042</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 18 5 2 -1.</_> + <_>11 19 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.1822491483762860e-004</threshold> + <left_val>0.5578622817993164</left_val> + <right_val>0.3366627991199493</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- 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.2095321482047439e-004</threshold> + <left_val>0.4072425961494446</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 18 3 2 -1.</_> + <_>5 19 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.6780969761312008e-004</threshold> + <left_val>0.6859595775604248</left_val> + <right_val>0.3105461895465851</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 11 3 2 -1.</_> + <_>13 12 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.8000211245380342e-004</threshold> + <left_node>1</left_node> + <right_val>0.3337332904338837</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 5 8 4 -1.</_> + <_>8 5 4 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.0538640506565571e-005</threshold> + <left_val>0.3370958864688873</left_val> + <right_val>0.5451210737228394</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 2 18 6 -1.</_> + <_>1 2 9 3 2.</_> + <_>10 5 9 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0439147986471653</threshold> + <left_val>0.2625670135021210</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 5 14 6 -1.</_> + <_>3 7 14 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.6501338258385658e-003</threshold> + <left_val>0.6050462722778320</left_val> + <right_val>0.3232415020465851</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 1 2 6 -1.</_> + <_>18 3 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.8661491125822067e-003</threshold> + <left_node>1</left_node> + <right_val>0.3262613117694855</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 11 6 1 -1.</_> + <_>11 11 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.3069426687434316e-005</threshold> + <left_val>0.5817307829856873</left_val> + <right_val>0.4164389967918396</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 6 11 -1.</_> + <_>3 2 3 11 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0525337383151054</threshold> + <left_node>1</left_node> + <right_val>0.7095398902893066</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 12 2 3 -1.</_> + <_>4 13 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3818660518154502e-003</threshold> + <left_val>0.5292875766754150</left_val> + <right_val>0.2541388869285584</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 12 9 2 -1.</_> + <_>9 12 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.9264067355543375e-004</threshold> + <left_node>1</left_node> + <right_val>0.4085341095924377</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 4 6 15 -1.</_> + <_>9 4 3 15 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0855795070528984</threshold> + <left_val>0.5263236165046692</left_val> + <right_val>0.3003202974796295</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 11 6 1 -1.</_> + <_>7 11 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.8343339615967125e-004</threshold> + <left_node>1</left_node> + <right_val>0.4029205143451691</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 4 6 15 -1.</_> + <_>8 4 3 15 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.7924815490841866e-003</threshold> + <left_val>0.3521319925785065</left_val> + <right_val>0.6664004921913147</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 12 6 7 -1.</_> + <_>14 12 3 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0144286202266812</threshold> + <left_val>0.4593566060066223</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>18 3 2 9 -1.</_> + <_>18 6 2 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0456870011985302</threshold> + <left_val>0.1474756002426148</left_val> + <right_val>0.5178632140159607</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 1 3 1 -1.</_> + <_>9 1 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.5763090234249830e-003</threshold> + <left_val>0.1837278008460999</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 12 6 7 -1.</_> + <_>3 12 3 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0383018590509892</threshold> + <left_val>0.8082658052444458</left_val> + <right_val>0.5166687965393066</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- 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>2.8978290501981974e-003</threshold> + <left_val>0.4798013865947723</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 0 10 2 -1.</_> + <_>8 1 10 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.5165060069411993e-003</threshold> + <left_val>0.3346295952796936</left_val> + <right_val>0.5444449186325073</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- 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.6281982688233256e-004</threshold> + <left_val>0.3589026927947998</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 2 3 3 -1.</_> + <_>1 3 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.6684391088783741e-003</threshold> + <left_val>0.5983129739761353</left_val> + <right_val>0.2983964085578919</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 13 4 3 -1.</_> + <_>9 14 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1319789811968803e-003</threshold> + <left_node>1</left_node> + <right_val>0.6163223981857300</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 13 7 2 -1.</_> + <_>12 14 7 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.6037310063838959e-003</threshold> + <left_val>0.5217130184173584</left_val> + <right_val>0.2054159045219421</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 12 9 2 -1.</_> + <_>8 12 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1668079969240353e-004</threshold> + <left_node>1</left_node> + <right_val>0.3446668982505798</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 10 4 8 -1.</_> + <_>6 14 4 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.1659509986639023e-003</threshold> + <left_val>0.5597484707832336</left_val> + <right_val>0.2673786878585815</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 0 18 4 -1.</_> + <_>7 0 6 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0225694999098778</threshold> + <left_val>0.6900268197059631</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 0 5 2 -1.</_> + <_>12 1 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.7129601221531630e-004</threshold> + <left_val>0.4486638903617859</left_val> + <right_val>0.5508785247802734</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 7 1 12 -1.</_> + <_>7 13 1 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0154344597831368</threshold> + <left_val>0.2048323005437851</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 2 3 4 -1.</_> + <_>7 2 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.4861656650900841e-003</threshold> + <left_val>0.1254952996969223</left_val> + <right_val>0.5060356259346008</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 13 20 6 -1.</_> + <_>0 15 20 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.1180747002363205</threshold> + <left_val>0.0676330626010895</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 5 12 2 -1.</_> + <_>14 5 6 1 2.</_> + <_>8 6 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.2300079688429832e-003</threshold> + <left_val>0.5660700798034668</left_val> + <right_val>0.4292201101779938</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 2 3 -1.</_> + <_>8 15 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.0290351286530495e-003</threshold> + <left_val>0.7136403918266296</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 14 4 3 -1.</_> + <_>8 15 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.9325206354260445e-003</threshold> + <left_val>0.4338876008987427</left_val> + <right_val>0.7060875296592712</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 13 7 6 -1.</_> + <_>12 15 7 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0477359816431999</threshold> + <left_node>1</left_node> + <right_val>0.5268685221672058</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 0 8 12 -1.</_> + <_>10 0 4 6 2.</_> + <_>6 6 4 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0441555790603161</threshold> + <left_val>0.2580580115318298</left_val> + <right_val>0.5406960844993591</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 15 9 4 -1.</_> + <_>0 17 9 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0259834807366133</threshold> + <left_val>0.1905054003000259</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 0 2 5 -1.</_> + <_>10 0 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.7885831445455551e-003</threshold> + <left_val>0.2551892995834351</left_val> + <right_val>0.5339077115058899</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 2 6 -1.</_> + <_>9 5 1 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.7423451691865921e-003</threshold> + <left_val>0.4693309962749481</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>17 2 3 6 -1.</_> + <_>17 4 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0116547504439950</threshold> + <left_val>0.5261964201927185</left_val> + <right_val>0.3145434856414795</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 11 2 3 -1.</_> + <_>3 12 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.6982729583978653e-003</threshold> + <left_val>0.1756853014230728</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 13 3 3 -1.</_> + <_>7 14 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.2983349673449993e-003</threshold> + <left_val>0.7774729728698731</left_val> + <right_val>0.5124292969703674</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 12 5 3 -1.</_> + <_>14 13 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.9091778025031090e-003</threshold> + <left_val>0.5284559726715088</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 8 14 3 -1.</_> + <_>4 9 14 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5874979726504534e-004</threshold> + <left_val>0.3887802064418793</left_val> + <right_val>0.5501173734664917</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 12 5 3 -1.</_> + <_>1 13 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.2235877849161625e-003</threshold> + <left_val>0.2489829063415527</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 15 12 2 -1.</_> + <_>1 15 6 1 2.</_> + <_>7 16 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3308860361576080e-003</threshold> + <left_val>0.4262146055698395</left_val> + <right_val>0.5935062170028687</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 11 4 2 -1.</_> + <_>12 12 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.2055278792977333e-003</threshold> + <left_node>1</left_node> + <right_val>0.2545222938060761</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 8 3 5 -1.</_> + <_>10 8 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0140651697292924</threshold> + <left_val>0.4851990044116974</left_val> + <right_val>0.7021418809890747</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 2 6 -1.</_> + <_>10 5 1 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.7384149879217148e-003</threshold> + <left_val>0.7143270969390869</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 2 3 6 -1.</_> + <_>0 4 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.3406780567020178e-003</threshold> + <left_val>0.5175725221633911</left_val> + <right_val>0.2808643877506256</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 11 4 2 -1.</_> + <_>12 12 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0118806995451450</threshold> + <left_node>1</left_node> + <right_val>0.5173221826553345</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 7 3 5 -1.</_> + <_>10 7 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4226379571482539e-003</threshold> + <left_val>0.4502865970134735</left_val> + <right_val>0.5795695185661316</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 11 4 2 -1.</_> + <_>4 12 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.9858129564672709e-003</threshold> + <left_node>1</left_node> + <right_val>0.1915116012096405</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 8 3 5 -1.</_> + <_>9 8 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.0481580868363380e-003</threshold> + <left_val>0.6502432227134705</left_val> + <right_val>0.4559315145015717</right_val></_></_> + <_> + <!-- tree 31 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 3 3 1 -1.</_> + <_>10 3 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7122729914262891e-003</threshold> + <left_val>0.5376247167587280</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>16 5 3 8 -1.</_> + <_>17 5 1 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0169808696955442</threshold> + <left_val>0.7056233286857605</left_val> + <right_val>0.4914605915546417</right_val></_></_> + <_> + <!-- tree 32 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 3 3 1 -1.</_> + <_>9 3 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1290470138192177e-003</threshold> + <left_val>0.2678706049919128</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 5 3 8 -1.</_> + <_>2 5 1 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.8620059601962566e-003</threshold> + <left_val>0.4410853981971741</left_val> + <right_val>0.6368319988250732</right_val></_></_> + <_> + <!-- tree 33 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 1 3 3 -1.</_> + <_>11 1 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.8065758999437094e-003</threshold> + <left_val>0.2763563990592957</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>17 5 2 4 -1.</_> + <_>17 5 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.9090270660817623e-003</threshold> + <left_val>0.4867301881313324</left_val> + <right_val>0.6728776097297669</right_val></_></_> + <_> + <!-- tree 34 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 8 14 3 -1.</_> + <_>2 9 14 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1004370171576738e-003</threshold> + <left_val>0.4070514142513275</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 7 1 3 -1.</_> + <_>9 8 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.3396299220621586e-003</threshold> + <left_val>0.2604948878288269</left_val> + <right_val>0.6154860258102417</right_val></_></_> + <_> + <!-- tree 35 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 1 8 10 -1.</_> + <_>6 6 8 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.6068160552531481e-003</threshold> + <left_val>0.5731999874114990</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>13 0 6 8 -1.</_> + <_>16 0 3 4 2.</_> + <_>13 4 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0408311896026134</threshold> + <left_val>0.4973376989364624</left_val> + <right_val>0.7387006878852844</right_val></_></_> + <_> + <!-- tree 36 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 5 2 4 -1.</_> + <_>2 5 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.1082250215113163e-003</threshold> + <left_val>0.6984751224517822</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 2 12 2 -1.</_> + <_>4 3 12 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.3759730225428939e-004</threshold> + <left_val>0.2691167891025543</left_val> + <right_val>0.4741779863834381</right_val></_></_> + <_> + <!-- tree 37 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 8 4 4 -1.</_> + <_>8 10 4 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.6740820137783885e-003</threshold> + <left_val>0.3551014065742493</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 6 12 4 -1.</_> + <_>9 6 4 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0882877036929131</threshold> + <left_val>0.5244613885879517</left_val> + <right_val>0.2096650004386902</right_val></_></_> + <_> + <!-- tree 38 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 2 8 1 -1.</_> + <_>5 2 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.2009629113599658e-004</threshold> + <left_val>0.4131096899509430</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 1 6 10 -1.</_> + <_>3 1 2 10 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.6624617213383317e-004</threshold> + <left_val>0.4620293080806732</left_val> + <right_val>0.6775410175323486</right_val></_></_> + <_> + <!-- tree 39 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 6 8 2 -1.</_> + <_>8 6 4 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.5769668435677886e-004</threshold> + <left_node>1</left_node> + <right_val>0.5628275275230408</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 7 6 6 -1.</_> + <_>12 7 2 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.1304790861904621e-003</threshold> + <left_val>0.5576859712600708</left_val> + <right_val>0.4577650129795075</right_val></_></_> + <_> + <!-- tree 40 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 6 8 2 -1.</_> + <_>8 6 4 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.7317050737328827e-004</threshold> + <left_node>1</left_node> + <right_val>0.4959256052970886</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 7 6 6 -1.</_> + <_>6 7 2 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0111722303554416</threshold> + <left_val>0.5625635981559753</left_val> + <right_val>0.2047107964754105</right_val></_></_> + <_> + <!-- tree 41 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 14 16 4 -1.</_> + <_>3 16 16 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0434352196753025</threshold> + <left_node>1</left_node> + <right_val>0.2242148071527481</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 12 4 2 -1.</_> + <_>8 13 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.6736161503940821e-004</threshold> + <left_val>0.4533343911170960</left_val> + <right_val>0.6199932098388672</right_val></_></_> + <_> + <!-- tree 42 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 12 3 3 -1.</_> + <_>8 13 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.1452889088541269e-003</threshold> + <left_val>0.6662756204605103</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 12 6 1 -1.</_> + <_>8 12 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5233129961416125e-003</threshold> + <left_val>0.5007988214492798</left_val> + <right_val>0.2384992986917496</right_val></_></_> + <_> + <!-- tree 43 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 10 2 3 -1.</_> + <_>18 11 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.0854279864579439e-003</threshold> + <left_node>1</left_node> + <right_val>0.3753500878810883</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>16 8 4 6 -1.</_> + <_>16 10 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0360982008278370</threshold> + <left_val>0.5177171230316162</left_val> + <right_val>0.1634493023157120</right_val></_></_> + <_> + <!-- tree 44 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 3 2 1 -1.</_> + <_>9 3 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6179570229724050e-003</threshold> + <left_node>1</left_node> + <right_val>0.2587381899356842</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 1 3 9 -1.</_> + <_>8 1 1 9 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.2132300809025764e-004</threshold> + <left_val>0.6299533843994141</left_val> + <right_val>0.4658789932727814</right_val></_></_> + <_> + <!-- tree 45 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 11 11 6 -1.</_> + <_>5 14 11 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.1878539165481925e-004</threshold> + <left_node>1</left_node> + <right_val>0.3354076147079468</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 2 3 14 -1.</_> + <_>12 9 3 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0393395200371742</threshold> + <left_val>0.2154128998517990</left_val> + <right_val>0.5235713720321655</right_val></_></_> + <_> + <!-- tree 46 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 3 3 -1.</_> + <_>9 7 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0988829890266061e-003</threshold> + <left_val>0.6468896865844727</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 5 12 5 -1.</_> + <_>7 5 4 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1191420964896679e-003</threshold> + <left_val>0.2893089056015015</left_val> + <right_val>0.5254815816879273</right_val></_></_></trees> + <stage_threshold>22.5852909088134770</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>5.2359891124069691e-003</threshold> + <left_val>0.3299711048603058</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 5 6 10 -1.</_> + <_>5 5 3 5 2.</_> + <_>8 10 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.2169889416545630e-003</threshold> + <left_val>0.7041593194007874</left_val> + <right_val>0.3235465884208679</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 18 2 2 -1.</_> + <_>16 18 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.2303592935204506e-003</threshold> + <left_node>1</left_node> + <right_val>0.4961170852184296</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>16 18 2 2 -1.</_> + <_>16 18 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.2303592935204506e-003</threshold> + <left_val>0.7128043174743652</left_val> + <right_val>0.4961170852184296</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 4 2 5 -1.</_> + <_>9 4 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.5343261444941163e-004</threshold> + <left_val>0.3208472132682800</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 4 1 4 -1.</_> + <_>8 6 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.1777061414904892e-004</threshold> + <left_val>0.6613916754722595</left_val> + <right_val>0.3551332950592041</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 15 12 4 -1.</_> + <_>13 15 6 2 2.</_> + <_>7 17 6 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.7823769487440586e-003</threshold> + <left_val>0.3710134923458099</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 18 6 2 -1.</_> + <_>11 19 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.0361868236213923e-005</threshold> + <left_val>0.5746393799781799</left_val> + <right_val>0.3894880115985870</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 7 4 10 -1.</_> + <_>7 12 4 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.5061789676547050e-003</threshold> + <left_node>1</left_node> + <right_val>0.3054102957248688</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 6 10 8 -1.</_> + <_>5 10 10 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7013119941111654e-004</threshold> + <left_val>0.2885577976703644</left_val> + <right_val>0.6487745046615601</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 1 6 12 -1.</_> + <_>14 1 3 6 2.</_> + <_>11 7 3 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.3378930054605007e-003</threshold> + <left_node>1</left_node> + <right_val>0.3174431025981903</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 8 12 1 -1.</_> + <_>9 8 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.1369170863181353e-003</threshold> + <left_val>0.3820919990539551</left_val> + <right_val>0.5232893228530884</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 7 3 6 -1.</_> + <_>4 9 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0250400518998504e-003</threshold> + <left_val>0.3622795045375824</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 11 3 4 -1.</_> + <_>4 13 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.4726220949087292e-005</threshold> + <left_val>0.6538959145545960</left_val> + <right_val>0.4003680944442749</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 16 2 2 -1.</_> + <_>14 17 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.7102291611954570e-004</threshold> + <left_node>1</left_node> + <right_val>0.3893173038959503</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>15 15 2 2 -1.</_> + <_>15 16 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.7743012439459562e-004</threshold> + <left_val>0.5614532828330994</left_val> + <right_val>0.3687644004821777</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 12 6 2 -1.</_> + <_>7 13 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.9692091094329953e-004</threshold> + <left_node>1</left_node> + <right_val>0.6443027853965759</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 13 4 2 -1.</_> + <_>8 14 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.5945948911830783e-004</threshold> + <left_val>0.3380852937698364</left_val> + <right_val>0.5824648141860962</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 1 6 12 -1.</_> + <_>14 1 3 6 2.</_> + <_>11 7 3 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.3973900028504431e-004</threshold> + <left_node>1</left_node> + <right_val>0.3938767015933991</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 2 4 2 -1.</_> + <_>12 3 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.9061429025605321e-004</threshold> + <left_val>0.3427971005439758</left_val> + <right_val>0.5515698790550232</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 10 12 6 -1.</_> + <_>3 10 6 3 2.</_> + <_>9 13 6 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.4110242053866386e-003</threshold> + <left_node>1</left_node> + <right_val>0.3803538084030151</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 1 6 12 -1.</_> + <_>3 1 3 6 2.</_> + <_>6 7 3 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.5764907998964190e-004</threshold> + <left_val>0.6439505219459534</left_val> + <right_val>0.4168345928192139</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 6 4 14 -1.</_> + <_>18 6 2 7 2.</_> + <_>16 13 2 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0220006499439478</threshold> + <left_val>0.6654601097106934</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 1 10 8 -1.</_> + <_>10 1 5 4 2.</_> + <_>5 5 5 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.8731682151556015e-003</threshold> + <left_val>0.4182722866535187</left_val> + <right_val>0.5604724287986755</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 6 4 14 -1.</_> + <_>0 6 2 7 2.</_> + <_>2 13 2 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0274444594979286</threshold> + <left_val>0.6586862802505493</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 15 12 4 -1.</_> + <_>1 15 6 2 2.</_> + <_>7 17 6 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.9792269449681044e-003</threshold> + <left_val>0.3244912028312683</left_val> + <right_val>0.4882870018482208</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 17 3 3 -1.</_> + <_>11 17 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.6783691979944706e-003</threshold> + <left_val>0.2229079008102417</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 2 2 6 -1.</_> + <_>12 2 1 3 2.</_> + <_>11 5 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5057219570735469e-005</threshold> + <left_val>0.4107285141944885</left_val> + <right_val>0.5747591257095337</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 17 3 3 -1.</_> + <_>8 17 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.4136710241436958e-003</threshold> + <left_val>0.2065797001123428</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 15 4 3 -1.</_> + <_>8 16 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.3679239936172962e-003</threshold> + <left_val>0.4926423132419586</left_val> + <right_val>0.7139484882354736</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 15 4 2 -1.</_> + <_>12 15 2 1 2.</_> + <_>10 16 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.1426660716533661e-003</threshold> + <left_val>0.6780086755752564</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>13 13 4 3 -1.</_> + <_>13 14 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0109073901548982</threshold> + <left_val>0.5214930176734924</left_val> + <right_val>0.1143995970487595</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 13 4 3 -1.</_> + <_>3 14 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.8436761610209942e-003</threshold> + <left_node>1</left_node> + <right_val>0.1937526017427445</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 2 2 6 -1.</_> + <_>7 2 1 3 2.</_> + <_>8 5 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.0507230197545141e-005</threshold> + <left_val>0.3812577128410339</left_val> + <right_val>0.5514187812805176</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 1 16 3 -1.</_> + <_>2 2 16 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0163457896560431</threshold> + <left_val>0.2474023997783661</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 15 4 2 -1.</_> + <_>12 15 2 1 2.</_> + <_>10 16 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5987500082701445e-003</threshold> + <left_val>0.4817782938480377</left_val> + <right_val>0.5923079848289490</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 15 4 2 -1.</_> + <_>6 15 2 1 2.</_> + <_>8 16 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.0257978253066540e-003</threshold> + <left_val>0.7508208751678467</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 0 13 3 -1.</_> + <_>3 1 13 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.7750471644103527e-003</threshold> + <left_val>0.2879810929298401</left_val> + <right_val>0.5199695229530335</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 9 20 3 -1.</_> + <_>0 10 20 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.2470689620822668e-003</threshold> + <left_val>0.3044910132884979</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 7 9 2 -1.</_> + <_>6 8 9 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5409620245918632e-003</threshold> + <left_val>0.4063482880592346</left_val> + <right_val>0.5676562786102295</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 3 6 -1.</_> + <_>9 14 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0128581197932363</threshold> + <left_val>0.0967175588011742</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 10 2 2 -1.</_> + <_>9 11 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4824670506641269e-004</threshold> + <left_val>0.4537833034992218</left_val> + <right_val>0.6115375161170960</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 2 5 -1.</_> + <_>9 7 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.0210810303688049e-003</threshold> + <left_node>1</left_node> + <right_val>0.4807750880718231</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 6 10 3 -1.</_> + <_>5 6 5 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0287950299680233</threshold> + <left_val>0.3403795063495636</left_val> + <right_val>0.5255529284477234</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 2 5 -1.</_> + <_>10 7 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.0210810303688049e-003</threshold> + <left_node>1</left_node> + <right_val>0.7505835890769959</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 6 10 3 -1.</_> + <_>10 6 5 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.4121179059147835e-003</threshold> + <left_val>0.5455446839332581</left_val> + <right_val>0.3226068913936615</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 9 2 2 -1.</_> + <_>13 9 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.7217529024928808e-003</threshold> + <left_val>0.2311848998069763</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 3 12 11 -1.</_> + <_>8 3 4 11 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.1986588984727860</threshold> + <left_val>0.5271047949790955</left_val> + <right_val>0.1469929963350296</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 1 2 7 -1.</_> + <_>8 1 1 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5208719560177997e-005</threshold> + <left_val>0.3678138852119446</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 4 3 8 -1.</_> + <_>8 4 1 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.9089918136596680e-003</threshold> + <left_val>0.7131929993629456</left_val> + <right_val>0.4993866980075836</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 9 2 2 -1.</_> + <_>13 9 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.5106288958340883e-003</threshold> + <left_val>0.5312054157257080</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 6 2 2 -1.</_> + <_>12 6 1 1 2.</_> + <_>11 7 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.3921660613268614e-004</threshold> + <left_val>0.4689378142356873</left_val> + <right_val>0.5714021921157837</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 4 2 3 -1.</_> + <_>5 5 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.9443131797015667e-003</threshold> + <left_node>1</left_node> + <right_val>0.6948797702789307</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 5 1 3 -1.</_> + <_>6 6 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2065629707649350e-003</threshold> + <left_val>0.4004504978656769</left_val> + <right_val>0.5874881744384766</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 9 2 2 -1.</_> + <_>13 9 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.5106288958340883e-003</threshold> + <left_val>0.5329571962356567</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>16 14 3 3 -1.</_> + <_>16 15 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7514040227979422e-003</threshold> + <left_val>0.5545849204063416</left_val> + <right_val>0.3449581861495972</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 9 2 2 -1.</_> + <_>6 9 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.1978210210800171e-003</threshold> + <left_val>0.1217183023691177</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 14 3 3 -1.</_> + <_>1 15 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3092850567772985e-003</threshold> + <left_val>0.5375049710273743</left_val> + <right_val>0.3415625095367432</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 1 1 6 -1.</_> + <_>13 3 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.7396182566881180e-004</threshold> + <left_val>0.4195179045200348</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>13 3 7 2 -1.</_> + <_>13 4 7 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0105307102203369</threshold> + <left_val>0.3460753858089447</left_val> + <right_val>0.5155860185623169</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 6 20 14 -1.</_> + <_>0 13 20 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.4067229926586151</threshold> + <left_val>0.0580656789243221</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 4 3 6 -1.</_> + <_>0 6 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0263145491480827</threshold> + <left_val>0.1473449021577835</left_val> + <right_val>0.5559378266334534</right_val></_></_> + <_> + <!-- tree 31 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 1 9 6 -1.</_> + <_>10 3 9 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.2557149641215801e-003</threshold> + <left_node>1</left_node> + <right_val>0.5477715134620667</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 0 12 5 -1.</_> + <_>8 0 6 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0121548604220152</threshold> + <left_val>0.4207791090011597</left_val> + <right_val>0.5621880888938904</right_val></_></_> + <_> + <!-- tree 32 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 18 5 -1.</_> + <_>6 0 6 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0184365399181843</threshold> + <left_val>0.6447147130966187</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 1 9 6 -1.</_> + <_>1 3 9 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.3676147945225239e-004</threshold> + <left_val>0.2765127122402191</left_val> + <right_val>0.4888595938682556</right_val></_></_> + <_> + <!-- tree 33 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 15 2 2 -1.</_> + <_>15 16 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.6265541091561317e-003</threshold> + <left_node>1</left_node> + <right_val>0.5264691114425659</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>13 16 3 4 -1.</_> + <_>13 18 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.1119807176291943e-004</threshold> + <left_val>0.5785310268402100</left_val> + <right_val>0.4291102886199951</right_val></_></_> + <_> + <!-- tree 34 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 15 2 2 -1.</_> + <_>3 16 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.1454841266386211e-004</threshold> + <left_node>1</left_node> + <right_val>0.3455410897731781</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 16 3 4 -1.</_> + <_>4 18 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.5028748465701938e-004</threshold> + <left_val>0.6026918888092041</left_val> + <right_val>0.4143893122673035</right_val></_></_> + <_> + <!-- tree 35 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 14 1 3 -1.</_> + <_>11 15 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0347720235586166e-003</threshold> + <left_val>0.6095293760299683</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 13 5 3 -1.</_> + <_>9 14 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.3966631162911654e-003</threshold> + <left_val>0.6108282208442688</left_val> + <right_val>0.4707720875740051</right_val></_></_> + <_> + <!-- tree 36 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 3 6 -1.</_> + <_>0 2 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.1795909162610769e-003</threshold> + <left_node>1</left_node> + <right_val>0.3244366943836212</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 1 6 3 -1.</_> + <_>6 1 2 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.6528950072824955e-004</threshold> + <left_val>0.3830757141113281</left_val> + <right_val>0.5734326243400574</right_val></_></_> + <_> + <!-- tree 37 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 13 4 3 -1.</_> + <_>9 14 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.3725210279226303e-003</threshold> + <left_node>1</left_node> + <right_val>0.6610919237136841</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 15 5 3 -1.</_> + <_>8 16 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.5799809955060482e-003</threshold> + <left_val>0.6139307022094727</left_val> + <right_val>0.4686149954795837</right_val></_></_> + <_> + <!-- tree 38 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 3 3 2 -1.</_> + <_>9 3 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.0194388758391142e-004</threshold> + <left_node>1</left_node> + <right_val>0.3520022034645081</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 8 18 2 -1.</_> + <_>1 9 18 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.6952210939489305e-004</threshold> + <left_val>0.2578754127025604</left_val> + <right_val>0.5467242002487183</right_val></_></_> + <_> + <!-- tree 39 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 14 1 3 -1.</_> + <_>11 15 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.9746137857437134e-004</threshold> + <left_val>0.4820146858692169</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 13 6 3 -1.</_> + <_>8 14 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.6688039544969797e-003</threshold> + <left_val>0.5710150003433228</left_val> + <right_val>0.4831911027431488</right_val></_></_> + <_> + <!-- tree 40 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 1 3 -1.</_> + <_>8 15 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.9501030743122101e-004</threshold> + <left_val>0.6133679151535034</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 13 12 4 -1.</_> + <_>4 13 6 2 2.</_> + <_>10 15 6 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.1904921419918537e-003</threshold> + <left_val>0.4928582906723023</left_val> + <right_val>0.2581309080123901</right_val></_></_> + <_> + <!-- tree 41 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 7 2 2 -1.</_> + <_>10 7 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.2274440056644380e-004</threshold> + <left_val>0.4471124112606049</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>13 4 2 8 -1.</_> + <_>14 4 1 4 2.</_> + <_>13 8 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.5176713764667511e-003</threshold> + <left_val>0.5161024928092957</left_val> + <right_val>0.3316533863544464</right_val></_></_> + <_> + <!-- tree 42 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 5 4 6 -1.</_> + <_>0 7 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0366236083209515</threshold> + <left_val>0.0926062166690826</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 7 2 2 -1.</_> + <_>9 7 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.1103712283074856e-003</threshold> + <left_val>0.8522114753723145</left_val> + <right_val>0.5137907862663269</right_val></_></_> + <_> + <!-- tree 43 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 0 3 7 -1.</_> + <_>14 0 1 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.6017331555485725e-003</threshold> + <left_node>1</left_node> + <right_val>0.5459060072898865</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 2 2 14 -1.</_> + <_>11 2 1 14 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0255786404013634</threshold> + <left_val>0.5219352841377258</left_val> + <right_val>0.1927185952663422</right_val></_></_> + <_> + <!-- tree 44 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 0 3 7 -1.</_> + <_>5 0 1 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0114474399015307</threshold> + <left_node>1</left_node> + <right_val>0.1916002035140991</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 5 8 12 -1.</_> + <_>5 5 4 6 2.</_> + <_>9 11 4 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.2427501436322927e-004</threshold> + <left_val>0.5231571197509766</left_val> + <right_val>0.3535340130329132</right_val></_></_> + <_> + <!-- tree 45 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 4 6 3 -1.</_> + <_>11 5 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.7127500921487808e-003</threshold> + <left_node>1</left_node> + <right_val>0.6464101076126099</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 3 4 3 -1.</_> + <_>12 4 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0113375699147582</threshold> + <left_val>0.7383037805557251</left_val> + <right_val>0.4964743852615356</right_val></_></_> + <_> + <!-- tree 46 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 10 12 -1.</_> + <_>5 5 5 6 2.</_> + <_>10 11 5 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.1453882157802582e-003</threshold> + <left_val>0.3611705899238586</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 6 12 3 -1.</_> + <_>9 6 6 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.5570756345987320e-003</threshold> + <left_val>0.3421907126903534</left_val> + <right_val>0.5943511724472046</right_val></_></_> + <_> + <!-- tree 47 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 6 2 7 -1.</_> + <_>9 6 1 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.2993308957666159e-003</threshold> + <left_val>0.4550104141235352</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 5 2 4 -1.</_> + <_>9 5 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.8430930580943823e-003</threshold> + <left_val>0.4716862142086029</left_val> + <right_val>0.6656190752983093</right_val></_></_> + <_> + <!-- tree 48 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 3 3 -1.</_> + <_>9 7 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.9116540513932705e-004</threshold> + <left_node>1</left_node> + <right_val>0.4592716991901398</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 1 6 4 -1.</_> + <_>7 1 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0254964698106050</threshold> + <left_val>0.6563401222229004</left_val> + <right_val>0.1258835047483444</right_val></_></_> + <_> + <!-- tree 49 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 16 7 3 -1.</_> + <_>13 17 7 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0157483592629433</threshold> + <left_node>1</left_node> + <right_val>0.5239502191543579</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 4 3 3 -1.</_> + <_>12 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0180461201816797</threshold> + <left_val>0.8015851974487305</left_val> + <right_val>0.5007957816123962</right_val></_></_> + <_> + <!-- tree 50 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 16 7 3 -1.</_> + <_>0 17 7 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0103233903646469</threshold> + <left_node>1</left_node> + <right_val>0.2274820059537888</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 4 3 3 -1.</_> + <_>5 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6452240524813533e-003</threshold> + <left_val>0.4351946115493774</left_val> + <right_val>0.5867627859115601</right_val></_></_> + <_> + <!-- tree 51 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 9 8 10 -1.</_> + <_>12 9 4 10 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0158811490982771</threshold> + <left_val>0.4465051889419556</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 10 12 5 -1.</_> + <_>12 10 4 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0105865197256207</threshold> + <left_val>0.4544458091259003</left_val> + <right_val>0.5707110762596130</right_val></_></_> + <_> + <!-- tree 52 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 9 8 10 -1.</_> + <_>4 9 4 10 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0215316899120808</threshold> + <left_val>0.6527643799781799</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 10 12 5 -1.</_> + <_>4 10 4 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.2480469457805157e-003</threshold> + <left_val>0.3444727957248688</left_val> + <right_val>0.5324636101722717</right_val></_></_></trees> + <stage_threshold>25.6093006134033200</stage_threshold> + <parent>9</parent> + <next>-1</next></_> + <_> + <!-- stage 11 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 3 6 2 -1.</_> + <_>5 3 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.8219340126961470e-003</threshold> + <left_val>0.3108788132667542</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 0 17 9 -1.</_> + <_>0 3 17 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.1313941627740860e-003</threshold> + <left_val>0.3133237063884735</left_val> + <right_val>0.6645867228507996</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.7055979697033763e-003</threshold> + <left_val>0.2640131115913391</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 4 6 4 -1.</_> + <_>12 4 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.4483548814896494e-005</threshold> + <left_val>0.5647205114364624</left_val> + <right_val>0.3485372960567474</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 10 20 4 -1.</_> + <_>0 12 20 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.8342390325851738e-004</threshold> + <left_node>1</left_node> + <right_val>0.3140654861927033</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 3 6 5 -1.</_> + <_>6 3 2 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.1868910882622004e-003</threshold> + <left_val>0.6489198803901672</left_val> + <right_val>0.3887729048728943</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 1 18 4 -1.</_> + <_>7 1 6 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.1604432016611099</threshold> + <left_node>1</left_node> + <right_val>0.7216529846191406</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>13 9 2 3 -1.</_> + <_>13 9 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.7285560071468353e-003</threshold> + <left_val>0.1653137952089310</left_val> + <right_val>0.5139825940132141</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 15 7 4 -1.</_> + <_>6 17 7 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.2638481469766703e-006</threshold> + <left_val>0.3140619993209839</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 17 4 2 -1.</_> + <_>3 18 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.5551197146996856e-004</threshold> + <left_val>0.5993698835372925</left_val> + <right_val>0.3317398130893707</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 4 8 10 -1.</_> + <_>9 9 8 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0108223203569651</threshold> + <left_val>0.2652938067913055</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 17 3 2 -1.</_> + <_>10 17 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.5834020711481571e-003</threshold> + <left_val>0.1849568933248520</left_val> + <right_val>0.5313957929611206</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 2 4 8 -1.</_> + <_>8 6 4 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.0205070506781340e-003</threshold> + <left_node>1</left_node> + <right_val>0.4040099978446960</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 4 14 12 -1.</_> + <_>3 4 7 6 2.</_> + <_>10 10 7 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0778646171092987</threshold> + <left_val>0.6158189773559570</left_val> + <right_val>0.1786486953496933</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 7 6 4 -1.</_> + <_>9 7 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0264943800866604</threshold> + <left_val>0.4511089920997620</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 7 9 4 -1.</_> + <_>6 9 9 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0369121097028255</threshold> + <left_val>0.4528219997882843</left_val> + <right_val>0.5972282886505127</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 10 3 3 -1.</_> + <_>2 11 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.7857790961861610e-003</threshold> + <left_node>1</left_node> + <right_val>0.2533892095088959</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 6 2 9 -1.</_> + <_>4 9 2 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.3849771656095982e-004</threshold> + <left_val>0.3410412073135376</left_val> + <right_val>0.5923643708229065</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 11 3 3 -1.</_> + <_>9 12 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0110031999647617</threshold> + <left_val>0.6958044171333313</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 1 15 2 -1.</_> + <_>3 2 15 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1737640015780926e-003</threshold> + <left_val>0.3851084113121033</left_val> + <right_val>0.5408189296722412</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 8 2 3 -1.</_> + <_>9 9 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.6596669815480709e-003</threshold> + <left_val>0.2009308934211731</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 6 2 5 -1.</_> + <_>10 6 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.4822750128805637e-003</threshold> + <left_val>0.6295393109321594</left_val> + <right_val>0.4395040869712830</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 2 3 -1.</_> + <_>9 8 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.4606071896851063e-003</threshold> + <left_val>0.2405299991369247</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 10 12 10 -1.</_> + <_>4 15 12 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.5969649907201529e-003</threshold> + <left_val>0.5450174212455750</left_val> + <right_val>0.3782357871532440</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 10 4 2 -1.</_> + <_>0 11 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.6222559865564108e-003</threshold> + <left_val>0.3033896982669830</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 15 9 2 -1.</_> + <_>5 16 9 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2059339787811041e-003</threshold> + <left_val>0.4633778929710388</left_val> + <right_val>0.6335952281951904</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 6 3 -1.</_> + <_>8 15 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.3124938383698463e-003</threshold> + <left_node>1</left_node> + <right_val>0.6598826050758362</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 16 4 3 -1.</_> + <_>8 17 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.4961250387132168e-003</threshold> + <left_val>0.6621696949005127</left_val> + <right_val>0.4755246937274933</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 9 4 2 -1.</_> + <_>8 10 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.3860689941793680e-003</threshold> + <left_val>0.2801201045513153</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 3 14 2 -1.</_> + <_>3 4 14 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.1588460337370634e-004</threshold> + <left_val>0.3829489052295685</left_val> + <right_val>0.5623626708984375</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 12 1 2 -1.</_> + <_>11 13 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.0330002927221358e-005</threshold> + <left_val>0.4536342918872833</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 12 12 1 -1.</_> + <_>8 12 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.0976549421902746e-004</threshold> + <left_val>0.5608139038085938</left_val> + <right_val>0.4265779852867127</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 1 2 -1.</_> + <_>0 3 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3642259873449802e-003</threshold> + <left_node>1</left_node> + <right_val>0.2637091875076294</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 4 4 6 -1.</_> + <_>9 4 2 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5483660390600562e-003</threshold> + <left_val>0.4170750975608826</left_val> + <right_val>0.5932987928390503</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 20 14 -1.</_> + <_>10 2 10 7 2.</_> + <_>0 9 10 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.1917960941791534</threshold> + <left_val>0.5256764292716980</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>14 6 1 3 -1.</_> + <_>14 7 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.4776909053325653e-003</threshold> + <left_val>0.6632621884346008</left_val> + <right_val>0.4892588853836060</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 4 20 12 -1.</_> + <_>0 4 10 6 2.</_> + <_>10 10 10 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.1264917999505997</threshold> + <left_val>0.1499778926372528</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 12 1 2 -1.</_> + <_>8 13 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.5253327193204314e-005</threshold> + <left_val>0.4233320057392120</left_val> + <right_val>0.5756040215492249</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 18 3 2 -1.</_> + <_>10 18 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.1856421157717705e-003</threshold> + <left_val>0.5288826823234558</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 17 6 2 -1.</_> + <_>11 17 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.7478230185806751e-004</threshold> + <left_val>0.4524017870426178</left_val> + <right_val>0.5604125261306763</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 6 2 3 -1.</_> + <_>5 7 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.2906810045242310e-003</threshold> + <left_val>0.5578274130821228</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 4 3 3 -1.</_> + <_>5 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6744500026106834e-003</threshold> + <left_val>0.3323057889938355</left_val> + <right_val>0.5558788180351257</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 15 3 2 -1.</_> + <_>14 16 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2349759927019477e-003</threshold> + <left_node>1</left_node> + <right_val>0.3653947114944458</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 3 3 4 -1.</_> + <_>12 3 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.7158754467964172e-003</threshold> + <left_val>0.1924533993005753</left_val> + <right_val>0.5313649773597717</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 15 3 2 -1.</_> + <_>3 16 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.6613621525466442e-003</threshold> + <left_node>1</left_node> + <right_val>0.2027730941772461</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 12 2 3 -1.</_> + <_>9 13 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.5815992206335068e-003</threshold> + <left_val>0.7636060118675232</left_val> + <right_val>0.5140826106071472</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 13 3 7 -1.</_> + <_>10 13 1 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0143521204590797</threshold> + <left_val>0.5252975821495056</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 12 5 3 -1.</_> + <_>12 13 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.7948719263076782e-003</threshold> + <left_val>0.2632937133312225</left_val> + <right_val>0.5328689217567444</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 18 3 2 -1.</_> + <_>9 18 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.4155680332332850e-003</threshold> + <left_val>0.2416087985038757</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 7 12 4 -1.</_> + <_>4 7 6 2 2.</_> + <_>10 9 6 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.2639090679585934e-003</threshold> + <left_val>0.3936544954776764</left_val> + <right_val>0.5478742122650147</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 19 14 1 -1.</_> + <_>6 19 7 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.7177697569131851e-003</threshold> + <left_val>0.4788199067115784</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>16 14 3 2 -1.</_> + <_>16 15 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.2232629600912333e-003</threshold> + <left_val>0.3631612062454224</left_val> + <right_val>0.5288316011428833</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 0 6 10 -1.</_> + <_>1 0 3 5 2.</_> + <_>4 5 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0421883687376976</threshold> + <left_val>0.6931139230728149</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 0 4 10 -1.</_> + <_>1 0 2 5 2.</_> + <_>3 5 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0198757499456406</threshold> + <left_val>0.4520100057125092</left_val> + <right_val>0.6855055093765259</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 3 5 6 -1.</_> + <_>15 5 5 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0311345104128122</threshold> + <left_node>1</left_node> + <right_val>0.5300424098968506</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 5 2 15 -1.</_> + <_>9 10 2 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.7032387703657150e-003</threshold> + <left_val>0.5606892108917236</left_val> + <right_val>0.4230622947216034</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 3 5 6 -1.</_> + <_>0 5 5 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.2733682096004486e-003</threshold> + <left_node>1</left_node> + <right_val>0.3247228860855103</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 0 3 2 -1.</_> + <_>7 0 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.1231069006025791e-003</threshold> + <left_val>0.1985695958137512</left_val> + <right_val>0.5349872708320618</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 8 8 2 -1.</_> + <_>16 8 4 1 2.</_> + <_>12 9 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.6453849063254893e-004</threshold> + <left_val>0.4207508862018585</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 8 12 1 -1.</_> + <_>9 8 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0303558893501759</threshold> + <left_val>0.5153458714485169</left_val> + <right_val>0.3118101060390472</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 13 3 3 -1.</_> + <_>3 14 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.2992769740521908e-003</threshold> + <left_val>0.3274506926536560</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 13 3 2 -1.</_> + <_>5 14 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.9509199773892760e-004</threshold> + <left_val>0.5953078269958496</left_val> + <right_val>0.4225521087646484</right_val></_></_> + <_> + <!-- tree 31 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 15 3 3 -1.</_> + <_>9 16 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.7784480527043343e-003</threshold> + <left_val>0.7211179733276367</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 15 7 3 -1.</_> + <_>7 16 7 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0169175993651152</threshold> + <left_val>0.4936591982841492</left_val> + <right_val>0.7030277252197266</right_val></_></_> + <_> + <!-- tree 32 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 14 11 6 -1.</_> + <_>3 16 11 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0519485697150230</threshold> + <left_val>0.1425534933805466</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 19 14 1 -1.</_> + <_>7 19 7 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.4751220159232616e-003</threshold> + <left_val>0.6059331893920898</left_val> + <right_val>0.4393995106220245</right_val></_></_> + <_> + <!-- tree 33 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 17 6 2 -1.</_> + <_>11 17 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5210839592327829e-005</threshold> + <left_val>0.4488849937915802</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 11 6 2 -1.</_> + <_>14 11 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0235579684376717e-003</threshold> + <left_val>0.4256550073623657</left_val> + <right_val>0.5795438289642334</right_val></_></_> + <_> + <!-- tree 34 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 17 6 2 -1.</_> + <_>7 17 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0427719826111570e-004</threshold> + <left_val>0.4246039986610413</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 1 9 10 -1.</_> + <_>3 1 3 10 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.7853781878948212e-003</threshold> + <left_val>0.4958009123802185</left_val> + <right_val>0.6759430766105652</right_val></_></_> + <_> + <!-- tree 35 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 1 3 3 -1.</_> + <_>11 1 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.4012699034065008e-003</threshold> + <left_val>0.5423480868339539</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 5 6 4 -1.</_> + <_>9 5 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.8582378551363945e-004</threshold> + <left_val>0.3636542856693268</left_val> + <right_val>0.5464348793029785</right_val></_></_> + <_> + <!-- tree 36 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 1 3 3 -1.</_> + <_>8 1 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.2973360028117895e-003</threshold> + <left_val>0.2548818886280060</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 4 4 11 -1.</_> + <_>2 4 2 11 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0143301896750927</threshold> + <left_val>0.6587656736373901</left_val> + <right_val>0.4532802104949951</right_val></_></_> + <_> + <!-- tree 37 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 6 4 -1.</_> + <_>9 5 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.8565965890884399e-004</threshold> + <left_val>0.3822771012783051</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 0 8 10 -1.</_> + <_>10 0 4 5 2.</_> + <_>6 5 4 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0466407611966133</threshold> + <left_val>0.3077321946620941</left_val> + <right_val>0.5244132876396179</right_val></_></_> + <_> + <!-- tree 38 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 6 5 14 -1.</_> + <_>6 13 5 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.1190730035305023</threshold> + <left_val>0.1033862978219986</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 5 4 14 -1.</_> + <_>8 12 4 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0193332806229591</threshold> + <left_val>0.5554745197296143</left_val> + <right_val>0.3221316933631897</right_val></_></_> + <_> + <!-- tree 39 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 7 6 5 -1.</_> + <_>9 7 2 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0314278490841389</threshold> + <left_val>0.4682379066944122</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 3 3 9 -1.</_> + <_>9 6 3 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.0082130504306406e-004</threshold> + <left_val>0.5373070240020752</left_val> + <right_val>0.3800666928291321</right_val></_></_> + <_> + <!-- tree 40 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 1 3 3 -1.</_> + <_>9 1 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.2584900297224522e-003</threshold> + <left_val>0.1799207031726837</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 6 2 4 -1.</_> + <_>10 6 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.2861045375466347e-003</threshold> + <left_val>0.5095068812370300</left_val> + <right_val>0.7544605135917664</right_val></_></_> + <_> + <!-- tree 41 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 8 6 9 -1.</_> + <_>10 8 3 9 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.0529709290713072e-003</threshold> + <left_val>0.5628644824028015</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>16 4 3 8 -1.</_> + <_>17 4 1 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.2524869311600924e-003</threshold> + <left_val>0.4801689088344574</left_val> + <right_val>0.5802102088928223</right_val></_></_> + <_> + <!-- tree 42 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 9 10 6 -1.</_> + <_>5 9 5 3 2.</_> + <_>10 12 5 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0318849012255669</threshold> + <left_val>0.1742745041847229</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 5 6 4 -1.</_> + <_>8 5 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.8379340181127191e-003</threshold> + <left_val>0.3466596901416779</left_val> + <right_val>0.5107154846191406</right_val></_></_> + <_> + <!-- tree 43 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 8 4 2 -1.</_> + <_>9 9 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.8512680223211646e-004</threshold> + <left_node>1</left_node> + <right_val>0.5326086282730103</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 7 2 2 -1.</_> + <_>11 7 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.5407879147678614e-003</threshold> + <left_val>0.6342775225639343</left_val> + <right_val>0.4992693066596985</right_val></_></_> + <_> + <!-- tree 44 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 12 4 8 -1.</_> + <_>8 12 2 4 2.</_> + <_>10 16 2 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.1559060811996460e-003</threshold> + <left_val>0.3433429002761841</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 1 4 9 -1.</_> + <_>0 4 4 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0449687503278255</threshold> + <left_val>0.1868136972188950</left_val> + <right_val>0.5215464830398560</right_val></_></_> + <_> + <!-- tree 45 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 10 3 3 -1.</_> + <_>9 11 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.8984281495213509e-003</threshold> + <left_node>1</left_node> + <right_val>0.6229305267333984</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 11 4 2 -1.</_> + <_>8 12 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.2763120252639055e-003</threshold> + <left_val>0.4935772120952606</left_val> + <right_val>0.7217944860458374</right_val></_></_> + <_> + <!-- tree 46 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 8 4 2 -1.</_> + <_>7 9 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0161520185647532e-004</threshold> + <left_node>1</left_node> + <right_val>0.5007976293563843</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 8 6 1 -1.</_> + <_>9 8 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.6290300118271261e-004</threshold> + <left_val>0.6024149060249329</left_val> + <right_val>0.2329508066177368</right_val></_></_> + <_> + <!-- tree 47 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 0 4 9 -1.</_> + <_>16 0 2 9 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.0541364625096321e-003</threshold> + <left_val>0.4510416984558106</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>16 0 3 6 -1.</_> + <_>16 3 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0353984907269478</threshold> + <left_val>0.5141996741294861</left_val> + <right_val>0.2860291898250580</right_val></_></_> + <_> + <!-- tree 48 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 4 9 -1.</_> + <_>2 0 2 9 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.6469351984560490e-003</threshold> + <left_val>0.4704925119876862</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 0 3 6 -1.</_> + <_>1 3 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.4807190056890249e-003</threshold> + <left_val>0.4179851114749908</left_val> + <right_val>0.6726647019386292</right_val></_></_> + <_> + <!-- tree 49 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 6 9 -1.</_> + <_>11 7 2 9 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.1088787838816643e-003</threshold> + <left_val>0.5809801816940308</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 6 3 6 -1.</_> + <_>11 6 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.0714469719678164e-003</threshold> + <left_val>0.6074783802032471</left_val> + <right_val>0.4524059891700745</right_val></_></_> + <_> + <!-- tree 50 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 2 18 2 -1.</_> + <_>1 2 9 1 2.</_> + <_>10 3 9 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.8939060866832733e-003</threshold> + <left_val>0.3383519947528839</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 8 6 8 -1.</_> + <_>7 8 2 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3467279495671391e-003</threshold> + <left_val>0.5696910023689270</left_val> + <right_val>0.3970845043659210</right_val></_></_> + <_> + <!-- tree 51 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 0 6 16 -1.</_> + <_>11 0 2 16 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0907791331410408</threshold> + <left_val>0.1502701938152313</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>14 1 6 18 -1.</_> + <_>17 1 3 9 2.</_> + <_>14 10 3 9 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0831717625260353</threshold> + <left_val>0.7573670744895935</left_val> + <right_val>0.4936437010765076</right_val></_></_> + <_> + <!-- tree 52 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 9 2 3 -1.</_> + <_>2 10 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4107000315561891e-003</threshold> + <left_val>0.3390932977199554</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 1 6 18 -1.</_> + <_>0 1 3 9 2.</_> + <_>3 10 3 9 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0556687600910664</threshold> + <left_val>0.5025097131729126</left_val> + <right_val>0.7422083020210266</right_val></_></_> + <_> + <!-- tree 53 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 8 4 12 -1.</_> + <_>11 8 2 12 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0577015392482281</threshold> + <left_val>0.5197371840476990</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 1 18 18 -1.</_> + <_>2 10 18 9 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.4250329136848450</threshold> + <left_val>0.0973469167947769</left_val> + <right_val>0.5185739994049072</right_val></_></_> + <_> + <!-- tree 54 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 3 3 1 -1.</_> + <_>7 3 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.4380719191394746e-004</threshold> + <left_val>0.3649350106716156</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 12 2 2 -1.</_> + <_>4 13 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7924769781529903e-004</threshold> + <left_val>0.5619279146194458</left_val> + <right_val>0.3760297000408173</right_val></_></_> + <_> + <!-- tree 55 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 13 5 3 -1.</_> + <_>8 14 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.0382469780743122e-003</threshold> + <left_node>1</left_node> + <right_val>0.6328445076942444</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 14 4 3 -1.</_> + <_>8 15 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0151911703869700</threshold> + <left_val>0.4936082065105438</left_val> + <right_val>0.7426524758338928</right_val></_></_> + <_> + <!-- tree 56 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 12 5 3 -1.</_> + <_>3 13 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0123003898188472</threshold> + <left_val>0.1389349997043610</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 3 3 4 -1.</_> + <_>7 3 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5168030513450503e-003</threshold> + <left_val>0.5091962218284607</left_val> + <right_val>0.3482648134231567</right_val></_></_> + <_> + <!-- tree 57 --> + <_> + <!-- 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>9.5754547510296106e-004</threshold> + <left_node>1</left_node> + <right_val>0.6036316752433777</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 8 12 1 -1.</_> + <_>9 8 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0189622007310390</threshold> + <left_val>0.2319173067808151</left_val> + <right_val>0.5116652846336365</right_val></_></_> + <_> + <!-- tree 58 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 4 4 8 -1.</_> + <_>10 4 2 8 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0222722608596087</threshold> + <left_val>0.6555022001266480</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 6 8 5 -1.</_> + <_>10 6 4 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0251452308148146</threshold> + <left_val>0.1326071023941040</left_val> + <right_val>0.4674034118652344</right_val></_></_> + <_> + <!-- tree 59 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 4 6 4 -1.</_> + <_>12 4 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0195339005440474</threshold> + <left_val>0.5182027220726013</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 7 2 2 -1.</_> + <_>13 7 1 1 2.</_> + <_>12 8 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1231349781155586e-003</threshold> + <left_val>0.6318243145942688</left_val> + <right_val>0.4825519025325775</right_val></_></_> + <_> + <!-- tree 60 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 5 10 8 -1.</_> + <_>3 9 10 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4861139934509993e-003</threshold> + <left_val>0.2918671071529388</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 1 2 12 -1.</_> + <_>7 7 2 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.5002888762392104e-004</threshold> + <left_val>0.5621371269226074</left_val> + <right_val>0.4249213039875031</right_val></_></_> + <_> + <!-- tree 61 --> + <_> + <!-- 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.1231349781155586e-003</threshold> + <left_node>1</left_node> + <right_val>0.4813745021820068</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 13 1 6 -1.</_> + <_>11 16 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0104097397997975</threshold> + <left_val>0.5184006094932556</left_val> + <right_val>0.2051223069429398</right_val></_></_> + <_> + <!-- tree 62 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 1 6 15 -1.</_> + <_>7 1 2 15 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0878325626254082</threshold> + <left_val>0.1179921999573708</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 7 2 2 -1.</_> + <_>6 7 1 1 2.</_> + <_>7 8 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6584879485890269e-003</threshold> + <left_val>0.4987811148166657</left_val> + <right_val>0.6973755955696106</right_val></_></_> + <_> + <!-- tree 63 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>17 5 2 2 -1.</_> + <_>17 6 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.3008750285953283e-003</threshold> + <left_node>1</left_node> + <right_val>0.5339831113815308</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 3 4 10 -1.</_> + <_>12 3 2 5 2.</_> + <_>10 8 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0330261699855328</threshold> + <left_val>0.5033289194107056</left_val> + <right_val>0.6851906776428223</right_val></_></_> + <_> + <!-- tree 64 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 5 2 2 -1.</_> + <_>1 6 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.3585069682449102e-003</threshold> + <left_val>0.3002822101116180</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 10 2 2 -1.</_> + <_>7 10 1 1 2.</_> + <_>8 11 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.8067491995170712e-004</threshold> + <left_val>0.4593083858489990</left_val> + <right_val>0.6440045237541199</right_val></_></_> + <_> + <!-- tree 65 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 12 14 4 -1.</_> + <_>10 12 7 2 2.</_> + <_>3 14 7 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0180257596075535</threshold> + <left_node>1</left_node> + <right_val>0.5311291217803955</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 15 3 2 -1.</_> + <_>9 16 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2354910140857100e-003</threshold> + <left_val>0.4729106128215790</left_val> + <right_val>0.5721461176872253</right_val></_></_> + <_> + <!-- tree 66 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 13 3 3 -1.</_> + <_>1 14 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.2583027435466647e-004</threshold> + <left_val>0.3662332892417908</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 3 1 2 -1.</_> + <_>0 4 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.0123997759073973e-004</threshold> + <left_val>0.5361989736557007</left_val> + <right_val>0.3008632957935333</right_val></_></_></trees> + <stage_threshold>32.6471290588378910</stage_threshold> + <parent>10</parent> + <next>-1</next></_> + <_> + <!-- stage 12 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 7 6 1 -1.</_> + <_>9 7 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.4914839304983616e-003</threshold> + <left_val>0.3422389030456543</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 4 16 6 -1.</_> + <_>0 6 16 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0504885986447334</threshold> + <left_val>0.7703458070755005</left_val> + <right_val>0.4516390860080719</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 3 2 14 -1.</_> + <_>9 10 2 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.7838351717218757e-004</threshold> + <left_node>1</left_node> + <right_val>0.3256342113018036</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 0 4 3 -1.</_> + <_>12 0 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.3572890495415777e-004</threshold> + <left_val>0.3406555950641632</left_val> + <right_val>0.5897027254104614</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 18 12 2 -1.</_> + <_>8 18 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.5575071126222610e-003</threshold> + <left_val>0.4306578934192658</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 10 12 4 -1.</_> + <_>8 10 4 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.1241987645626068e-003</threshold> + <left_val>0.7149587273597717</left_val> + <right_val>0.4345684945583344</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 9 2 2 -1.</_> + <_>9 10 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.4612158671952784e-004</threshold> + <left_val>0.3295974135398865</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>14 1 2 8 -1.</_> + <_>15 1 1 4 2.</_> + <_>14 5 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.8972938889637589e-004</threshold> + <left_val>0.5845620036125183</left_val> + <right_val>0.3526687920093536</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 4 9 1 -1.</_> + <_>6 4 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.1604831646254752e-006</threshold> + <left_val>0.4081954956054688</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 3 4 2 -1.</_> + <_>3 4 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.8497708737850189e-004</threshold> + <left_val>0.4203113019466400</left_val> + <right_val>0.6634126901626587</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 15 2 4 -1.</_> + <_>11 17 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.9489860278554261e-004</threshold> + <left_val>0.3942466974258423</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>14 13 2 6 -1.</_> + <_>14 15 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0170838497579098</threshold> + <left_val>0.2294072061777115</left_val> + <right_val>0.5238960981369019</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 6 1 6 -1.</_> + <_>6 9 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.3513697609305382e-004</threshold> + <left_val>0.3026031851768494</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 10 8 8 -1.</_> + <_>6 14 8 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.5499608647078276e-004</threshold> + <left_val>0.6032196283340454</left_val> + <right_val>0.3412458896636963</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 13 4 3 -1.</_> + <_>8 14 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.0216713249683380e-003</threshold> + <left_node>1</left_node> + <right_val>0.7306240797042847</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 11 4 8 -1.</_> + <_>10 15 4 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0389305092394352</threshold> + <left_val>0.3599325120449066</left_val> + <right_val>0.5234380960464478</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 11 6 1 -1.</_> + <_>7 11 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.0348767621908337e-005</threshold> + <left_node>1</left_node> + <right_val>0.3493758141994476</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 4 6 10 -1.</_> + <_>8 4 3 10 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.5350573062896729e-003</threshold> + <left_val>0.2746109068393707</left_val> + <right_val>0.5626586079597473</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 2 6 3 -1.</_> + <_>14 3 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0108544500544667</threshold> + <left_val>0.5282226204872131</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 12 3 2 -1.</_> + <_>9 13 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.5329501153901219e-004</threshold> + <left_val>0.4522049129009247</left_val> + <right_val>0.6054301857948303</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 1 4 6 -1.</_> + <_>8 3 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.8117150466423482e-004</threshold> + <left_val>0.3306862115859985</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 5 13 8 -1.</_> + <_>3 9 13 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.6641560038551688e-004</threshold> + <left_val>0.1455000042915344</left_val> + <right_val>0.5384927988052368</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 5 5 3 -1.</_> + <_>12 6 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.4854792803525925e-003</threshold> + <left_node>1</left_node> + <right_val>0.4814155995845795</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 14 15 6 -1.</_> + <_>5 16 15 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0189343094825745</threshold> + <left_val>0.3563741147518158</left_val> + <right_val>0.5405145287513733</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 5 5 3 -1.</_> + <_>3 6 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.9814549274742603e-003</threshold> + <left_node>1</left_node> + <right_val>0.6957743167877197</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 14 2 6 -1.</_> + <_>9 14 1 3 2.</_> + <_>10 17 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.4286780282855034e-003</threshold> + <left_val>0.5050892829895020</left_val> + <right_val>0.2316994965076447</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 12 3 2 -1.</_> + <_>9 13 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.4203791185282171e-004</threshold> + <left_node>1</left_node> + <right_val>0.6018581986427307</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 13 3 2 -1.</_> + <_>9 14 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.3822550429031253e-004</threshold> + <left_val>0.4755082130432129</left_val> + <right_val>0.5585237741470337</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 6 3 -1.</_> + <_>0 3 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.4261639490723610e-003</threshold> + <left_val>0.2282465994358063</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 1 9 11 -1.</_> + <_>3 1 3 11 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.9637769162654877e-003</threshold> + <left_val>0.4040588140487671</left_val> + <right_val>0.5650169849395752</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- 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.0136540504172444</threshold> + <left_val>0.5267739295959473</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 13 6 3 -1.</_> + <_>7 14 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.9892877042293549e-003</threshold> + <left_val>0.6794049739837647</left_val> + <right_val>0.4797033965587616</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 12 14 4 -1.</_> + <_>3 12 7 2 2.</_> + <_>10 14 7 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0365586318075657</threshold> + <left_node>1</left_node> + <right_val>0.0884257331490517</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 14 1 4 -1.</_> + <_>7 16 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.8999379941960797e-005</threshold> + <left_val>0.4020788073539734</left_val> + <right_val>0.5457332134246826</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- 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.0136540504172444</threshold> + <left_val>0.5267612934112549</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 14 1 3 -1.</_> + <_>10 15 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.8802779959514737e-003</threshold> + <left_val>0.4806052148342133</left_val> + <right_val>0.6394364833831787</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 13 4 6 -1.</_> + <_>8 13 2 3 2.</_> + <_>10 16 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0136540504172444</threshold> + <left_val>0.1724810004234314</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 14 1 3 -1.</_> + <_>9 15 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2778700329363346e-003</threshold> + <left_val>0.4479824006557465</left_val> + <right_val>0.6310008764266968</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 15 2 3 -1.</_> + <_>10 16 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.8843395244330168e-004</threshold> + <left_node>1</left_node> + <right_val>0.5948169231414795</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 16 1 2 -1.</_> + <_>11 17 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4511500012304168e-005</threshold> + <left_val>0.4854174852371216</left_val> + <right_val>0.5309361219406128</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 0 2 2 -1.</_> + <_>9 1 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.2775429533794522e-004</threshold> + <left_val>0.3183631896972656</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 1 5 8 -1.</_> + <_>0 5 5 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0147537402808666</threshold> + <left_val>0.3084976077079773</left_val> + <right_val>0.5352026224136353</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 14 2 3 -1.</_> + <_>10 15 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.4148250706493855e-003</threshold> + <left_val>0.6115326881408691</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 13 2 3 -1.</_> + <_>10 14 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.5806681998074055e-003</threshold> + <left_val>0.4951646029949188</left_val> + <right_val>0.7061331272125244</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 3 16 6 -1.</_> + <_>0 6 16 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.7734688743948936e-003</threshold> + <left_node>1</left_node> + <right_val>0.3754220902919769</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 1 2 2 -1.</_> + <_>5 1 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.4033669079653919e-005</threshold> + <left_val>0.4115517139434815</left_val> + <right_val>0.5889444947242737</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 2 3 -1.</_> + <_>9 8 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.2278084009885788e-003</threshold> + <left_val>0.0956105664372444</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 8 2 12 -1.</_> + <_>10 12 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.3380909375846386e-003</threshold> + <left_val>0.5300508737564087</left_val> + <right_val>0.3961898088455200</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 2 2 -1.</_> + <_>10 7 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.7049109339714050e-003</threshold> + <left_val>0.6481869220733643</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 0 6 8 -1.</_> + <_>7 0 2 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.7341338619589806e-003</threshold> + <left_val>0.5110440254211426</left_val> + <right_val>0.3121519088745117</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 3 6 -1.</_> + <_>10 7 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0108866095542908</threshold> + <left_val>0.4801428914070129</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 12 10 8 -1.</_> + <_>8 16 10 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0110386600717902</threshold> + <left_val>0.5429710149765015</left_val> + <right_val>0.4162363111972809</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 3 6 -1.</_> + <_>9 7 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0100541999563575</threshold> + <left_val>0.7329335212707520</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 7 12 2 -1.</_> + <_>10 7 6 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.7072880230844021e-003</threshold> + <left_val>0.5356872081756592</left_val> + <right_val>0.3455547094345093</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 6 8 3 -1.</_> + <_>8 6 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.8278098003938794e-004</threshold> + <left_val>0.3655022084712982</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>16 15 3 3 -1.</_> + <_>16 16 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.5739220436662436e-003</threshold> + <left_val>0.3776760101318359</left_val> + <right_val>0.5391774773597717</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 6 12 3 -1.</_> + <_>10 6 6 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.0167761296033859e-003</threshold> + <left_val>0.4039304852485657</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 8 3 5 -1.</_> + <_>8 8 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.7727289814502001e-003</threshold> + <left_val>0.6950443983078003</left_val> + <right_val>0.4981116950511932</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 10 20 2 -1.</_> + <_>10 10 10 1 2.</_> + <_>0 11 10 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0163182895630598</threshold> + <left_node>1</left_node> + <right_val>0.5296732783317566</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 16 9 4 -1.</_> + <_>14 16 3 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0116630000993609</threshold> + <left_val>0.5842639803886414</left_val> + <right_val>0.4789502918720245</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 5 3 4 -1.</_> + <_>1 5 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.5881489273160696e-003</threshold> + <left_node>1</left_node> + <right_val>0.6092178821563721</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 15 4 2 -1.</_> + <_>8 15 2 1 2.</_> + <_>10 16 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.7328999023884535e-003</threshold> + <left_val>0.6721742749214172</left_val> + <right_val>0.4066894054412842</right_val></_></_> + <_> + <!-- tree 31 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 8 19 3 -1.</_> + <_>1 9 19 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4355930034071207e-003</threshold> + <left_val>0.3585087954998016</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>15 16 3 3 -1.</_> + <_>15 17 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.8340899841859937e-003</threshold> + <left_val>0.5371158123016357</left_val> + <right_val>0.4033507108688355</right_val></_></_> + <_> + <!-- tree 32 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 4 20 10 -1.</_> + <_>0 4 10 5 2.</_> + <_>10 9 10 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.1228028982877731</threshold> + <left_node>1</left_node> + <right_val>0.1547572016716003</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 14 7 6 -1.</_> + <_>2 16 7 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0502287000417709</threshold> + <left_val>0.5433843731880188</left_val> + <right_val>0.0842926725745201</right_val></_></_> + <_> + <!-- tree 33 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 6 6 6 -1.</_> + <_>10 6 2 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0214370004832745</threshold> + <left_node>1</left_node> + <right_val>0.4860053956508637</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>16 4 4 6 -1.</_> + <_>16 6 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0310096200555563</threshold> + <left_val>0.1833010017871857</left_val> + <right_val>0.5207554101943970</right_val></_></_> + <_> + <!-- tree 34 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 13 6 3 -1.</_> + <_>7 14 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0129737202078104</threshold> + <left_val>0.7048240900039673</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 13 4 3 -1.</_> + <_>7 14 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5818020328879356e-003</threshold> + <left_val>0.4170587062835693</left_val> + <right_val>0.5865163803100586</right_val></_></_> + <_> + <!-- tree 35 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 13 6 2 -1.</_> + <_>13 14 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.7806248813867569e-003</threshold> + <left_node>1</left_node> + <right_val>0.5307918190956116</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>14 12 2 3 -1.</_> + <_>14 13 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1735740117728710e-003</threshold> + <left_val>0.5522453188896179</left_val> + <right_val>0.3507165014743805</right_val></_></_> + <_> + <!-- tree 36 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 13 6 2 -1.</_> + <_>1 14 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4651629608124495e-003</threshold> + <left_node>1</left_node> + <right_val>0.3042651116847992</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 12 2 3 -1.</_> + <_>4 13 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.3532148916274309e-003</threshold> + <left_val>0.5339323282241821</left_val> + <right_val>0.2806236147880554</right_val></_></_> + <_> + <!-- tree 37 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>17 4 3 5 -1.</_> + <_>18 4 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.1809681355953217e-003</threshold> + <left_val>0.6410133242607117</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 5 14 8 -1.</_> + <_>12 5 7 4 2.</_> + <_>5 9 7 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.5688649192452431e-004</threshold> + <left_val>0.5620871186256409</left_val> + <right_val>0.4390318989753723</right_val></_></_> + <_> + <!-- tree 38 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 8 6 5 -1.</_> + <_>8 8 2 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0262280106544495</threshold> + <left_node>1</left_node> + <right_val>0.6445556879043579</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 4 4 6 -1.</_> + <_>0 6 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0179581101983786</threshold> + <left_val>0.2002713978290558</left_val> + <right_val>0.4624665081501007</right_val></_></_> + <_> + <!-- tree 39 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 1 3 6 -1.</_> + <_>10 1 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.6468721963465214e-003</threshold> + <left_node>1</left_node> + <right_val>0.5263200998306274</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 4 6 3 -1.</_> + <_>10 5 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.7482809964567423e-003</threshold> + <left_val>0.5873981118202210</left_val> + <right_val>0.4836600124835968</right_val></_></_> + <_> + <!-- tree 40 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 1 3 6 -1.</_> + <_>9 1 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0138518502935767</threshold> + <left_node>1</left_node> + <right_val>0.1566130965948105</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 4 6 3 -1.</_> + <_>4 5 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.6369190309196711e-003</threshold> + <left_val>0.4270178973674774</left_val> + <right_val>0.5806660056114197</right_val></_></_> + <_> + <!-- tree 41 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 4 3 3 -1.</_> + <_>12 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.1513599678874016e-003</threshold> + <left_val>0.6215866208076477</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 11 4 2 -1.</_> + <_>12 12 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4788460248382762e-005</threshold> + <left_val>0.5576642751693726</left_val> + <right_val>0.4122002124786377</right_val></_></_> + <_> + <!-- tree 42 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 20 6 -1.</_> + <_>0 2 10 3 2.</_> + <_>10 5 10 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0736769884824753</threshold> + <left_val>0.1536709964275360</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 4 3 3 -1.</_> + <_>5 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.0912780202925205e-003</threshold> + <left_val>0.6344268918037415</left_val> + <right_val>0.4507412016391754</right_val></_></_> + <_> + <!-- tree 43 --> + <_> + <!-- 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>7.9240966588258743e-003</threshold> + <left_val>0.5457975268363953</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 10 16 6 -1.</_> + <_>11 10 8 3 2.</_> + <_>3 13 8 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.5778040811419487e-003</threshold> + <left_val>0.5401657223701477</left_val> + <right_val>0.3890799880027771</right_val></_></_> + <_> + <!-- tree 44 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 10 16 6 -1.</_> + <_>1 10 8 3 2.</_> + <_>9 13 8 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.5403169244527817e-003</threshold> + <left_node>1</left_node> + <right_val>0.3555611073970795</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 7 2 4 -1.</_> + <_>5 7 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1886510037584230e-004</threshold> + <left_val>0.5836750268936157</left_val> + <right_val>0.4274316132068634</right_val></_></_> + <_> + <!-- tree 45 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 16 9 4 -1.</_> + <_>14 16 3 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0184083692729473</threshold> + <left_val>0.5860440135002136</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 16 14 4 -1.</_> + <_>10 16 7 2 2.</_> + <_>3 18 7 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.3490579333156347e-003</threshold> + <left_val>0.4498957991600037</left_val> + <right_val>0.5498198866844177</right_val></_></_> + <_> + <!-- tree 46 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 16 9 4 -1.</_> + <_>3 16 3 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.6157399453222752e-003</threshold> + <left_node>1</left_node> + <right_val>0.4100992977619171</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 14 6 6 -1.</_> + <_>1 14 3 3 2.</_> + <_>4 17 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.3190969843417406e-003</threshold> + <left_val>0.6701378822326660</left_val> + <right_val>0.4353001117706299</right_val></_></_> + <_> + <!-- tree 47 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 0 2 1 -1.</_> + <_>9 0 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.4642979092895985e-004</threshold> + <left_node>1</left_node> + <right_val>0.5391176939010620</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 7 8 10 -1.</_> + <_>10 7 4 5 2.</_> + <_>6 12 4 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.7858550250530243e-003</threshold> + <left_val>0.5504050254821777</left_val> + <right_val>0.3990935087203980</right_val></_></_> + <_> + <!-- tree 48 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 15 1 2 -1.</_> + <_>2 16 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6395459533669055e-004</threshold> + <left_node>1</left_node> + <right_val>0.3592933118343353</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 14 7 6 -1.</_> + <_>0 16 7 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.3508940357714891e-003</threshold> + <left_val>0.4034172892570496</left_val> + <right_val>0.5806077122688294</right_val></_></_> + <_> + <!-- tree 49 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 8 6 2 -1.</_> + <_>7 9 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.5449963333085179e-005</threshold> + <left_node>1</left_node> + <right_val>0.5412384867668152</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 2 2 15 -1.</_> + <_>9 7 2 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0270184893161058</threshold> + <left_val>0.4944922924041748</left_val> + <right_val>0.5589436292648315</right_val></_></_> + <_> + <!-- tree 50 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 6 2 2 -1.</_> + <_>5 7 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.4561208495870233e-004</threshold> + <left_node>1</left_node> + <right_val>0.5809218287467957</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 6 8 3 -1.</_> + <_>6 7 8 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1687109945341945e-003</threshold> + <left_val>0.4746957123279572</left_val> + <right_val>0.2845895886421204</right_val></_></_> + <_> + <!-- tree 51 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 13 5 6 -1.</_> + <_>12 15 5 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0228975005447865</threshold> + <left_node>1</left_node> + <right_val>0.2414411008358002</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 0 20 18 -1.</_> + <_>0 9 20 9 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.7087926268577576</threshold> + <left_val>0.5195764899253845</left_val> + <right_val>0.1030092015862465</right_val></_></_> + <_> + <!-- tree 52 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 1 6 6 -1.</_> + <_>7 1 2 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0374838300049305</threshold> + <left_node>1</left_node> + <right_val>0.1814638972282410</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 1 4 9 -1.</_> + <_>7 1 2 9 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2827500468119979e-003</threshold> + <left_val>0.4246071875095367</left_val> + <right_val>0.5707973241806030</right_val></_></_> + <_> + <!-- tree 53 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 19 18 1 -1.</_> + <_>7 19 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.1718312315642834e-003</threshold> + <left_val>0.6143323183059692</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>14 16 5 2 -1.</_> + <_>14 17 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.7545939665287733e-003</threshold> + <left_val>0.5205671191215515</left_val> + <right_val>0.4220441877841950</right_val></_></_> + <_> + <!-- tree 54 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 5 15 10 -1.</_> + <_>0 10 15 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.6072919610887766e-003</threshold> + <left_val>0.3182592093944550</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 15 4 2 -1.</_> + <_>7 15 2 1 2.</_> + <_>9 16 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.5258748792111874e-004</threshold> + <left_val>0.5710468292236328</left_val> + <right_val>0.4226093888282776</right_val></_></_> + <_> + <!-- tree 55 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 11 2 2 -1.</_> + <_>14 12 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.0514748804271221e-003</threshold> + <left_node>1</left_node> + <right_val>0.5162829756736755</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 8 3 3 -1.</_> + <_>9 9 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.4323761723935604e-003</threshold> + <left_val>0.2666288912296295</left_val> + <right_val>0.5214679837226868</right_val></_></_> + <_> + <!-- tree 56 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 11 2 2 -1.</_> + <_>4 12 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4652940080850385e-005</threshold> + <left_node>1</left_node> + <right_val>0.3981761038303375</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 8 3 3 -1.</_> + <_>8 9 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.8556920113041997e-003</threshold> + <left_val>0.3322763144969940</left_val> + <right_val>0.5705834031105042</right_val></_></_> + <_> + <!-- tree 57 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 10 2 3 -1.</_> + <_>9 11 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.7609540633857250e-003</threshold> + <left_node>1</left_node> + <right_val>0.6636558175086975</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 8 4 3 -1.</_> + <_>8 9 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5676260227337480e-003</threshold> + <left_val>0.5505567789077759</left_val> + <right_val>0.4420661926269531</right_val></_></_> + <_> + <!-- tree 58 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 9 4 10 -1.</_> + <_>1 9 2 5 2.</_> + <_>3 14 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.4239919409155846e-003</threshold> + <left_node>1</left_node> + <right_val>0.5959938168525696</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 12 6 8 -1.</_> + <_>2 12 2 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.4692399464547634e-003</threshold> + <left_val>0.5369594097137451</left_val> + <right_val>0.3744339942932129</right_val></_></_> + <_> + <!-- tree 59 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 1 4 2 -1.</_> + <_>11 1 2 1 2.</_> + <_>9 2 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.8038539504632354e-004</threshold> + <left_val>0.4103595018386841</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 13 7 6 -1.</_> + <_>12 15 7 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0450864508748055</threshold> + <left_val>0.5177506804466248</left_val> + <right_val>0.1878100037574768</right_val></_></_> + <_> + <!-- tree 60 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 0 2 3 -1.</_> + <_>7 1 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.1405387930572033e-003</threshold> + <left_val>0.2352892011404038</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 14 6 3 -1.</_> + <_>9 14 2 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0212361291050911</threshold> + <left_val>0.1708751022815704</left_val> + <right_val>0.5424973964691162</right_val></_></_> + <_> + <!-- tree 61 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 6 6 4 -1.</_> + <_>11 6 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.3763340432196856e-003</threshold> + <left_val>0.5836530923843384</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 10 8 3 -1.</_> + <_>8 10 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0541225895285606</threshold> + <left_val>0.5117433071136475</left_val> + <right_val>0.1865931004285812</right_val></_></_> + <_> + <!-- tree 62 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 10 4 3 -1.</_> + <_>8 10 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.3492980077862740e-004</threshold> + <left_val>0.5108693242073059</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 8 3 5 -1.</_> + <_>7 8 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.8454048121348023e-004</threshold> + <left_val>0.4775491058826447</left_val> + <right_val>0.2439853996038437</right_val></_></_></trees> + <stage_threshold>30.6721305847167970</stage_threshold> + <parent>11</parent> + <next>-1</next></_> + <_> + <!-- stage 13 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 4 8 1 -1.</_> + <_>4 4 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.0031939968466759e-003</threshold> + <left_val>0.3349649906158447</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 2 2 6 -1.</_> + <_>8 2 1 3 2.</_> + <_>9 5 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.9161207647994161e-004</threshold> + <left_val>0.4518367946147919</left_val> + <right_val>0.7289354205131531</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 7 20 6 -1.</_> + <_>0 9 20 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0112127903848886</threshold> + <left_val>0.2950800955295563</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 10 3 6 -1.</_> + <_>12 13 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.6108198845759034e-004</threshold> + <left_val>0.5669054985046387</left_val> + <right_val>0.2830851078033447</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 15 1 4 -1.</_> + <_>8 17 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1984579759882763e-004</threshold> + <left_val>0.4090577960014343</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 16 2 4 -1.</_> + <_>5 18 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.9725349557120353e-004</threshold> + <left_val>0.6951494216918945</left_val> + <right_val>0.4637868106365204</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 2 8 12 -1.</_> + <_>6 6 8 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.5180420167744160e-003</threshold> + <left_node>1</left_node> + <right_val>0.3167675137519836</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 7 12 2 -1.</_> + <_>8 7 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2148249661549926e-003</threshold> + <left_val>0.3316706120967865</left_val> + <right_val>0.5396397709846497</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 0 6 1 -1.</_> + <_>9 0 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.2497441172599792e-003</threshold> + <left_val>0.2600573897361755</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 11 3 3 -1.</_> + <_>8 12 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.4915721565485001e-003</threshold> + <left_val>0.7484294772148132</left_val> + <right_val>0.5073192119598389</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 11 3 6 -1.</_> + <_>12 14 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.5378600265830755e-004</threshold> + <left_node>1</left_node> + <right_val>0.3952010869979858</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 2 6 10 -1.</_> + <_>14 2 3 5 2.</_> + <_>11 7 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.9741100519895554e-004</threshold> + <left_val>0.5880274772644043</left_val> + <right_val>0.3552120029926300</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 10 12 -1.</_> + <_>5 7 5 6 2.</_> + <_>10 13 5 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0430792495608330</threshold> + <left_val>0.2434878051280975</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 4 2 10 -1.</_> + <_>4 9 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.1999092102050781e-004</threshold> + <left_val>0.3195562958717346</left_val> + <right_val>0.5585454702377319</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 2 3 -1.</_> + <_>9 7 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.5451628975570202e-003</threshold> + <left_node>1</left_node> + <right_val>0.4845289885997772</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 9 6 2 -1.</_> + <_>11 9 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.9610403627157211e-003</threshold> + <left_val>0.3801181018352509</left_val> + <right_val>0.5358511805534363</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 7 2 2 -1.</_> + <_>5 7 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.1919340835884213e-004</threshold> + <left_node>1</left_node> + <right_val>0.4356329143047333</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 2 4 6 -1.</_> + <_>0 4 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0192238893359900</threshold> + <left_val>0.2613066136837006</left_val> + <right_val>0.6155496239662170</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 7 3 4 -1.</_> + <_>11 7 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.3076990144327283e-003</threshold> + <left_val>0.5942062139511108</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 7 3 5 -1.</_> + <_>10 7 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0198250394314528</threshold> + <left_val>0.4945428073406220</left_val> + <right_val>0.7384855151176453</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 1 1 3 -1.</_> + <_>9 2 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.2013280540704727e-003</threshold> + <left_val>0.2214481979608536</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 6 16 6 -1.</_> + <_>0 6 8 3 2.</_> + <_>8 9 8 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.8596705570816994e-003</threshold> + <left_val>0.3600977063179016</left_val> + <right_val>0.5298550128936768</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 15 3 3 -1.</_> + <_>10 16 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4142199652269483e-003</threshold> + <left_node>1</left_node> + <right_val>0.5776566267013550</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 14 4 3 -1.</_> + <_>9 15 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0112327598035336</threshold> + <left_val>0.6934456825256348</left_val> + <right_val>0.4827207028865814</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- 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>2.9746301006525755e-003</threshold> + <left_node>1</left_node> + <right_val>0.3216677010059357</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 0 14 2 -1.</_> + <_>3 1 14 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.3283828310668468e-004</threshold> + <left_val>0.3962500095367432</left_val> + <right_val>0.5680363774299622</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 14 3 3 -1.</_> + <_>9 15 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0101052597165108</threshold> + <left_node>1</left_node> + <right_val>0.7567418217658997</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 15 3 3 -1.</_> + <_>10 16 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0116536999121308</threshold> + <left_val>0.6523556709289551</left_val> + <right_val>0.5027053952217102</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 13 2 6 -1.</_> + <_>9 16 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.0609981194138527e-003</threshold> + <left_val>0.2538770139217377</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 13 6 3 -1.</_> + <_>7 14 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.2343141026794910e-003</threshold> + <left_val>0.4387277066707611</left_val> + <right_val>0.6177632212638855</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 11 3 6 -1.</_> + <_>12 14 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0298022795468569</threshold> + <left_node>1</left_node> + <right_val>0.5201140046119690</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 12 5 2 -1.</_> + <_>8 13 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1611840454861522e-003</threshold> + <left_val>0.4647909998893738</left_val> + <right_val>0.6184254884719849</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 11 3 6 -1.</_> + <_>5 14 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.4824447296559811e-004</threshold> + <left_node>1</left_node> + <right_val>0.3040994107723236</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 12 3 2 -1.</_> + <_>8 13 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.1284630424343050e-004</threshold> + <left_val>0.4518808126449585</left_val> + <right_val>0.6245782971382141</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 13 7 6 -1.</_> + <_>11 15 7 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0312035400420427</threshold> + <left_val>0.2788935899734497</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 14 6 3 -1.</_> + <_>7 15 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.7652881108224392e-003</threshold> + <left_val>0.4698500037193298</left_val> + <right_val>0.6502454280853272</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 13 14 4 -1.</_> + <_>3 13 7 2 2.</_> + <_>10 15 7 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0256447792053223</threshold> + <left_node>1</left_node> + <right_val>0.1805171072483063</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 14 4 6 -1.</_> + <_>8 14 2 3 2.</_> + <_>10 17 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.5331530533730984e-003</threshold> + <left_val>0.3208068907260895</left_val> + <right_val>0.5522022843360901</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 15 4 3 -1.</_> + <_>8 16 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.2047149725258350e-003</threshold> + <left_node>1</left_node> + <right_val>0.6436933875083923</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 16 6 2 -1.</_> + <_>9 16 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.4282479716930538e-004</threshold> + <left_val>0.5676705241203308</left_val> + <right_val>0.4509103894233704</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 7 6 2 -1.</_> + <_>7 8 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.1979342717677355e-004</threshold> + <left_val>0.3122146129608154</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 9 13 3 -1.</_> + <_>3 10 13 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.0101029016077518e-004</threshold> + <left_val>0.2965193986892700</left_val> + <right_val>0.5230494737625122</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 8 3 4 -1.</_> + <_>9 10 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.1816839994862676e-004</threshold> + <left_node>1</left_node> + <right_val>0.5464711785316467</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 10 4 3 -1.</_> + <_>8 11 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2239529751241207e-003</threshold> + <left_val>0.4618502855300903</left_val> + <right_val>0.5679548978805542</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 7 3 4 -1.</_> + <_>8 7 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.8743730662390590e-004</threshold> + <left_val>0.5430880188941956</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 7 3 5 -1.</_> + <_>9 7 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.8252469599246979e-003</threshold> + <left_val>0.5433623194694519</left_val> + <right_val>0.3385221064090729</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 3 3 4 -1.</_> + <_>13 3 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.4570789001882076e-003</threshold> + <left_node>1</left_node> + <right_val>0.5265594720840454</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 7 2 3 -1.</_> + <_>9 7 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.3775748237967491e-003</threshold> + <left_val>0.4857215881347656</left_val> + <right_val>0.6815124154090881</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 3 3 4 -1.</_> + <_>6 3 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.7602309603244066e-003</threshold> + <left_node>1</left_node> + <right_val>0.2832160890102387</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 7 12 1 -1.</_> + <_>7 7 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.7752222316339612e-004</threshold> + <left_val>0.3966830968856812</left_val> + <right_val>0.5512480735778809</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 5 3 3 -1.</_> + <_>12 6 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.5084479972720146e-003</threshold> + <left_node>1</left_node> + <right_val>0.6784620285034180</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 2 6 2 -1.</_> + <_>11 3 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.5949047459289432e-004</threshold> + <left_val>0.3906503021717072</left_val> + <right_val>0.5457202792167664</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 2 14 2 -1.</_> + <_>3 2 7 1 2.</_> + <_>10 3 7 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6352660022675991e-003</threshold> + <left_node>1</left_node> + <right_val>0.3640204071998596</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 1 7 14 -1.</_> + <_>6 8 7 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.2750849418807775e-004</threshold> + <left_val>0.5829724073410034</left_val> + <right_val>0.4194979965686798</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 0 12 5 -1.</_> + <_>8 0 6 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0220676101744175</threshold> + <left_val>0.4606702923774719</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 9 18 1 -1.</_> + <_>7 9 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0192037895321846</threshold> + <left_val>0.3261483013629913</left_val> + <right_val>0.5236080884933472</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 10 5 -1.</_> + <_>5 0 5 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0129981096833944</threshold> + <left_val>0.7022112011909485</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 5 8 15 -1.</_> + <_>2 10 8 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.1332690268754959e-003</threshold> + <left_val>0.2870470881462097</left_val> + <right_val>0.5076476931571960</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 5 3 3 -1.</_> + <_>12 6 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.2937557920813560e-003</threshold> + <left_node>1</left_node> + <right_val>0.4709520936012268</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>13 4 2 3 -1.</_> + <_>13 5 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1857069805264473e-003</threshold> + <left_val>0.4708291888237000</left_val> + <right_val>0.6169841885566711</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 15 4 3 -1.</_> + <_>2 16 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.5750709250569344e-003</threshold> + <left_val>0.3114252984523773</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 6 10 3 -1.</_> + <_>10 6 5 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0451521389186382</threshold> + <left_val>0.1851435005664825</left_val> + <right_val>0.5504814982414246</right_val></_></_> + <_> + <!-- tree 31 --> + <_> + <!-- 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>-2.7783559635281563e-003</threshold> + <left_node>1</left_node> + <right_val>0.4937348067760468</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 4 4 3 -1.</_> + <_>12 5 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.5752480141818523e-003</threshold> + <left_val>0.6152948141098023</left_val> + <right_val>0.4735499918460846</right_val></_></_> + <_> + <!-- tree 32 --> + <_> + <!-- 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>1.1614130344241858e-003</threshold> + <left_node>1</left_node> + <right_val>0.6510571837425232</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 4 4 3 -1.</_> + <_>4 5 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.3350189439952374e-003</threshold> + <left_val>0.4088341891765595</left_val> + <right_val>0.5684152245521545</right_val></_></_> + <_> + <!-- tree 33 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 4 3 3 -1.</_> + <_>12 4 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.8499289657920599e-003</threshold> + <left_node>1</left_node> + <right_val>0.3025828897953033</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 3 2 1 -1.</_> + <_>9 3 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.4529630318284035e-003</threshold> + <left_val>0.5232502818107605</left_val> + <right_val>0.2017620950937271</right_val></_></_> + <_> + <!-- tree 34 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 5 5 3 -1.</_> + <_>4 6 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.6731390282511711e-003</threshold> + <left_node>1</left_node> + <right_val>0.6428425908088684</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 6 4 3 -1.</_> + <_>4 7 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1937100682407618e-003</threshold> + <left_val>0.4328865110874176</left_val> + <right_val>0.6420509815216065</right_val></_></_> + <_> + <!-- tree 35 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 4 3 3 -1.</_> + <_>12 4 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.4666871912777424e-003</threshold> + <left_node>1</left_node> + <right_val>0.5254065990447998</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 8 4 3 -1.</_> + <_>8 9 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.7186251506209373e-003</threshold> + <left_val>0.2490984052419663</left_val> + <right_val>0.5287619233131409</right_val></_></_> + <_> + <!-- tree 36 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 4 3 3 -1.</_> + <_>7 4 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.9941878579556942e-004</threshold> + <left_node>1</left_node> + <right_val>0.3329795897006989</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 14 1 3 -1.</_> + <_>4 15 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.8276498243212700e-004</threshold> + <left_val>0.3598344922065735</left_val> + <right_val>0.5498340725898743</right_val></_></_> + <_> + <!-- tree 37 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 2 3 -1.</_> + <_>9 7 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.3231188319623470e-003</threshold> + <left_val>0.4818705022335053</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>17 0 3 2 -1.</_> + <_>17 1 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.0838290005922318e-003</threshold> + <left_val>0.5266330242156982</left_val> + <right_val>0.3105789124965668</right_val></_></_> + <_> + <!-- tree 38 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 10 2 9 -1.</_> + <_>8 13 2 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.0515898833982646e-004</threshold> + <left_node>1</left_node> + <right_val>0.3995291888713837</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 8 18 2 -1.</_> + <_>0 9 18 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2640280183404684e-003</threshold> + <left_val>0.3228437900543213</left_val> + <right_val>0.5819215178489685</right_val></_></_> + <_> + <!-- tree 39 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 15 2 3 -1.</_> + <_>9 16 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0101526603102684</threshold> + <left_val>0.8026071190834045</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 7 4 3 -1.</_> + <_>8 8 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.6863690000027418e-003</threshold> + <left_val>0.3875617086887360</left_val> + <right_val>0.5466570854187012</right_val></_></_> + <_> + <!-- tree 40 --> + <_> + <!-- 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>-9.0515613555908203e-003</threshold> + <left_node>1</left_node> + <right_val>0.4372057914733887</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 18 6 2 -1.</_> + <_>0 19 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.3204211182892323e-003</threshold> + <left_val>0.1126551032066345</left_val> + <right_val>0.6395416259765625</right_val></_></_> + <_> + <!-- tree 41 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 9 4 3 -1.</_> + <_>12 9 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.6117300149053335e-003</threshold> + <left_val>0.5423989295959473</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 8 3 8 -1.</_> + <_>10 8 1 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0143390195444226</threshold> + <left_val>0.4979273080825806</left_val> + <right_val>0.6042236089706421</right_val></_></_> + <_> + <!-- tree 42 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 9 4 3 -1.</_> + <_>6 9 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.8452780097723007e-003</threshold> + <left_node>1</left_node> + <right_val>0.3491092026233673</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 18 6 1 -1.</_> + <_>6 18 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4783289771003183e-005</threshold> + <left_val>0.4195067882537842</left_val> + <right_val>0.5775966048240662</right_val></_></_> + <_> + <!-- tree 43 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 3 2 -1.</_> + <_>10 7 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.1814555451273918e-003</threshold> + <left_val>0.4885987043380737</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 7 8 12 -1.</_> + <_>10 7 4 6 2.</_> + <_>6 13 4 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.6321990452706814e-003</threshold> + <left_val>0.5444468259811401</left_val> + <right_val>0.4420995116233826</right_val></_></_> + <_> + <!-- tree 44 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 3 2 -1.</_> + <_>9 7 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.2483461070805788e-003</threshold> + <left_val>0.6699792146682739</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 7 3 6 -1.</_> + <_>9 7 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0123745603486896</threshold> + <left_val>0.4478605985641480</left_val> + <right_val>0.6564893722534180</right_val></_></_> + <_> + <!-- tree 45 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 16 14 4 -1.</_> + <_>10 16 7 2 2.</_> + <_>3 18 7 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.6516688093543053e-003</threshold> + <left_node>1</left_node> + <right_val>0.5511878728866577</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 14 18 4 -1.</_> + <_>10 14 9 2 2.</_> + <_>1 16 9 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.5750613361597061e-003</threshold> + <left_val>0.4017445147037506</left_val> + <right_val>0.5405536293983460</right_val></_></_> + <_> + <!-- tree 46 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 3 3 -1.</_> + <_>8 8 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.5078441984951496e-003</threshold> + <left_node>1</left_node> + <right_val>0.2294393032789230</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 4 20 12 -1.</_> + <_>0 4 10 6 2.</_> + <_>10 10 10 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0286752097308636</threshold> + <left_val>0.5177900195121765</left_val> + <right_val>0.3567756116390228</right_val></_></_> + <_> + <!-- tree 47 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 10 12 -1.</_> + <_>10 5 5 6 2.</_> + <_>5 11 5 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.0673860609531403e-003</threshold> + <left_val>0.5564699769020081</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 2 4 7 -1.</_> + <_>10 2 2 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2367829913273454e-003</threshold> + <left_val>0.3627698123455048</left_val> + <right_val>0.5572413802146912</right_val></_></_> + <_> + <!-- tree 48 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 11 4 3 -1.</_> + <_>8 12 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.4818679131567478e-003</threshold> + <left_node>1</left_node> + <right_val>0.6784911155700684</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 12 3 3 -1.</_> + <_>8 13 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.7109839506447315e-003</threshold> + <left_val>0.4121252894401550</left_val> + <right_val>0.6072235703468323</right_val></_></_> + <_> + <!-- tree 49 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 13 5 6 -1.</_> + <_>13 15 5 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.9405790418386459e-003</threshold> + <left_node>1</left_node> + <right_val>0.5459766983985901</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 0 6 6 -1.</_> + <_>9 0 2 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0333020985126495</threshold> + <left_val>0.5276706814765930</left_val> + <right_val>0.2374915927648544</right_val></_></_> + <_> + <!-- tree 50 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 13 5 6 -1.</_> + <_>2 15 5 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0361046306788921</threshold> + <left_node>1</left_node> + <right_val>0.0724927932024002</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 4 2 12 -1.</_> + <_>0 4 1 6 2.</_> + <_>1 10 1 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0196746494621038</threshold> + <left_val>0.4626345932483673</left_val> + <right_val>0.8208963274955750</right_val></_></_> + <_> + <!-- tree 51 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 19 3 1 -1.</_> + <_>10 19 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.4766150638461113e-003</threshold> + <left_val>0.5208731889724731</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>18 0 2 6 -1.</_> + <_>18 2 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3987369602546096e-003</threshold> + <left_val>0.5484414100646973</left_val> + <right_val>0.4230034947395325</right_val></_></_> + <_> + <!-- tree 52 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 3 1 6 -1.</_> + <_>0 5 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.0974249131977558e-003</threshold> + <left_node>1</left_node> + <right_val>0.2780553102493286</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 0 3 6 -1.</_> + <_>0 2 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.6973790954798460e-003</threshold> + <left_val>0.5403831005096436</left_val> + <right_val>0.3790988922119141</right_val></_></_> + <_> + <!-- tree 53 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>17 2 3 7 -1.</_> + <_>18 2 1 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.6591699831187725e-003</threshold> + <left_node>1</left_node> + <right_val>0.4798336029052734</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 3 4 7 -1.</_> + <_>10 3 2 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.9460969856008887e-004</threshold> + <left_val>0.3766950070858002</left_val> + <right_val>0.5429229140281677</right_val></_></_> + <_> + <!-- tree 54 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 3 7 -1.</_> + <_>1 2 1 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1750570740550756e-003</threshold> + <left_node>1</left_node> + <right_val>0.6207162737846375</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 2 4 8 -1.</_> + <_>8 2 2 8 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4614439569413662e-003</threshold> + <left_val>0.3357945084571838</left_val> + <right_val>0.5142632126808167</right_val></_></_> + <_> + <!-- tree 55 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 0 1 4 -1.</_> + <_>13 2 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.3006567759439349e-004</threshold> + <left_node>1</left_node> + <right_val>0.5344640016555786</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 1 12 5 -1.</_> + <_>9 1 4 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.1486930996179581</threshold> + <left_val>0.5159608125686646</left_val> + <right_val>0.2561823129653931</right_val></_></_> + <_> + <!-- tree 56 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 0 1 4 -1.</_> + <_>6 2 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.8816498494707048e-005</threshold> + <left_node>1</left_node> + <right_val>0.5123091936111450</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 1 12 5 -1.</_> + <_>7 1 4 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.6275369562208652e-003</threshold> + <left_val>0.6017646193504334</left_val> + <right_val>0.3109371960163117</right_val></_></_> + <_> + <!-- tree 57 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 12 3 8 -1.</_> + <_>10 12 1 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0128818098455668</threshold> + <left_val>0.2712287008762360</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 13 6 1 -1.</_> + <_>9 13 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.4982917653396726e-004</threshold> + <left_val>0.5442442297935486</left_val> + <right_val>0.4028888046741486</right_val></_></_> + <_> + <!-- tree 58 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 14 6 3 -1.</_> + <_>7 15 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0123159997165203</threshold> + <left_node>1</left_node> + <right_val>0.4736065864562988</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 16 7 3 -1.</_> + <_>5 17 7 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.0286601334810257e-003</threshold> + <left_val>0.7451434731483460</left_val> + <right_val>0.3487991988658905</right_val></_></_> + <_> + <!-- tree 59 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 12 20 6 -1.</_> + <_>0 14 20 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0868761166930199</threshold> + <left_val>0.2290333062410355</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 18 14 2 -1.</_> + <_>4 19 14 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5107560102478601e-005</threshold> + <left_val>0.5517889857292175</left_val> + <right_val>0.4393149018287659</right_val></_></_> + <_> + <!-- tree 60 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 12 3 8 -1.</_> + <_>9 12 1 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0174576602876186</threshold> + <left_val>0.0901679024100304</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 13 3 3 -1.</_> + <_>7 14 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.5219470262527466e-003</threshold> + <left_val>0.6233540177345276</left_val> + <right_val>0.4789459109306335</right_val></_></_> + <_> + <!-- tree 61 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 12 10 -1.</_> + <_>11 5 6 5 2.</_> + <_>5 10 6 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0656520025804639e-003</threshold> + <left_val>0.5489696264266968</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 1 5 10 -1.</_> + <_>8 6 5 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.2540300637483597e-003</threshold> + <left_val>0.5579808950424194</left_val> + <right_val>0.4375877976417542</right_val></_></_> + <_> + <!-- tree 62 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 4 9 12 -1.</_> + <_>5 10 9 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.0349102392792702e-003</threshold> + <left_val>0.3579156100749970</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 13 6 6 -1.</_> + <_>7 15 6 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5230999561026692e-003</threshold> + <left_val>0.5613660216331482</left_val> + <right_val>0.3939043879508972</right_val></_></_> + <_> + <!-- tree 63 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 4 5 16 -1.</_> + <_>8 12 5 8 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.8441150207072496e-003</threshold> + <left_node>1</left_node> + <right_val>0.3901554942131043</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 12 4 6 -1.</_> + <_>8 15 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.2824429217725992e-003</threshold> + <left_val>0.4528619050979614</left_val> + <right_val>0.5441343188285828</right_val></_></_> + <_> + <!-- tree 64 --> + <_> + <!-- 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>3.2161718991119415e-005</threshold> + <left_node>1</left_node> + <right_val>0.5803111791610718</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 12 2 2 -1.</_> + <_>7 12 1 1 2.</_> + <_>8 13 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.0118400900391862e-005</threshold> + <left_val>0.3336850106716156</left_val> + <right_val>0.5504856109619141</right_val></_></_> + <_> + <!-- tree 65 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 0 2 14 -1.</_> + <_>18 0 1 14 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.6150099262595177e-003</threshold> + <left_val>0.6124789118766785</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 11 7 2 -1.</_> + <_>12 12 7 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0173892099410295</threshold> + <left_val>0.0872716307640076</left_val> + <right_val>0.5204588174819946</right_val></_></_> + <_> + <!-- tree 66 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 18 1 2 -1.</_> + <_>1 19 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.4361080654198304e-005</threshold> + <left_val>0.3935329020023346</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 18 1 2 -1.</_> + <_>2 19 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0354899859521538e-004</threshold> + <left_val>0.5918853878974915</left_val> + <right_val>0.4119614064693451</right_val></_></_> + <_> + <!-- tree 67 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 2 1 -1.</_> + <_>9 7 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5939630102366209e-003</threshold> + <left_val>0.4839623868465424</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 6 2 3 -1.</_> + <_>9 6 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.5440789759159088e-003</threshold> + <left_val>0.4787364900112152</left_val> + <right_val>0.6360663175582886</right_val></_></_> + <_> + <!-- tree 68 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 1 2 2 -1.</_> + <_>4 1 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5083180187502876e-005</threshold> + <left_val>0.4231117069721222</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 0 3 2 -1.</_> + <_>3 1 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.9282202427275479e-005</threshold> + <left_val>0.4274589121341705</left_val> + <right_val>0.6094048023223877</right_val></_></_> + <_> + <!-- tree 69 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 10 3 4 -1.</_> + <_>12 12 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.5371708003804088e-004</threshold> + <left_node>1</left_node> + <right_val>0.4271987974643707</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 7 8 2 -1.</_> + <_>7 8 8 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.9186759600415826e-003</threshold> + <left_val>0.4497107863426209</left_val> + <right_val>0.5549122095108032</right_val></_></_> + <_> + <!-- tree 70 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 8 3 4 -1.</_> + <_>8 10 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.0764222396537662e-004</threshold> + <left_node>1</left_node> + <right_val>0.5477195978164673</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 12 6 3 -1.</_> + <_>7 13 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7236480489373207e-003</threshold> + <left_val>0.2882922887802124</left_val> + <right_val>0.5615127086639404</right_val></_></_></trees> + <stage_threshold>34.6770782470703120</stage_threshold> + <parent>12</parent> + <next>-1</next></_> + <_> + <!-- stage 14 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 10 3 -1.</_> + <_>5 2 5 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0130921695381403</threshold> + <left_val>0.3338870108127594</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 1 20 6 -1.</_> + <_>0 3 20 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.1446479735895991e-004</threshold> + <left_val>0.3099352121353149</left_val> + <right_val>0.6677492260932922</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 6 6 3 -1.</_> + <_>9 6 2 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0218357294797897</threshold> + <left_val>0.4369049072265625</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 7 14 4 -1.</_> + <_>3 9 14 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0483239404857159</threshold> + <left_val>0.4301724135875702</left_val> + <right_val>0.6153885126113892</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 3 6 -1.</_> + <_>5 9 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6091950237751007e-003</threshold> + <left_val>0.3387326002120972</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 8 3 12 -1.</_> + <_>8 12 3 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3469760306179523e-003</threshold> + <left_val>0.6248713731765747</left_val> + <right_val>0.3594130873680115</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 17 6 2 -1.</_> + <_>12 17 3 1 2.</_> + <_>9 18 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7729059618432075e-004</threshold> + <left_val>0.3868424892425537</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 17 4 3 -1.</_> + <_>10 18 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.6743620876222849e-004</threshold> + <left_val>0.4409345090389252</left_val> + <right_val>0.5476474165916443</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 2 4 2 -1.</_> + <_>4 3 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.2352119665592909e-003</threshold> + <left_val>0.3260171115398407</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 3 6 14 -1.</_> + <_>9 3 2 14 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1705530341714621e-003</threshold> + <left_val>0.4111348986625671</left_val> + <right_val>0.6088163852691650</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 13 1 6 -1.</_> + <_>15 16 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.9695429475395940e-005</threshold> + <left_node>1</left_node> + <right_val>0.4269422888755798</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>13 14 2 6 -1.</_> + <_>13 16 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.7050738572143018e-004</threshold> + <left_val>0.4306466877460480</left_val> + <right_val>0.5810514092445374</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 11 5 6 -1.</_> + <_>4 14 5 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.9626210208516568e-005</threshold> + <left_node>1</left_node> + <right_val>0.3669143021106720</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 17 4 2 -1.</_> + <_>6 17 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.3152441028505564e-004</threshold> + <left_val>0.4610663950443268</left_val> + <right_val>0.6290590167045593</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 6 20 2 -1.</_> + <_>0 6 10 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0523058287799358</threshold> + <left_node>1</left_node> + <right_val>0.5328689813613892</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 5 10 12 -1.</_> + <_>11 5 5 6 2.</_> + <_>6 11 5 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0268804691731930</threshold> + <left_val>0.5213261246681213</left_val> + <right_val>0.3231219947338104</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 0 2 12 -1.</_> + <_>4 0 1 6 2.</_> + <_>5 6 1 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.4203000066336244e-004</threshold> + <left_node>1</left_node> + <right_val>0.3568570017814636</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 1 6 2 -1.</_> + <_>6 1 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.6424639616161585e-003</threshold> + <left_val>0.3440661132335663</left_val> + <right_val>0.5625604987144470</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 7 2 1 -1.</_> + <_>13 7 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.6830288697965443e-004</threshold> + <left_node>1</left_node> + <right_val>0.4561173021793366</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 5 15 6 -1.</_> + <_>5 7 15 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.2649629972875118e-003</threshold> + <left_val>0.5321351885795593</left_val> + <right_val>0.3674154877662659</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 10 18 2 -1.</_> + <_>1 10 9 1 2.</_> + <_>10 11 9 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0156272090971470</threshold> + <left_node>1</left_node> + <right_val>0.2029353976249695</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 6 15 7 -1.</_> + <_>6 6 5 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.1621132045984268</threshold> + <left_val>0.5563033223152161</left_val> + <right_val>0.2618849873542786</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 4 3 -1.</_> + <_>8 15 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.7391691002994776e-003</threshold> + <left_val>0.6062194705009460</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 14 3 3 -1.</_> + <_>9 15 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.0878419745713472e-003</threshold> + <left_val>0.5950763821601868</left_val> + <right_val>0.4545117020606995</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 4 3 -1.</_> + <_>8 15 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.3334210272878408e-003</threshold> + <left_node>1</left_node> + <right_val>0.6435524225234985</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 13 3 2 -1.</_> + <_>8 14 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.5116386394947767e-005</threshold> + <left_val>0.3520734012126923</left_val> + <right_val>0.5179778933525085</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 14 5 3 -1.</_> + <_>15 15 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.4625718407332897e-003</threshold> + <left_val>0.5326688289642334</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 14 20 1 -1.</_> + <_>0 14 10 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0220326893031597</threshold> + <left_val>0.3491981029510498</left_val> + <right_val>0.5429236888885498</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 14 6 3 -1.</_> + <_>0 15 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.3081610500812531e-003</threshold> + <left_val>0.2084023058414459</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 3 4 2 -1.</_> + <_>5 4 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.3259368976578116e-004</threshold> + <left_val>0.3965272009372711</left_val> + <right_val>0.5425453782081604</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 6 20 1 -1.</_> + <_>0 6 10 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0322092287242413</threshold> + <left_node>1</left_node> + <right_val>0.5306411981582642</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 3 10 14 -1.</_> + <_>11 3 5 7 2.</_> + <_>6 10 5 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.0424838708713651e-004</threshold> + <left_val>0.5450385808944702</left_val> + <right_val>0.4256696999073029</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 12 4 2 -1.</_> + <_>8 13 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.2727500181645155e-003</threshold> + <left_node>1</left_node> + <right_val>0.5968611240386963</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 3 8 6 -1.</_> + <_>6 3 4 3 2.</_> + <_>10 6 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.9820008464157581e-003</threshold> + <left_val>0.4758140146732330</left_val> + <right_val>0.3150944113731384</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 7 2 1 -1.</_> + <_>13 7 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.8856618124991655e-004</threshold> + <left_node>1</left_node> + <right_val>0.4847748875617981</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 3 10 14 -1.</_> + <_>11 3 5 7 2.</_> + <_>6 10 5 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.8227191008627415e-004</threshold> + <left_val>0.5426316261291504</left_val> + <right_val>0.4338341057300568</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 2 1 -1.</_> + <_>6 7 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.4473457061685622e-005</threshold> + <left_node>1</left_node> + <right_val>0.4287509918212891</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 3 10 14 -1.</_> + <_>4 3 5 7 2.</_> + <_>9 10 5 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.9148979703895748e-004</threshold> + <left_val>0.6345185041427612</left_val> + <right_val>0.4101851880550385</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 2 2 -1.</_> + <_>9 7 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.6939629353582859e-003</threshold> + <left_node>1</left_node> + <right_val>0.4849104881286621</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 3 20 1 -1.</_> + <_>0 3 10 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0112078497186303</threshold> + <left_val>0.4146336913108826</left_val> + <right_val>0.5471264123916626</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 1 10 3 -1.</_> + <_>2 2 10 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0103374095633626</threshold> + <left_val>0.2877183854579926</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 7 2 2 -1.</_> + <_>10 7 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.6883640568703413e-003</threshold> + <left_val>0.5101901888847351</left_val> + <right_val>0.7216951251029968</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 17 3 2 -1.</_> + <_>10 17 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.8984280545264482e-003</threshold> + <left_node>1</left_node> + <right_val>0.5276182293891907</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 7 3 6 -1.</_> + <_>10 7 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.9986729174852371e-003</threshold> + <left_val>0.6618459820747376</left_val> + <right_val>0.4841631054878235</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 17 3 2 -1.</_> + <_>9 17 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.5043681748211384e-003</threshold> + <left_node>1</left_node> + <right_val>0.1874157935380936</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 7 3 6 -1.</_> + <_>9 7 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0177995301783085</threshold> + <left_val>0.4616934955120087</left_val> + <right_val>0.7088965773582459</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 3 4 6 -1.</_> + <_>16 5 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0184625703841448</threshold> + <left_val>0.3001979887485504</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>15 6 2 12 -1.</_> + <_>16 6 1 6 2.</_> + <_>15 12 1 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4931300029275008e-005</threshold> + <left_val>0.4561808109283447</left_val> + <right_val>0.5610787868499756</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 4 18 10 -1.</_> + <_>1 4 9 5 2.</_> + <_>10 9 9 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0860212296247482</threshold> + <left_val>0.2341700941324234</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 4 2 4 -1.</_> + <_>9 6 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.0818758356617764e-005</threshold> + <left_val>0.5672286152839661</left_val> + <right_val>0.4199964106082916</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 5 3 2 -1.</_> + <_>12 6 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2670679716393352e-003</threshold> + <left_node>1</left_node> + <right_val>0.6207482218742371</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 12 10 4 -1.</_> + <_>5 14 10 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3699879636988044e-003</threshold> + <left_val>0.5394958853721619</left_val> + <right_val>0.3823862969875336</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 3 2 -1.</_> + <_>5 6 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.3162781037390232e-003</threshold> + <left_node>1</left_node> + <right_val>0.7061681151390076</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 6 12 6 -1.</_> + <_>8 6 4 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4532039640471339e-003</threshold> + <left_val>0.3065513074398041</left_val> + <right_val>0.4827373027801514</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 4 6 6 -1.</_> + <_>14 6 6 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0714920610189438</threshold> + <left_node>1</left_node> + <right_val>0.5193122029304504</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>16 0 4 6 -1.</_> + <_>18 0 2 3 2.</_> + <_>16 3 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.9857978913933039e-003</threshold> + <left_val>0.4642435014247894</left_val> + <right_val>0.5807694792747498</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 4 6 6 -1.</_> + <_>0 6 6 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.2516499310731888e-003</threshold> + <left_node>1</left_node> + <right_val>0.2949813902378082</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 0 4 6 -1.</_> + <_>0 0 2 3 2.</_> + <_>2 3 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.7005500160157681e-003</threshold> + <left_val>0.4585886895656586</left_val> + <right_val>0.6022353768348694</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 0 8 5 -1.</_> + <_>12 0 4 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0111303897574544</threshold> + <left_val>0.4357841014862061</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>16 0 4 17 -1.</_> + <_>16 0 2 17 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0150928497314453</threshold> + <left_val>0.4561539888381958</left_val> + <right_val>0.6119061708450317</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 0 18 20 -1.</_> + <_>7 0 6 20 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0279433000832796</threshold> + <left_val>0.6537144184112549</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 0 2 5 -1.</_> + <_>7 0 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.4036991312168539e-005</threshold> + <left_val>0.3474723100662231</left_val> + <right_val>0.5336967706680298</right_val></_></_> + <_> + <!-- tree 31 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 6 20 1 -1.</_> + <_>0 6 10 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0122327702119946</threshold> + <left_val>0.3731676042079926</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 7 6 4 -1.</_> + <_>10 7 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.8591412855312228e-004</threshold> + <left_val>0.5717229247093201</left_val> + <right_val>0.4793379008769989</right_val></_></_> + <_> + <!-- tree 32 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 1 16 4 -1.</_> + <_>1 1 8 2 2.</_> + <_>9 3 8 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.8992990739643574e-003</threshold> + <left_val>0.4056436121463776</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 2 4 2 -1.</_> + <_>7 2 2 1 2.</_> + <_>9 3 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.9113907152786851e-004</threshold> + <left_val>0.6174048185348511</left_val> + <right_val>0.4471754133701325</right_val></_></_> + <_> + <!-- tree 33 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 4 9 3 -1.</_> + <_>7 5 9 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.2117747515439987e-003</threshold> + <left_node>1</left_node> + <right_val>0.6179698109626770</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 4 5 12 -1.</_> + <_>10 10 5 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0455644801259041</threshold> + <left_val>0.2285494953393936</left_val> + <right_val>0.5249565839767456</right_val></_></_> + <_> + <!-- tree 34 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 12 2 3 -1.</_> + <_>3 13 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.3631910122931004e-003</threshold> + <left_val>0.1784950047731400</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 8 3 5 -1.</_> + <_>9 8 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0122749703004956</threshold> + <left_val>0.7261952757835388</left_val> + <right_val>0.4550398886203766</right_val></_></_> + <_> + <!-- tree 35 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 9 2 3 -1.</_> + <_>13 9 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.4185991175472736e-003</threshold> + <left_val>0.5252990722656250</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>15 11 2 2 -1.</_> + <_>15 12 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.1846961984410882e-004</threshold> + <left_val>0.5445222258567810</left_val> + <right_val>0.3272218108177185</right_val></_></_> + <_> + <!-- tree 36 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 6 2 3 -1.</_> + <_>5 7 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.1358140297234058e-003</threshold> + <left_node>1</left_node> + <right_val>0.7013831734657288</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 11 6 2 -1.</_> + <_>2 12 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.9578010910190642e-004</threshold> + <left_val>0.4965943992137909</left_val> + <right_val>0.3295598030090332</right_val></_></_> + <_> + <!-- tree 37 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 11 4 3 -1.</_> + <_>15 12 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.6887691132724285e-003</threshold> + <left_val>0.5362641811370850</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>16 0 4 17 -1.</_> + <_>16 0 2 17 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0182554405182600</threshold> + <left_val>0.6496108770370483</left_val> + <right_val>0.4757137000560761</right_val></_></_> + <_> + <!-- tree 38 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 11 4 3 -1.</_> + <_>1 12 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.2736468389630318e-003</threshold> + <left_val>0.2343741059303284</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 11 1 3 -1.</_> + <_>9 12 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.4320168886333704e-003</threshold> + <left_val>0.4620118141174316</left_val> + <right_val>0.6898419260978699</right_val></_></_> + <_> + <!-- tree 39 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 9 6 7 -1.</_> + <_>10 9 3 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0496176294982433</threshold> + <left_val>0.2100719958543778</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 15 4 2 -1.</_> + <_>8 16 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1701210169121623e-003</threshold> + <left_val>0.4621528983116150</left_val> + <right_val>0.5797135829925537</right_val></_></_> + <_> + <!-- tree 40 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 9 6 7 -1.</_> + <_>7 9 3 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0452372916042805</threshold> + <left_val>0.2118262052536011</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 14 2 3 -1.</_> + <_>9 15 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.7563421539962292e-003</threshold> + <left_val>0.4884614944458008</left_val> + <right_val>0.6872498989105225</right_val></_></_> + <_> + <!-- tree 41 --> + <_> + <!-- 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.0148359695449471</threshold> + <left_node>1</left_node> + <right_val>0.5275105834007263</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 7 8 2 -1.</_> + <_>6 8 8 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.7436608262360096e-004</threshold> + <left_val>0.4172320961952210</left_val> + <right_val>0.5491139888763428</right_val></_></_> + <_> + <!-- tree 42 --> + <_> + <!-- 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.0148359695449471</threshold> + <left_node>1</left_node> + <right_val>0.2124876976013184</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 1 2 10 -1.</_> + <_>3 1 1 5 2.</_> + <_>4 6 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.0892542609944940e-004</threshold> + <left_val>0.5495215058326721</left_val> + <right_val>0.4207795858383179</right_val></_></_> + <_> + <!-- tree 43 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 4 1 10 -1.</_> + <_>13 9 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.7517668250948191e-004</threshold> + <left_val>0.3321942090988159</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 8 4 3 -1.</_> + <_>9 9 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.7618978209793568e-003</threshold> + <left_val>0.2212958037853241</left_val> + <right_val>0.5232653021812439</right_val></_></_> + <_> + <!-- tree 44 --> + <_> + <!-- 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.0401358604431152</threshold> + <left_val>0.1101796030998230</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 1 3 5 -1.</_> + <_>6 1 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.3651469275355339e-003</threshold> + <left_val>0.3810100853443146</left_val> + <right_val>0.5617291927337647</right_val></_></_> + <_> + <!-- tree 45 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 10 2 3 -1.</_> + <_>9 11 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.4713007779791951e-004</threshold> + <left_node>1</left_node> + <right_val>0.5795056819915772</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 11 2 2 -1.</_> + <_>9 12 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.2727389372885227e-003</threshold> + <left_val>0.6392269134521484</left_val> + <right_val>0.4711438119411469</right_val></_></_> + <_> + <!-- tree 46 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 10 20 2 -1.</_> + <_>0 11 20 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.6202510818839073e-003</threshold> + <left_node>1</left_node> + <right_val>0.3409883975982666</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 7 6 4 -1.</_> + <_>1 7 3 2 2.</_> + <_>4 9 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.7307618660852313e-004</threshold> + <left_val>0.3659302890300751</left_val> + <right_val>0.5388171076774597</right_val></_></_> + <_> + <!-- tree 47 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 0 8 8 -1.</_> + <_>16 0 4 4 2.</_> + <_>12 4 4 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0330949090421200</threshold> + <left_node>1</left_node> + <right_val>0.7170385718345642</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>14 1 6 4 -1.</_> + <_>16 1 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0115441195666790</threshold> + <left_val>0.6386818289756775</left_val> + <right_val>0.4681304097175598</right_val></_></_> + <_> + <!-- tree 48 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 3 2 14 -1.</_> + <_>6 10 2 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.4234469793736935e-003</threshold> + <left_val>0.3263700902462006</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 1 7 12 -1.</_> + <_>6 7 7 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.2252950370311737e-003</threshold> + <left_val>0.5767819285392761</left_val> + <right_val>0.4346418082714081</right_val></_></_> + <_> + <!-- tree 49 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 0 15 5 -1.</_> + <_>10 0 5 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0181331094354391</threshold> + <left_val>0.4697827994823456</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>15 0 4 10 -1.</_> + <_>15 0 2 10 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.0903049781918526e-003</threshold> + <left_val>0.4437389075756073</left_val> + <right_val>0.6061668992042542</right_val></_></_> + <_> + <!-- tree 50 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 0 18 3 -1.</_> + <_>7 0 6 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0132729401811957</threshold> + <left_val>0.6558511257171631</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 0 17 2 -1.</_> + <_>0 1 17 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4632199599873275e-004</threshold> + <left_val>0.3376353979110718</left_val> + <right_val>0.5091655254364014</right_val></_></_> + <_> + <!-- tree 51 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 0 3 3 -1.</_> + <_>11 0 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.5790191031992435e-003</threshold> + <left_val>0.2947883903980255</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 0 3 12 -1.</_> + <_>11 0 1 12 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.6997101162560284e-004</threshold> + <left_val>0.5556982159614563</left_val> + <right_val>0.4665456116199493</right_val></_></_> + <_> + <!-- tree 52 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 3 4 16 -1.</_> + <_>1 3 2 8 2.</_> + <_>3 11 2 8 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0481794402003288</threshold> + <left_val>0.7338355779647827</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 0 3 3 -1.</_> + <_>8 0 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.2581362696364522e-004</threshold> + <left_val>0.3543871939182282</left_val> + <right_val>0.5285149812698364</right_val></_></_> + <_> + <!-- tree 53 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 13 2 6 -1.</_> + <_>9 16 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0147807300090790</threshold> + <left_val>0.1944441944360733</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 0 6 13 -1.</_> + <_>11 0 2 13 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.1002745032310486</threshold> + <left_val>0.0990492925047874</left_val> + <right_val>0.5139853954315186</right_val></_></_> + <_> + <!-- tree 54 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 7 3 2 -1.</_> + <_>8 7 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.3848101096227765e-004</threshold> + <left_val>0.5827109813690186</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 2 1 12 -1.</_> + <_>8 6 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.8861360624432564e-003</threshold> + <left_val>0.3441427946090698</left_val> + <right_val>0.5148838758468628</right_val></_></_> + <_> + <!-- tree 55 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 10 12 6 -1.</_> + <_>10 10 6 3 2.</_> + <_>4 13 6 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0436827614903450</threshold> + <left_node>1</left_node> + <right_val>0.5207998156547546</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>13 5 2 3 -1.</_> + <_>13 6 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.6115700602531433e-003</threshold> + <left_val>0.4835503101348877</left_val> + <right_val>0.6322219967842102</right_val></_></_> + <_> + <!-- tree 56 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 10 12 6 -1.</_> + <_>4 10 6 3 2.</_> + <_>10 13 6 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0436827614903450</threshold> + <left_node>1</left_node> + <right_val>0.1364538073539734</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 5 2 3 -1.</_> + <_>5 6 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7179530113935471e-003</threshold> + <left_val>0.4537320137023926</left_val> + <right_val>0.6066750884056091</right_val></_></_> + <_> + <!-- tree 57 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 6 6 7 -1.</_> + <_>10 6 2 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0339649096131325</threshold> + <left_node>1</left_node> + <right_val>0.4968374967575073</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 6 2 4 -1.</_> + <_>9 6 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0993590112775564e-003</threshold> + <left_val>0.5831680893898010</left_val> + <right_val>0.4688239991664887</right_val></_></_> + <_> + <!-- tree 58 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 6 6 7 -1.</_> + <_>8 6 2 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0543010793626308</threshold> + <left_node>1</left_node> + <right_val>0.7568289041519165</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 6 2 4 -1.</_> + <_>10 6 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0993590112775564e-003</threshold> + <left_val>0.4330148100852966</left_val> + <right_val>0.5768468976020813</right_val></_></_> + <_> + <!-- tree 59 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 9 2 3 -1.</_> + <_>12 9 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4954120160837192e-005</threshold> + <left_node>1</left_node> + <right_val>0.4443281888961792</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 6 20 1 -1.</_> + <_>0 6 10 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0314158685505390</threshold> + <left_val>0.5274472832679749</left_val> + <right_val>0.3037855923175812</right_val></_></_> + <_> + <!-- tree 60 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 10 2 -1.</_> + <_>10 7 5 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0108318496495485</threshold> + <left_node>1</left_node> + <right_val>0.3581720888614655</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 16 4 3 -1.</_> + <_>1 17 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.6545711383223534e-004</threshold> + <left_val>0.5937584042549133</left_val> + <right_val>0.4294629991054535</right_val></_></_> + <_> + <!-- tree 61 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 4 3 3 -1.</_> + <_>12 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.2743160370737314e-003</threshold> + <left_node>1</left_node> + <right_val>0.5954576730728149</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 3 5 3 -1.</_> + <_>10 4 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.9340821094810963e-003</threshold> + <left_val>0.4792222976684570</left_val> + <right_val>0.5856133103370667</right_val></_></_> + <_> + <!-- tree 62 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 9 14 8 -1.</_> + <_>3 9 7 4 2.</_> + <_>10 13 7 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.1451907753944397e-003</threshold> + <left_node>1</left_node> + <right_val>0.3573477864265442</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 8 8 10 -1.</_> + <_>6 8 4 5 2.</_> + <_>10 13 4 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.2763288840651512e-003</threshold> + <left_val>0.4026022851467133</left_val> + <right_val>0.5764743089675903</right_val></_></_> + <_> + <!-- tree 63 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 4 3 3 -1.</_> + <_>12 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.3787851035594940e-003</threshold> + <left_node>1</left_node> + <right_val>0.4981333017349243</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 3 5 3 -1.</_> + <_>10 4 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5621910570189357e-003</threshold> + <left_val>0.4736588001251221</left_val> + <right_val>0.5583608150482178</right_val></_></_> + <_> + <!-- tree 64 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 4 3 3 -1.</_> + <_>5 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.2318739686161280e-003</threshold> + <left_node>1</left_node> + <right_val>0.6167436838150024</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 3 5 3 -1.</_> + <_>5 4 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.6804019734263420e-003</threshold> + <left_val>0.4131424129009247</left_val> + <right_val>0.6280695199966431</right_val></_></_> + <_> + <!-- tree 65 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 16 2 3 -1.</_> + <_>13 17 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.3396480139344931e-003</threshold> + <left_val>0.3446358144283295</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 5 20 6 -1.</_> + <_>0 7 20 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.2093348056077957</threshold> + <left_val>0.1038658022880554</left_val> + <right_val>0.5204489231109619</right_val></_></_> + <_> + <!-- tree 66 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 14 3 3 -1.</_> + <_>3 15 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.3805822283029556e-003</threshold> + <left_node>1</left_node> + <right_val>0.2167402058839798</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 15 5 3 -1.</_> + <_>7 16 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.0137799009680748e-003</threshold> + <left_val>0.6738399267196655</left_val> + <right_val>0.4896650910377502</right_val></_></_> + <_> + <!-- tree 67 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 9 2 3 -1.</_> + <_>12 9 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.1756077706813812e-003</threshold> + <left_node>1</left_node> + <right_val>0.5177915096282959</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>15 13 2 6 -1.</_> + <_>15 13 1 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.3951779156923294e-004</threshold> + <left_val>0.4819645881652832</left_val> + <right_val>0.5464438199996948</right_val></_></_> + <_> + <!-- tree 68 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 9 2 3 -1.</_> + <_>7 9 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0127760469913483e-003</threshold> + <left_node>1</left_node> + <right_val>0.3423596024513245</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 13 2 6 -1.</_> + <_>4 13 1 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.9784599104896188e-004</threshold> + <left_val>0.4488461017608643</left_val> + <right_val>0.5912671089172363</right_val></_></_> + <_> + <!-- tree 69 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 4 2 4 -1.</_> + <_>11 4 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3596490316558629e-004</threshold> + <left_node>1</left_node> + <right_val>0.5568863153457642</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>13 4 2 5 -1.</_> + <_>13 4 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0135716600343585</threshold> + <left_val>0.5161067843437195</left_val> + <right_val>0.1713000982999802</right_val></_></_> + <_> + <!-- tree 70 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 4 2 4 -1.</_> + <_>8 4 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.0259079721872695e-005</threshold> + <left_node>1</left_node> + <right_val>0.4916203916072846</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 4 2 5 -1.</_> + <_>6 4 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.2625840976834297e-003</threshold> + <left_val>0.6404662728309631</left_val> + <right_val>0.2859084904193878</right_val></_></_> + <_> + <!-- tree 71 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>19 6 1 2 -1.</_> + <_>19 7 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.9217010412830859e-004</threshold> + <left_node>1</left_node> + <right_val>0.5459282994270325</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 7 8 13 -1.</_> + <_>12 7 4 13 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0219938792288303</threshold> + <left_val>0.4715713858604431</left_val> + <right_val>0.5690075159072876</right_val></_></_> + <_> + <!-- tree 72 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 6 1 2 -1.</_> + <_>0 7 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.8907777788117528e-004</threshold> + <left_node>1</left_node> + <right_val>0.3279826939105988</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 15 4 3 -1.</_> + <_>6 16 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.0893891602754593e-004</threshold> + <left_val>0.4302007853984833</left_val> + <right_val>0.5696045160293579</right_val></_></_> + <_> + <!-- tree 73 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 8 2 2 -1.</_> + <_>11 9 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1662710312521085e-004</threshold> + <left_node>1</left_node> + <right_val>0.5387235283851624</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 7 2 4 -1.</_> + <_>11 7 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.0604078248143196e-003</threshold> + <left_val>0.5021423101425171</left_val> + <right_val>0.5965322256088257</right_val></_></_> + <_> + <!-- tree 74 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 13 2 3 -1.</_> + <_>4 14 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.5925969071686268e-004</threshold> + <left_node>1</left_node> + <right_val>0.3473494052886963</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 17 18 3 -1.</_> + <_>6 17 6 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0195261295884848</threshold> + <left_val>0.6475545167922974</left_val> + <right_val>0.4643782079219818</right_val></_></_></trees> + <stage_threshold>36.7265014648437500</stage_threshold> + <parent>13</parent> + <next>-1</next></_> + <_> + <!-- stage 15 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 0 18 5 -1.</_> + <_>7 0 6 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0412424392998219</threshold> + <left_val>0.3393315076828003</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 7 3 4 -1.</_> + <_>5 9 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0156267099082470</threshold> + <left_val>0.5104100108146668</left_val> + <right_val>0.7772815227508545</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 6 2 2 -1.</_> + <_>10 6 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.9947189614176750e-004</threshold> + <left_val>0.3664673864841461</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 4 14 4 -1.</_> + <_>13 4 7 2 2.</_> + <_>6 6 7 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0037609608843923e-003</threshold> + <left_val>0.5405650734901428</left_val> + <right_val>0.3926205039024353</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.8128242855891585e-004</threshold> + <left_val>0.4251519143581390</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 15 2 4 -1.</_> + <_>7 17 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3098999625071883e-004</threshold> + <left_val>0.4135144948959351</left_val> + <right_val>0.6925746202468872</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 5 5 14 -1.</_> + <_>8 12 5 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.1696720980107784e-003</threshold> + <left_node>1</left_node> + <right_val>0.3455873131752014</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 9 2 2 -1.</_> + <_>9 10 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.0587369799613953e-003</threshold> + <left_val>0.2234193980693817</left_val> + <right_val>0.5286118984222412</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 5 3 7 -1.</_> + <_>8 5 1 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.6395038953050971e-004</threshold> + <left_node>1</left_node> + <right_val>0.4206520020961762</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 0 3 9 -1.</_> + <_>0 3 3 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.5089480224996805e-003</threshold> + <left_val>0.6502981781959534</left_val> + <right_val>0.4117597937583923</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 6 8 8 -1.</_> + <_>12 6 4 4 2.</_> + <_>8 10 4 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.3975980002433062e-003</threshold> + <left_node>1</left_node> + <right_val>0.3673301041126251</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 8 13 2 -1.</_> + <_>4 9 13 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0901279747486115e-003</threshold> + <left_val>0.2906238138675690</left_val> + <right_val>0.5445111989974976</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 3 6 1 -1.</_> + <_>6 3 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.6524370585102588e-004</threshold> + <left_val>0.4233515858650208</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 1 2 6 -1.</_> + <_>9 3 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.1602319106459618e-004</threshold> + <left_val>0.3886361122131348</left_val> + <right_val>0.6269165873527527</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 5 6 4 -1.</_> + <_>12 5 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.3739910102449358e-004</threshold> + <left_val>0.5524451136589050</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 5 2 12 -1.</_> + <_>9 9 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0247397609055042</threshold> + <left_val>0.4960095882415772</left_val> + <right_val>0.5373491048812866</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 4 3 -1.</_> + <_>8 15 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0153428399935365</threshold> + <left_val>0.6849405169487000</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 12 4 3 -1.</_> + <_>8 13 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0115404697135091</threshold> + <left_val>0.4037235081195831</left_val> + <right_val>0.6786940097808838</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 3 6 7 -1.</_> + <_>12 3 2 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.4230621792376041e-003</threshold> + <left_node>1</left_node> + <right_val>0.3814676105976105</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 10 16 6 -1.</_> + <_>3 12 16 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0129778096452355</threshold> + <left_val>0.5527058839797974</left_val> + <right_val>0.3744955956935883</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 3 10 -1.</_> + <_>5 10 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1063399724662304e-003</threshold> + <left_val>0.3520928919315338</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 10 3 6 -1.</_> + <_>6 13 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3743690215051174e-003</threshold> + <left_val>0.5641903281211853</left_val> + <right_val>0.3075025975704193</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>17 2 2 12 -1.</_> + <_>17 2 1 12 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0162337794899940</threshold> + <left_val>0.4888828098773956</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>16 6 2 14 -1.</_> + <_>16 13 2 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.1519351806491613e-004</threshold> + <left_val>0.5456321239471436</left_val> + <right_val>0.4743550121784210</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 11 12 9 -1.</_> + <_>3 14 12 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0907824933528900</threshold> + <left_val>0.2925248146057129</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 2 4 12 -1.</_> + <_>2 2 2 12 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0116652101278305</threshold> + <left_val>0.4688454866409302</left_val> + <right_val>0.6230347752571106</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 0 2 18 -1.</_> + <_>18 0 1 18 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0232864096760750</threshold> + <left_val>0.6895843148231506</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>16 12 3 2 -1.</_> + <_>16 13 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1559339947998524e-003</threshold> + <left_val>0.5355802178382874</left_val> + <right_val>0.3423466086387634</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 2 15 -1.</_> + <_>1 2 1 15 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.3167220428586006e-003</threshold> + <left_val>0.5937076210975647</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 10 2 4 -1.</_> + <_>1 12 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5610599657520652e-003</threshold> + <left_val>0.4708659946918488</left_val> + <right_val>0.2736997008323669</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 1 2 18 -1.</_> + <_>11 1 1 18 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0140766398981214</threshold> + <left_val>0.5287156105041504</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 2 14 2 -1.</_> + <_>10 2 7 1 2.</_> + <_>3 3 7 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.1018589660525322e-003</threshold> + <left_val>0.5336192846298218</left_val> + <right_val>0.3224813938140869</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 1 2 18 -1.</_> + <_>8 1 1 18 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.8221647739410400e-003</threshold> + <left_val>0.2983910143375397</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 1 8 12 -1.</_> + <_>6 7 8 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.3852899000048637e-003</threshold> + <left_val>0.5623999238014221</left_val> + <right_val>0.4295912086963654</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 4 3 -1.</_> + <_>8 15 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.3483278974890709e-003</threshold> + <left_node>1</left_node> + <right_val>0.6813961267471314</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 14 6 3 -1.</_> + <_>7 15 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.5707519855350256e-003</threshold> + <left_val>0.5857968926429749</left_val> + <right_val>0.4603429138660431</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 13 5 2 -1.</_> + <_>0 14 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.3340100888162851e-003</threshold> + <left_node>1</left_node> + <right_val>0.2744851112365723</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 0 2 6 -1.</_> + <_>9 0 1 3 2.</_> + <_>10 3 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.7432780265808105e-003</threshold> + <left_val>0.5047526955604553</left_val> + <right_val>0.2362741976976395</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 0 2 6 -1.</_> + <_>10 0 1 3 2.</_> + <_>9 3 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.5055489540100098e-003</threshold> + <left_val>0.5242248177528381</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 7 3 6 -1.</_> + <_>10 7 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0125892497599125</threshold> + <left_val>0.4823690950870514</left_val> + <right_val>0.6752536892890930</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 0 2 6 -1.</_> + <_>9 0 1 3 2.</_> + <_>10 3 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.3358368352055550e-003</threshold> + <left_val>0.1734634935855866</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 7 3 6 -1.</_> + <_>9 7 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.7639651931822300e-003</threshold> + <left_val>0.6354380846023560</left_val> + <right_val>0.4587475061416626</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 6 2 6 -1.</_> + <_>9 6 1 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3599749654531479e-003</threshold> + <left_val>0.4580380916595459</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 4 4 3 -1.</_> + <_>9 4 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0284042600542307</threshold> + <left_val>0.5176380872726440</left_val> + <right_val>0.1204385012388229</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 4 4 3 -1.</_> + <_>0 5 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.2958156019449234e-003</threshold> + <left_val>0.2337957024574280</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 7 4 2 -1.</_> + <_>8 8 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1800320353358984e-003</threshold> + <left_val>0.3902814090251923</left_val> + <right_val>0.5652930140495300</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 6 6 3 -1.</_> + <_>12 6 2 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.0948140881955624e-003</threshold> + <left_val>0.5512028932571411</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 6 3 12 -1.</_> + <_>9 10 3 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.1679958812892437e-003</threshold> + <left_val>0.5455976128578186</left_val> + <right_val>0.4798949062824249</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 4 2 3 -1.</_> + <_>5 5 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.4458891972899437e-003</threshold> + <left_node>1</left_node> + <right_val>0.6127086877822876</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 6 1 3 -1.</_> + <_>5 7 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.2766510481014848e-003</threshold> + <left_val>0.5317131876945496</left_val> + <right_val>0.3850932121276856</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 17 3 2 -1.</_> + <_>10 17 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.9404270723462105e-004</threshold> + <left_val>0.5446437001228333</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 7 20 2 -1.</_> + <_>0 8 20 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0423096083104610</threshold> + <left_val>0.5234643816947937</left_val> + <right_val>0.2213044017553330</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 3 6 7 -1.</_> + <_>6 3 2 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.6189671158790588e-003</threshold> + <left_val>0.4916197955608368</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 10 6 10 -1.</_> + <_>5 10 3 5 2.</_> + <_>8 15 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.2401198558509350e-003</threshold> + <left_val>0.1471475958824158</left_val> + <right_val>0.4852893948554993</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 17 3 2 -1.</_> + <_>10 17 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.5610670931637287e-003</threshold> + <left_val>0.2773773968219757</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 10 2 2 -1.</_> + <_>9 11 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.5506159949582070e-005</threshold> + <left_val>0.4626461863517761</left_val> + <right_val>0.5768079161643982</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 17 3 2 -1.</_> + <_>9 17 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.1903791502118111e-003</threshold> + <left_val>0.1644289940595627</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 6 1 3 -1.</_> + <_>5 7 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.1186462193727493e-004</threshold> + <left_val>0.4778591096401215</left_val> + <right_val>0.6261864900588989</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 1 20 2 -1.</_> + <_>10 1 10 1 2.</_> + <_>0 2 10 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0137798096984625</threshold> + <left_val>0.5257307887077332</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>14 2 6 9 -1.</_> + <_>14 5 6 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1290319962427020e-003</threshold> + <left_val>0.5498048067092896</left_val> + <right_val>0.3983106911182404</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 3 3 2 -1.</_> + <_>5 4 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0610350000206381e-004</threshold> + <left_val>0.4033519029617310</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 4 4 2 -1.</_> + <_>7 4 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6695790691301227e-004</threshold> + <left_val>0.4149340093135834</left_val> + <right_val>0.5795341134071350</right_val></_></_> + <_> + <!-- tree 31 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 2 6 9 -1.</_> + <_>14 5 6 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1290319962427020e-003</threshold> + <left_node>1</left_node> + <right_val>0.3934114873409271</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 12 20 6 -1.</_> + <_>0 14 20 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.1201934963464737</threshold> + <left_val>0.0734004825353622</left_val> + <right_val>0.5202586054801941</right_val></_></_> + <_> + <!-- tree 32 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 2 16 4 -1.</_> + <_>2 2 8 2 2.</_> + <_>10 4 8 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0152307404205203</threshold> + <left_val>0.3749505877494812</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 12 5 3 -1.</_> + <_>7 13 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.5759829916059971e-003</threshold> + <left_val>0.5078150033950806</left_val> + <right_val>0.6606066226959229</right_val></_></_> + <_> + <!-- tree 33 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 9 6 10 -1.</_> + <_>14 9 3 10 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0134794600307941</threshold> + <left_val>0.4547711014747620</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>16 6 3 2 -1.</_> + <_>16 7 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.1162950433790684e-003</threshold> + <left_val>0.3311006128787994</left_val> + <right_val>0.5384259223937988</right_val></_></_> + <_> + <!-- tree 34 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 9 6 10 -1.</_> + <_>3 9 3 10 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0178777091205120</threshold> + <left_val>0.6513252854347229</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 16 5 2 -1.</_> + <_>0 17 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0931970318779349e-003</threshold> + <left_val>0.5264765024185181</left_val> + <right_val>0.3456991016864777</right_val></_></_> + <_> + <!-- tree 35 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 12 2 3 -1.</_> + <_>9 13 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.0553159303963184e-003</threshold> + <left_val>0.6268613934516907</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 7 2 12 -1.</_> + <_>9 11 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.6365049891173840e-003</threshold> + <left_val>0.5399212837219238</left_val> + <right_val>0.4345397055149078</right_val></_></_> + <_> + <!-- tree 36 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 2 6 2 -1.</_> + <_>5 2 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.7896481747739017e-005</threshold> + <left_val>0.3835605978965759</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 1 1 2 -1.</_> + <_>4 2 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.2714448752813041e-004</threshold> + <left_val>0.3337667882442474</left_val> + <right_val>0.5539165735244751</right_val></_></_> + <_> + <!-- tree 37 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 15 1 2 -1.</_> + <_>11 16 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.3425030889920890e-004</threshold> + <left_node>1</left_node> + <right_val>0.5788270235061646</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 1 16 2 -1.</_> + <_>11 1 8 1 2.</_> + <_>3 2 8 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0140055799856782</threshold> + <left_val>0.5275077819824219</left_val> + <right_val>0.2701125144958496</right_val></_></_> + <_> + <!-- tree 38 --> + <_> + <!-- 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>-9.2654931358993053e-004</threshold> + <left_val>0.5852280259132385</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 11 10 6 -1.</_> + <_>5 11 5 3 2.</_> + <_>10 14 5 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.9504268206655979e-003</threshold> + <left_val>0.4728336930274963</left_val> + <right_val>0.3313918113708496</right_val></_></_> + <_> + <!-- tree 39 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 11 4 6 -1.</_> + <_>10 14 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.8086868375539780e-004</threshold> + <left_node>1</left_node> + <right_val>0.4258810877799988</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>14 9 6 11 -1.</_> + <_>16 9 2 11 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0120180202648044</threshold> + <left_val>0.5609787106513977</left_val> + <right_val>0.4895192086696625</right_val></_></_> + <_> + <!-- tree 40 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 9 6 11 -1.</_> + <_>2 9 2 11 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.1452154070138931</threshold> + <left_val>0.0438944809138775</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 11 16 6 -1.</_> + <_>2 11 8 3 2.</_> + <_>10 14 8 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.6049019806087017e-003</threshold> + <left_val>0.4229170978069305</left_val> + <right_val>0.5616292953491211</right_val></_></_> + <_> + <!-- tree 41 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 0 8 10 -1.</_> + <_>16 0 4 5 2.</_> + <_>12 5 4 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0349097512662411</threshold> + <left_node>1</left_node> + <right_val>0.4788128137588501</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>14 2 6 4 -1.</_> + <_>16 2 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.7478420417755842e-003</threshold> + <left_val>0.4800282120704651</left_val> + <right_val>0.5801389217376709</right_val></_></_> + <_> + <!-- tree 42 --> + <_> + <!-- 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.0330380313098431</threshold> + <left_node>1</left_node> + <right_val>0.7078176140785217</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 2 6 4 -1.</_> + <_>2 2 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.6872599739581347e-003</threshold> + <left_val>0.4449624121189117</left_val> + <right_val>0.5957731008529663</right_val></_></_> + <_> + <!-- tree 43 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 9 15 2 -1.</_> + <_>9 9 5 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.5311939902603626e-003</threshold> + <left_val>0.4177047014236450</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 3 4 8 -1.</_> + <_>14 3 2 4 2.</_> + <_>12 7 2 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.1058510541915894e-003</threshold> + <left_val>0.5372948050498962</left_val> + <right_val>0.3736926913261414</right_val></_></_> + <_> + <!-- tree 44 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 2 2 9 -1.</_> + <_>10 2 1 9 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.7599847465753555e-003</threshold> + <left_val>0.6658807992935181</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 2 20 1 -1.</_> + <_>10 2 10 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0230033099651337</threshold> + <left_val>0.2647922039031982</left_val> + <right_val>0.5101817846298218</right_val></_></_> + <_> + <!-- tree 45 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 1 4 5 -1.</_> + <_>16 1 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.3664818406105042e-003</threshold> + <left_val>0.4548634886741638</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>16 0 4 6 -1.</_> + <_>16 3 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0389717705547810</threshold> + <left_val>0.5157061815261841</left_val> + <right_val>0.3436439037322998</right_val></_></_> + <_> + <!-- tree 46 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 3 6 4 -1.</_> + <_>6 3 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0277671907097101</threshold> + <left_val>0.2354391068220139</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 0 18 5 -1.</_> + <_>6 0 6 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.8894089460372925e-003</threshold> + <left_val>0.6887741088867188</left_val> + <right_val>0.5111051797866821</right_val></_></_> + <_> + <!-- tree 47 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 2 12 14 -1.</_> + <_>12 2 6 7 2.</_> + <_>6 9 6 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.2073140610009432e-003</threshold> + <left_val>0.5438867807388306</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 8 3 5 -1.</_> + <_>12 8 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.7484978353604674e-004</threshold> + <left_val>0.5451148748397827</left_val> + <right_val>0.4831353127956390</right_val></_></_> + <_> + <!-- tree 48 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 12 2 2 -1.</_> + <_>5 13 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.1947520114481449e-003</threshold> + <left_val>0.2113419026136398</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 10 4 3 -1.</_> + <_>7 10 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.6169899501837790e-004</threshold> + <left_val>0.5273681879043579</left_val> + <right_val>0.3992587029933929</right_val></_></_> + <_> + <!-- tree 49 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 9 15 2 -1.</_> + <_>9 9 5 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.2421479225158691e-003</threshold> + <left_val>0.4688260853290558</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 7 6 2 -1.</_> + <_>12 7 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.2139769969508052e-003</threshold> + <left_val>0.5504235029220581</left_val> + <right_val>0.4384871125221252</right_val></_></_> + <_> + <!-- tree 50 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 9 15 2 -1.</_> + <_>6 9 5 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.9469770379364491e-003</threshold> + <left_val>0.3892847001552582</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 0 2 10 -1.</_> + <_>5 0 1 5 2.</_> + <_>6 5 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.9291830034926534e-004</threshold> + <left_val>0.6001722812652588</left_val> + <right_val>0.4561662971973419</right_val></_></_> + <_> + <!-- tree 51 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 20 14 -1.</_> + <_>0 7 20 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.6255072951316834</threshold> + <left_node>1</left_node> + <right_val>0.0681256130337715</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 7 8 4 -1.</_> + <_>12 7 4 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.7744520753622055e-003</threshold> + <left_val>0.4813025891780853</left_val> + <right_val>0.5620657205581665</right_val></_></_> + <_> + <!-- tree 52 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 7 8 4 -1.</_> + <_>4 7 4 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0943782478570938</threshold> + <left_node>1</left_node> + <right_val>0.0666322931647301</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 1 3 3 -1.</_> + <_>9 1 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.9560910295695066e-003</threshold> + <left_val>0.3588232994079590</left_val> + <right_val>0.5295407176017761</right_val></_></_> + <_> + <!-- tree 53 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 3 4 -1.</_> + <_>10 7 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.0652769431471825e-003</threshold> + <left_val>0.4822688102722168</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 9 3 1 -1.</_> + <_>10 9 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.2138071148656309e-004</threshold> + <left_val>0.4670332968235016</left_val> + <right_val>0.5683112740516663</right_val></_></_> + <_> + <!-- tree 54 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 9 3 2 -1.</_> + <_>8 10 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.4220191193744540e-004</threshold> + <left_node>1</left_node> + <right_val>0.5360795259475708</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 4 2 8 -1.</_> + <_>8 4 1 4 2.</_> + <_>9 8 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.7313501127064228e-003</threshold> + <left_val>0.6137245893478394</left_val> + <right_val>0.3188089132308960</right_val></_></_> + <_> + <!-- tree 55 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 8 12 3 -1.</_> + <_>5 9 12 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5395509544759989e-003</threshold> + <left_val>0.4487720131874085</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 14 1 3 -1.</_> + <_>11 15 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.4315000046044588e-003</threshold> + <left_val>0.4894166886806488</left_val> + <right_val>0.6716653704643250</right_val></_></_> + <_> + <!-- tree 56 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 10 3 6 -1.</_> + <_>6 12 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0155816199257970</threshold> + <left_val>0.3336741924285889</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 17 8 3 -1.</_> + <_>4 18 8 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0816920548677444e-003</threshold> + <left_val>0.4718219935894013</left_val> + <right_val>0.5960627198219299</right_val></_></_> + <_> + <!-- tree 57 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>17 6 2 3 -1.</_> + <_>17 7 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.2197659127414227e-003</threshold> + <left_val>0.3588554859161377</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 12 2 2 -1.</_> + <_>10 12 1 1 2.</_> + <_>9 13 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.3048671260476112e-004</threshold> + <left_val>0.6218712925910950</left_val> + <right_val>0.4817300140857697</right_val></_></_> + <_> + <!-- tree 58 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 13 2 4 -1.</_> + <_>9 13 1 2 2.</_> + <_>10 15 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.7418707981705666e-003</threshold> + <left_val>0.2550027072429657</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 11 2 3 -1.</_> + <_>9 12 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.2950369901955128e-003</threshold> + <left_val>0.6728078722953796</left_val> + <right_val>0.5051063895225525</right_val></_></_> + <_> + <!-- tree 59 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 12 10 -1.</_> + <_>11 5 6 5 2.</_> + <_>5 10 6 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.5216049291193485e-003</threshold> + <left_val>0.5401909947395325</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 3 12 12 -1.</_> + <_>12 3 6 6 2.</_> + <_>6 9 6 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.4289379362016916e-003</threshold> + <left_val>0.5419461727142334</left_val> + <right_val>0.4347142875194550</right_val></_></_> + <_> + <!-- tree 60 --> + <_> + <!-- 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.5261470582336187e-003</threshold> + <left_val>0.6970624923706055</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 3 3 2 -1.</_> + <_>5 3 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4817339833825827e-003</threshold> + <left_val>0.3263416886329651</left_val> + <right_val>0.4917873144149780</right_val></_></_> + <_> + <!-- tree 61 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 2 12 14 -1.</_> + <_>12 2 6 7 2.</_> + <_>6 9 6 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.2247453033924103</threshold> + <left_val>7.2937291115522385e-003</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 2 12 3 -1.</_> + <_>9 2 4 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.8342509176582098e-003</threshold> + <left_val>0.4579229950904846</left_val> + <right_val>0.5379881262779236</right_val></_></_> + <_> + <!-- tree 62 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 1 18 17 -1.</_> + <_>7 1 6 17 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0208216104656458</threshold> + <left_val>0.6024088859558106</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 9 10 1 -1.</_> + <_>5 9 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4896340144332498e-004</threshold> + <left_val>0.3336144089698792</left_val> + <right_val>0.4962815940380096</right_val></_></_> + <_> + <!-- tree 63 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 8 4 3 -1.</_> + <_>16 9 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.3524499740451574e-003</threshold> + <left_val>0.3558751046657562</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 13 6 6 -1.</_> + <_>7 16 6 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0372798815369606</threshold> + <left_val>0.1698562949895859</left_val> + <right_val>0.5208985805511475</right_val></_></_> + <_> + <!-- tree 64 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 14 1 6 -1.</_> + <_>6 16 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3896770542487502e-004</threshold> + <left_node>1</left_node> + <right_val>0.5590686202049255</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 17 4 2 -1.</_> + <_>6 18 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.1912620761431754e-004</threshold> + <left_val>0.5848733782768250</left_val> + <right_val>0.3795836865901947</right_val></_></_> + <_> + <!-- tree 65 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 18 6 2 -1.</_> + <_>13 18 3 1 2.</_> + <_>10 19 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.4003461264073849e-004</threshold> + <left_node>1</left_node> + <right_val>0.5670288205146790</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>16 8 1 3 -1.</_> + <_>16 9 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.8956850767135620e-003</threshold> + <left_val>0.5182694792747498</left_val> + <right_val>0.3327709138393402</right_val></_></_> + <_> + <!-- tree 66 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 13 4 3 -1.</_> + <_>8 14 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6084529925137758e-003</threshold> + <left_node>1</left_node> + <right_val>0.5410485863685608</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 15 1 2 -1.</_> + <_>9 16 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.7474587811157107e-004</threshold> + <left_val>0.6022642254829407</left_val> + <right_val>0.3644644021987915</right_val></_></_> + <_> + <!-- tree 67 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 0 3 12 -1.</_> + <_>14 0 1 12 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0134350396692753</threshold> + <left_node>1</left_node> + <right_val>0.3441281914710999</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>15 11 1 3 -1.</_> + <_>15 12 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1368139423429966e-003</threshold> + <left_val>0.5292434096336365</left_val> + <right_val>0.2747075855731964</right_val></_></_> + <_> + <!-- tree 68 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 15 3 3 -1.</_> + <_>8 16 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0141576295718551</threshold> + <left_node>1</left_node> + <right_val>0.8027868270874023</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 0 3 12 -1.</_> + <_>5 0 1 12 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.3884391672909260e-003</threshold> + <left_val>0.5222315192222595</left_val> + <right_val>0.3586727976799011</right_val></_></_> + <_> + <!-- tree 69 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 3 3 -1.</_> + <_>10 7 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.8013410568237305e-003</threshold> + <left_val>0.4900386929512024</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 9 3 1 -1.</_> + <_>10 9 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.8858849438838661e-004</threshold> + <left_val>0.4681056141853333</left_val> + <right_val>0.5721952915191650</right_val></_></_> + <_> + <!-- tree 70 --> + <_> + <!-- 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>-2.2143588867038488e-003</threshold> + <left_val>0.5388805866241455</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 2 12 3 -1.</_> + <_>8 2 4 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.4642972797155380e-003</threshold> + <left_val>0.6675537824630737</left_val> + <right_val>0.3448441922664642</right_val></_></_> + <_> + <!-- tree 71 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 18 2 2 -1.</_> + <_>18 18 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0150443902239203</threshold> + <left_node>1</left_node> + <right_val>0.9239614009857178</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>17 2 3 8 -1.</_> + <_>18 2 1 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.6346402056515217e-003</threshold> + <left_val>0.4884896874427795</left_val> + <right_val>0.6306052803993225</right_val></_></_> + <_> + <!-- tree 72 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 18 2 2 -1.</_> + <_>1 18 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.3895121305249631e-004</threshold> + <left_node>1</left_node> + <right_val>0.3997431099414825</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 11 2 6 -1.</_> + <_>6 14 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1157610171940178e-004</threshold> + <left_val>0.5663982033729553</left_val> + <right_val>0.3972980976104736</right_val></_></_> + <_> + <!-- tree 73 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 10 5 6 -1.</_> + <_>13 12 5 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0275149494409561</threshold> + <left_node>1</left_node> + <right_val>0.5201063752174377</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 8 15 3 -1.</_> + <_>5 9 15 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0516030602157116</threshold> + <left_val>0.5140730142593384</left_val> + <right_val>0.1245130971074104</right_val></_></_> + <_> + <!-- tree 74 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 10 5 6 -1.</_> + <_>2 12 5 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.7510651163756847e-003</threshold> + <left_node>1</left_node> + <right_val>0.3802095055580139</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 8 15 3 -1.</_> + <_>0 9 15 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.1457639522850513e-003</threshold> + <left_val>0.3309448063373566</left_val> + <right_val>0.5474538803100586</right_val></_></_> + <_> + <!-- tree 75 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 2 3 1 -1.</_> + <_>17 2 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.8178009930998087e-004</threshold> + <left_node>1</left_node> + <right_val>0.4892601966857910</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>17 4 3 2 -1.</_> + <_>18 4 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.3638541875407100e-004</threshold> + <left_val>0.5937399268150330</left_val> + <right_val>0.4664669036865234</right_val></_></_> + <_> + <!-- tree 76 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 8 8 12 -1.</_> + <_>0 8 4 6 2.</_> + <_>4 14 4 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0416674911975861</threshold> + <left_node>1</left_node> + <right_val>0.7021353244781494</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 7 8 6 -1.</_> + <_>1 7 4 3 2.</_> + <_>5 10 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.7763780243694782e-003</threshold> + <left_val>0.3222751021385193</left_val> + <right_val>0.5068395137786865</right_val></_></_> + <_> + <!-- tree 77 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 1 6 2 -1.</_> + <_>16 1 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.9170580673962831e-003</threshold> + <left_node>1</left_node> + <right_val>0.4717701077461243</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>15 0 4 4 -1.</_> + <_>17 0 2 2 2.</_> + <_>15 2 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.2789530814625323e-004</threshold> + <left_val>0.4509383141994476</left_val> + <right_val>0.5651162862777710</right_val></_></_></trees> + <stage_threshold>38.2360382080078130</stage_threshold> + <parent>14</parent> + <next>-1</next></_> + <_> + <!-- stage 16 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 1 4 11 -1.</_> + <_>3 1 2 11 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0117298001423478</threshold> + <left_val>0.3805224895477295</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 5 1 8 -1.</_> + <_>5 9 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1712179984897375e-003</threshold> + <left_val>0.3140017986297607</left_val> + <right_val>0.6858146190643311</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 7 6 1 -1.</_> + <_>9 7 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.3555096536874771e-003</threshold> + <left_node>1</left_node> + <right_val>0.6834673285484314</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 7 12 2 -1.</_> + <_>8 7 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6570610459893942e-003</threshold> + <left_val>0.2992472946643829</left_val> + <right_val>0.5475677847862244</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 4 4 4 -1.</_> + <_>8 6 4 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.3387809740379453e-003</threshold> + <left_node>1</left_node> + <right_val>0.2941406965255737</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 4 9 1 -1.</_> + <_>5 4 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7580550047568977e-004</threshold> + <left_val>0.3896977901458740</left_val> + <right_val>0.5872970819473267</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 12 2 8 -1.</_> + <_>9 16 2 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.9473248869180679e-003</threshold> + <left_val>0.3576571941375732</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 8 14 12 -1.</_> + <_>3 14 14 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.3220899105072021e-003</threshold> + <left_val>0.5232400894165039</left_val> + <right_val>0.3231087923049927</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 13 7 3 -1.</_> + <_>6 14 7 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.4366689659655094e-003</threshold> + <left_node>1</left_node> + <right_val>0.6715673208236694</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 9 6 3 -1.</_> + <_>7 9 2 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.1322889369912446e-004</threshold> + <left_val>0.5470541715621948</left_val> + <right_val>0.3863396048545837</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 1 6 3 -1.</_> + <_>12 2 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.8024631366133690e-003</threshold> + <left_val>0.2771460115909576</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 12 6 2 -1.</_> + <_>8 13 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.6611228501424193e-004</threshold> + <left_val>0.4689136147499085</left_val> + <right_val>0.5851963758468628</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 18 2 -1.</_> + <_>0 2 9 1 2.</_> + <_>9 3 9 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.2346500605344772e-003</threshold> + <left_val>0.2704397141933441</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 10 3 6 -1.</_> + <_>6 13 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4676499631605111e-005</threshold> + <left_val>0.5622550249099731</left_val> + <right_val>0.3579317033290863</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 0 6 6 -1.</_> + <_>14 0 3 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.7007937729358673e-003</threshold> + <left_val>0.4173871874809265</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>15 0 5 8 -1.</_> + <_>15 4 5 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.5320650786161423e-003</threshold> + <left_val>0.4195013046264648</left_val> + <right_val>0.5549468994140625</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 16 6 4 -1.</_> + <_>9 16 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0216164104640484</threshold> + <left_node>1</left_node> + <right_val>0.2857390940189362</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 11 14 4 -1.</_> + <_>2 11 7 2 2.</_> + <_>9 13 7 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.4567608963698149e-003</threshold> + <left_val>0.6024532914161682</left_val> + <right_val>0.4377507865428925</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 10 6 10 -1.</_> + <_>14 10 3 10 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0229143202304840</threshold> + <left_val>0.4689350128173828</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 8 10 12 -1.</_> + <_>14 8 5 6 2.</_> + <_>9 14 5 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.4328910987824202e-003</threshold> + <left_val>0.4664604961872101</left_val> + <right_val>0.5762562155723572</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 10 6 10 -1.</_> + <_>3 10 3 10 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.6510833352804184e-003</threshold> + <left_val>0.6381739974021912</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 8 10 12 -1.</_> + <_>1 8 5 6 2.</_> + <_>6 14 5 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4510039472952485e-003</threshold> + <left_val>0.3711487948894501</left_val> + <right_val>0.5530750751495361</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 3 6 1 -1.</_> + <_>11 3 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.8191719949245453e-003</threshold> + <left_val>0.5264362096786499</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 4 6 3 -1.</_> + <_>9 4 2 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.0798550394829363e-004</threshold> + <left_val>0.3730512857437134</left_val> + <right_val>0.5445731282234192</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 3 6 1 -1.</_> + <_>7 3 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.9962218143045902e-003</threshold> + <left_val>0.2438170015811920</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 5 6 3 -1.</_> + <_>6 5 2 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5010139577498194e-005</threshold> + <left_val>0.5324671268463135</left_val> + <right_val>0.3682988882064819</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 16 3 3 -1.</_> + <_>9 17 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.2428788729012012e-003</threshold> + <left_val>0.6481474041938782</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 14 6 3 -1.</_> + <_>8 15 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.1374982148408890e-003</threshold> + <left_val>0.4896158874034882</left_val> + <right_val>0.6558843255043030</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- 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.8254585862159729e-003</threshold> + <left_node>1</left_node> + <right_val>0.3613870143890381</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 12 2 3 -1.</_> + <_>4 13 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.4092212384566665e-004</threshold> + <left_val>0.5502895712852478</left_val> + <right_val>0.3632518053054810</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 16 6 3 -1.</_> + <_>12 17 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0125033501535654</threshold> + <left_val>0.2261132001876831</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 12 7 2 -1.</_> + <_>7 13 7 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.6759645491838455e-003</threshold> + <left_val>0.4987890124320984</left_val> + <right_val>0.6847196221351624</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 16 6 3 -1.</_> + <_>2 17 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0104167601093650</threshold> + <left_val>0.2446299046278000</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 7 16 6 -1.</_> + <_>0 10 16 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.7432460337877274e-003</threshold> + <left_val>0.3511525094509125</left_val> + <right_val>0.5399826765060425</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 3 3 -1.</_> + <_>10 7 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.2385691776871681e-003</threshold> + <left_val>0.6823673248291016</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 7 3 5 -1.</_> + <_>10 7 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0183258708566427</threshold> + <left_val>0.4891580045223236</left_val> + <right_val>0.7135618925094605</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 5 20 10 -1.</_> + <_>0 5 10 5 2.</_> + <_>10 10 10 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0243345405906439</threshold> + <left_val>0.3522521853446960</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 1 4 2 -1.</_> + <_>5 1 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.6469361404888332e-004</threshold> + <left_val>0.4049868881702423</left_val> + <right_val>0.5515825748443604</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- 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>3.4260009415447712e-003</threshold> + <left_node>1</left_node> + <right_val>0.4126769900321960</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>17 6 3 2 -1.</_> + <_>17 7 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.5827318895608187e-003</threshold> + <left_val>0.2899428904056549</left_val> + <right_val>0.5386431813240051</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- 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>1.0545699624344707e-003</threshold> + <left_node>1</left_node> + <right_val>0.3771344125270844</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 12 10 6 -1.</_> + <_>5 14 10 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.1257691383361816e-004</threshold> + <left_val>0.5827386975288391</left_val> + <right_val>0.4267556965351105</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 3 3 -1.</_> + <_>10 7 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.6589010376483202e-003</threshold> + <left_val>0.4688124954700470</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 3 2 6 -1.</_> + <_>11 3 1 3 2.</_> + <_>10 6 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.8598358407616615e-003</threshold> + <left_val>0.4853922128677368</left_val> + <right_val>0.6163644790649414</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 4 3 3 -1.</_> + <_>0 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.0638676881790161e-003</threshold> + <left_node>1</left_node> + <right_val>0.1749195009469986</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 16 8 4 -1.</_> + <_>3 16 4 2 2.</_> + <_>7 18 4 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.5898370705544949e-003</threshold> + <left_val>0.6826189756393433</left_val> + <right_val>0.4894070029258728</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 13 5 2 -1.</_> + <_>8 14 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.6368070868775249e-004</threshold> + <left_val>0.4614596068859100</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 7 4 12 -1.</_> + <_>8 11 4 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0625949501991272</threshold> + <left_val>0.5183017253875732</left_val> + <right_val>0.2686696052551270</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 9 2 2 -1.</_> + <_>6 9 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.9753207713365555e-003</threshold> + <left_val>0.1758466958999634</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 15 2 3 -1.</_> + <_>9 16 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.0880119409412146e-003</threshold> + <left_val>0.6369382143020630</left_val> + <right_val>0.4930044114589691</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 9 2 3 -1.</_> + <_>13 9 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.5644511748105288e-004</threshold> + <left_node>1</left_node> + <right_val>0.4139398932456970</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>14 0 6 17 -1.</_> + <_>16 0 2 17 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0317214615643024</threshold> + <left_val>0.6045557260513306</left_val> + <right_val>0.4816364049911499</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 10 2 2 -1.</_> + <_>6 10 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2898689601570368e-003</threshold> + <left_val>0.5450810790061951</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 9 9 1 -1.</_> + <_>5 9 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.8405163735151291e-003</threshold> + <left_val>0.2924000918865204</left_val> + <right_val>0.6699606180191040</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 11 2 3 -1.</_> + <_>9 12 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2237089686095715e-003</threshold> + <left_node>1</left_node> + <right_val>0.6282836794853210</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 11 6 3 -1.</_> + <_>7 12 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.4232585504651070e-003</threshold> + <left_val>0.5986570119857788</left_val> + <right_val>0.4852580130100250</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 6 3 2 -1.</_> + <_>0 7 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.2726322105154395e-004</threshold> + <left_val>0.3340049088001251</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 0 6 1 -1.</_> + <_>9 0 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.6842931769788265e-003</threshold> + <left_val>0.5168923735618591</left_val> + <right_val>0.2679480016231537</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 16 3 3 -1.</_> + <_>9 17 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0379579616710544e-003</threshold> + <left_val>0.5925791859626770</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 13 17 6 -1.</_> + <_>2 16 17 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.1342730447649956e-003</threshold> + <left_val>0.5437728166580200</left_val> + <right_val>0.4346800148487091</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 3 3 7 -1.</_> + <_>2 3 1 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4971119817346334e-003</threshold> + <left_val>0.4129500985145569</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 1 6 4 -1.</_> + <_>3 1 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5762320253998041e-003</threshold> + <left_val>0.4522874057292938</left_val> + <right_val>0.6556292176246643</right_val></_></_> + <_> + <!-- tree 31 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 1 6 5 -1.</_> + <_>14 1 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.7496247142553329e-003</threshold> + <left_val>0.4532034099102020</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>13 2 3 2 -1.</_> + <_>13 3 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.5103599121794105e-004</threshold> + <left_val>0.3785983920097351</left_val> + <right_val>0.5416975021362305</right_val></_></_> + <_> + <!-- tree 32 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 1 6 5 -1.</_> + <_>3 1 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0173255708068609</threshold> + <left_val>0.6884248256683350</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 3 2 6 -1.</_> + <_>2 5 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.3266440778970718e-003</threshold> + <left_val>0.3091326057910919</left_val> + <right_val>0.5243654847145081</right_val></_></_> + <_> + <!-- tree 33 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 10 3 2 -1.</_> + <_>9 11 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5157909729168750e-005</threshold> + <left_val>0.4765793979167938</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 13 4 3 -1.</_> + <_>8 14 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.8041470320895314e-003</threshold> + <left_val>0.4725385904312134</left_val> + <right_val>0.5716555118560791</right_val></_></_> + <_> + <!-- tree 34 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 3 3 1 -1.</_> + <_>7 3 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.0691560823470354e-003</threshold> + <left_node>1</left_node> + <right_val>0.2143359929323196</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 2 3 12 -1.</_> + <_>8 6 3 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.2225510444259271e-005</threshold> + <left_val>0.5653210282325745</left_val> + <right_val>0.4385111033916473</right_val></_></_> + <_> + <!-- tree 35 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 12 1 2 -1.</_> + <_>11 13 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0072169970953837e-004</threshold> + <left_node>1</left_node> + <right_val>0.5924776196479797</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 12 2 2 -1.</_> + <_>12 12 1 1 2.</_> + <_>11 13 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3573700562119484e-004</threshold> + <left_val>0.4573448896408081</left_val> + <right_val>0.5769382715225220</right_val></_></_> + <_> + <!-- tree 36 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 2 2 -1.</_> + <_>5 6 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.2137878527864814e-004</threshold> + <left_node>1</left_node> + <right_val>0.5992609262466431</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 4 1 3 -1.</_> + <_>5 5 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.0316581251099706e-004</threshold> + <left_val>0.3610081076622009</left_val> + <right_val>0.5049325823783875</right_val></_></_> + <_> + <!-- tree 37 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 11 16 4 -1.</_> + <_>11 11 8 2 2.</_> + <_>3 13 8 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0395824797451496</threshold> + <left_node>1</left_node> + <right_val>0.1538489013910294</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 10 20 3 -1.</_> + <_>0 11 20 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0475196801126003</threshold> + <left_val>0.5216140747070313</left_val> + <right_val>0.1428391039371491</right_val></_></_> + <_> + <!-- tree 38 --> + <_> + <!-- 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.0188717599958181</threshold> + <left_node>1</left_node> + <right_val>0.2825506925582886</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 2 4 2 -1.</_> + <_>4 3 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.9876459049992263e-004</threshold> + <left_val>0.4035016894340515</left_val> + <right_val>0.5437793135643005</right_val></_></_> + <_> + <!-- tree 39 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 6 2 2 -1.</_> + <_>13 6 1 1 2.</_> + <_>12 7 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.6556600136682391e-004</threshold> + <left_val>0.4668996930122376</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 11 6 6 -1.</_> + <_>12 13 6 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.7090610973536968e-003</threshold> + <left_val>0.5331354737281799</left_val> + <right_val>0.4136571884155273</right_val></_></_> + <_> + <!-- tree 40 --> + <_> + <!-- 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>-1.8931160448119044e-003</threshold> + <left_val>0.7155163288116455</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 4 4 16 -1.</_> + <_>8 4 2 16 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0130569497123361</threshold> + <left_val>0.3117899894714356</left_val> + <right_val>0.5208439826965332</right_val></_></_> + <_> + <!-- tree 41 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 18 3 2 -1.</_> + <_>11 19 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.9484119547996670e-004</threshold> + <left_node>1</left_node> + <right_val>0.4637658894062042</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 17 6 2 -1.</_> + <_>12 17 3 1 2.</_> + <_>9 18 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5093220099515747e-005</threshold> + <left_val>0.4561653137207031</left_val> + <right_val>0.5445234179496765</right_val></_></_> + <_> + <!-- tree 42 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 13 5 2 -1.</_> + <_>2 14 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.1617960202274844e-006</threshold> + <left_node>1</left_node> + <right_val>0.4193108081817627</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 15 2 2 -1.</_> + <_>3 16 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.0164679628796875e-004</threshold> + <left_val>0.5966237783432007</left_val> + <right_val>0.4100500047206879</right_val></_></_> + <_> + <!-- tree 43 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 3 3 -1.</_> + <_>10 7 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.4195181690156460e-003</threshold> + <left_val>0.4845055937767029</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 6 2 6 -1.</_> + <_>9 6 1 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.3984181508421898e-003</threshold> + <left_val>0.6206846237182617</left_val> + <right_val>0.4931209087371826</right_val></_></_> + <_> + <!-- tree 44 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 14 7 6 -1.</_> + <_>1 16 7 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.8031201846897602e-003</threshold> + <left_node>1</left_node> + <right_val>0.5282462835311890</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 1 2 11 -1.</_> + <_>9 1 1 11 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0107314297929406</threshold> + <left_val>0.9104834198951721</left_val> + <right_val>0.3455922007560730</right_val></_></_> + <_> + <!-- tree 45 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 2 4 -1.</_> + <_>9 7 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4246780192479491e-003</threshold> + <left_val>0.4708554148674011</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 10 2 1 -1.</_> + <_>11 10 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.2717568147927523e-005</threshold> + <left_val>0.5651623010635376</left_val> + <right_val>0.4731023907661438</right_val></_></_> + <_> + <!-- tree 46 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 3 3 9 -1.</_> + <_>1 3 1 9 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.4803409837186337e-003</threshold> + <left_node>1</left_node> + <right_val>0.6175886988639832</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 3 3 6 -1.</_> + <_>0 5 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.0789140146225691e-003</threshold> + <left_val>0.5139533281326294</left_val> + <right_val>0.3423087894916534</right_val></_></_> + <_> + <!-- tree 47 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 15 2 2 -1.</_> + <_>12 15 1 1 2.</_> + <_>11 16 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1310289846733212e-003</threshold> + <left_node>1</left_node> + <right_val>0.4918282032012940</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 14 2 2 -1.</_> + <_>12 14 1 1 2.</_> + <_>11 15 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0410690447315574e-003</threshold> + <left_val>0.5942087173461914</left_val> + <right_val>0.4923042953014374</right_val></_></_> + <_> + <!-- tree 48 --> + <_> + <!-- 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.1648540385067463e-003</threshold> + <left_node>1</left_node> + <right_val>0.6405271887779236</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 14 2 2 -1.</_> + <_>7 14 1 1 2.</_> + <_>8 15 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.0057362103834748e-004</threshold> + <left_val>0.4504396915435791</left_val> + <right_val>0.6192076802253723</right_val></_></_> + <_> + <!-- tree 49 --> + <_> + <!-- 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>6.8781538866460323e-003</threshold> + <left_val>0.5374813079833984</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 14 16 4 -1.</_> + <_>10 14 8 2 2.</_> + <_>2 16 8 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0352839007973671</threshold> + <left_val>0.2247101068496704</left_val> + <right_val>0.5217170715332031</right_val></_></_> + <_> + <!-- tree 50 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 8 2 2 -1.</_> + <_>9 9 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.3320200378075242e-003</threshold> + <left_val>0.2554703056812286</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 7 5 3 -1.</_> + <_>7 8 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.3177571129053831e-003</threshold> + <left_val>0.3792515993118286</left_val> + <right_val>0.5243226885795593</right_val></_></_> + <_> + <!-- tree 51 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 5 6 2 -1.</_> + <_>9 5 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1332940377760679e-004</threshold> + <left_val>0.3860337138175964</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 1 6 18 -1.</_> + <_>11 1 2 18 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0134679004549980</threshold> + <left_val>0.5380687713623047</left_val> + <right_val>0.4178363978862763</right_val></_></_> + <_> + <!-- tree 52 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 6 3 4 -1.</_> + <_>9 6 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.2829169863834977e-003</threshold> + <left_val>0.6133623123168945</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 5 2 4 -1.</_> + <_>8 5 1 2 2.</_> + <_>9 7 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.1571638323366642e-004</threshold> + <left_val>0.4028537869453430</left_val> + <right_val>0.5536851882934570</right_val></_></_> + <_> + <!-- tree 53 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 13 2 6 -1.</_> + <_>10 13 1 3 2.</_> + <_>9 16 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.9254198782145977e-003</threshold> + <left_val>0.5279921293258667</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 0 3 18 -1.</_> + <_>12 0 1 18 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0337805896997452</threshold> + <left_val>0.2334675043821335</left_val> + <right_val>0.5175911784172058</right_val></_></_> + <_> + <!-- tree 54 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 0 3 18 -1.</_> + <_>7 0 1 18 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0378537215292454</threshold> + <left_val>0.1074853017926216</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 15 4 2 -1.</_> + <_>7 15 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.0752900531515479e-004</threshold> + <left_val>0.5345929861068726</left_val> + <right_val>0.4198938012123108</right_val></_></_> + <_> + <!-- tree 55 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 9 18 1 -1.</_> + <_>7 9 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.1193809118121862e-003</threshold> + <left_val>0.3855825066566467</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 0 20 3 -1.</_> + <_>0 1 20 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0157149694859982</threshold> + <left_val>0.3335190117359161</left_val> + <right_val>0.5263202190399170</right_val></_></_> + <_> + <!-- tree 56 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 6 2 4 -1.</_> + <_>10 6 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.8525702701881528e-004</threshold> + <left_val>0.5860397219657898</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 10 6 2 -1.</_> + <_>8 10 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.8750501223839819e-004</threshold> + <left_val>0.5437784790992737</left_val> + <right_val>0.3716104924678803</right_val></_></_> + <_> + <!-- tree 57 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 7 20 1 -1.</_> + <_>0 7 10 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0280168596655130</threshold> + <left_node>1</left_node> + <right_val>0.3330754935741425</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 3 5 4 -1.</_> + <_>11 5 5 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.9018839811906219e-003</threshold> + <left_val>0.5366597771644592</left_val> + <right_val>0.4693793952465057</right_val></_></_> + <_> + <!-- tree 58 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 10 1 -1.</_> + <_>10 7 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0206475593149662</threshold> + <left_node>1</left_node> + <right_val>0.1006956025958061</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 10 3 3 -1.</_> + <_>8 11 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.3002571910619736e-003</threshold> + <left_val>0.4816035926342011</left_val> + <right_val>0.6215677261352539</right_val></_></_> + <_> + <!-- tree 59 --> + <_> + <!-- 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>0.0134591404348612</threshold> + <left_val>0.5461953878402710</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 0 9 10 -1.</_> + <_>11 5 9 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0103200403973460</threshold> + <left_val>0.4578453004360199</left_val> + <right_val>0.5419309735298157</right_val></_></_> + <_> + <!-- tree 60 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 8 18 -1.</_> + <_>4 2 4 18 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.3199074864387512</threshold> + <left_node>1</left_node> + <right_val>0.2008046954870224</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 0 2 6 -1.</_> + <_>0 2 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.2198798665776849e-004</threshold> + <left_val>0.5193281173706055</left_val> + <right_val>0.3912194073200226</right_val></_></_> + <_> + <!-- tree 61 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 0 9 2 -1.</_> + <_>6 1 9 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.1852539288811386e-004</threshold> + <left_val>0.4299744069576263</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 1 12 2 -1.</_> + <_>4 2 12 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.5891108564101160e-004</threshold> + <left_val>0.4344502985477448</left_val> + <right_val>0.5531973838806152</right_val></_></_> + <_> + <!-- tree 62 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 1 16 14 -1.</_> + <_>2 8 16 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.2099243998527527</threshold> + <left_val>0.1075721010565758</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 1 8 12 -1.</_> + <_>5 7 8 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.9328152090311050e-003</threshold> + <left_val>0.5762796998023987</left_val> + <right_val>0.4574643969535828</right_val></_></_> + <_> + <!-- tree 63 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 11 2 2 -1.</_> + <_>9 12 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.3409130517393351e-003</threshold> + <left_node>1</left_node> + <right_val>0.7476807832717896</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 10 5 6 -1.</_> + <_>9 12 5 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.7120270319283009e-003</threshold> + <left_val>0.5261765122413635</left_val> + <right_val>0.4505550861358643</right_val></_></_> + <_> + <!-- tree 64 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 0 13 8 -1.</_> + <_>3 4 13 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0287131909281015</threshold> + <left_val>0.4407103061676025</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 7 5 8 -1.</_> + <_>6 11 5 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.6156550738960505e-003</threshold> + <left_val>0.4244270920753479</left_val> + <right_val>0.6892976760864258</right_val></_></_> + <_> + <!-- tree 65 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 2 3 -1.</_> + <_>9 6 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0135589698329568</threshold> + <left_val>0.1252267956733704</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 8 8 3 -1.</_> + <_>6 9 8 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.0331799644045532e-004</threshold> + <left_val>0.4077791869640350</left_val> + <right_val>0.5442817807197571</right_val></_></_> + <_> + <!-- tree 66 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 2 7 6 -1.</_> + <_>2 5 7 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.5601762142032385e-004</threshold> + <left_val>0.5378003716468811</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 1 14 4 -1.</_> + <_>2 1 7 2 2.</_> + <_>9 3 7 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.4025330785661936e-003</threshold> + <left_val>0.3166579902172089</left_val> + <right_val>0.5285738110542297</right_val></_></_> + <_> + <!-- tree 67 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 14 1 3 -1.</_> + <_>11 15 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.4089901018887758e-003</threshold> + <left_node>1</left_node> + <right_val>0.4905214905738831</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 15 8 2 -1.</_> + <_>6 16 8 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.0019602319225669e-004</threshold> + <left_val>0.4522736072540283</left_val> + <right_val>0.5580614209175110</right_val></_></_> + <_> + <!-- tree 68 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 1 3 -1.</_> + <_>8 15 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1901070140302181e-003</threshold> + <left_node>1</left_node> + <right_val>0.6612681746482849</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 11 2 8 -1.</_> + <_>8 15 2 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.3745369873940945e-003</threshold> + <left_val>0.5107765197753906</left_val> + <right_val>0.3386929929256439</right_val></_></_> + <_> + <!-- tree 69 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 15 8 2 -1.</_> + <_>6 16 8 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.0019602319225669e-004</threshold> + <left_node>1</left_node> + <right_val>0.5707560181617737</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 16 8 3 -1.</_> + <_>7 17 8 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0173460692167282</threshold> + <left_val>0.5016021132469177</left_val> + <right_val>0.6306459903717041</right_val></_></_> + <_> + <!-- tree 70 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 16 2 2 -1.</_> + <_>0 17 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.9568449351936579e-003</threshold> + <left_val>0.3017806112766266</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 16 8 4 -1.</_> + <_>1 16 4 2 2.</_> + <_>5 18 4 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0112290196120739</threshold> + <left_val>0.6293851137161255</left_val> + <right_val>0.4520488977432251</right_val></_></_> + <_> + <!-- tree 71 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 9 16 3 -1.</_> + <_>2 10 16 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.6608388870954514e-003</threshold> + <left_val>0.3344007134437561</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>13 11 2 4 -1.</_> + <_>13 11 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0116151003167033</threshold> + <left_val>0.2825379073619843</left_val> + <right_val>0.5150970816612244</right_val></_></_> + <_> + <!-- tree 72 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 13 16 6 -1.</_> + <_>0 15 16 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0952486023306847</threshold> + <left_val>0.1398265063762665</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 11 2 4 -1.</_> + <_>6 11 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.3701781220734119e-003</threshold> + <left_val>0.5293998718261719</left_val> + <right_val>0.2331728041172028</right_val></_></_> + <_> + <!-- tree 73 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 2 2 18 -1.</_> + <_>19 2 1 9 2.</_> + <_>18 11 1 9 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0149539001286030</threshold> + <left_node>1</left_node> + <right_val>0.4940465986728668</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>19 7 1 9 -1.</_> + <_>19 10 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.7038792874664068e-004</threshold> + <left_val>0.5466570854187012</left_val> + <right_val>0.4626767933368683</right_val></_></_> + <_> + <!-- tree 74 --> + <_> + <!-- 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>5.8516198769211769e-003</threshold> + <left_node>1</left_node> + <right_val>0.6270040869712830</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 7 1 9 -1.</_> + <_>0 10 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1150549582671374e-004</threshold> + <left_val>0.5508140921592712</left_val> + <right_val>0.4061872959136963</right_val></_></_> + <_> + <!-- tree 75 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 12 2 2 -1.</_> + <_>14 13 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.9679190346505493e-006</threshold> + <left_node>1</left_node> + <right_val>0.4096567928791046</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 14 2 3 -1.</_> + <_>11 15 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.9677387839183211e-004</threshold> + <left_val>0.5615556836128235</left_val> + <right_val>0.4666886031627655</right_val></_></_> + <_> + <!-- tree 76 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 8 6 2 -1.</_> + <_>7 9 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0194594804197550</threshold> + <left_node>1</left_node> + <right_val>0.2311480939388275</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 12 4 6 -1.</_> + <_>7 12 2 3 2.</_> + <_>9 15 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0111608300358057</threshold> + <left_val>0.3087011873722076</left_val> + <right_val>0.5514662265777588</right_val></_></_> + <_> + <!-- tree 77 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 13 5 3 -1.</_> + <_>8 14 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0140561498701572</threshold> + <left_node>1</left_node> + <right_val>0.7005056142807007</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 14 2 2 -1.</_> + <_>13 14 1 1 2.</_> + <_>12 15 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.2958350493572652e-004</threshold> + <left_val>0.5797485709190369</left_val> + <right_val>0.4691650867462158</right_val></_></_> + <_> + <!-- tree 78 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 13 6 3 -1.</_> + <_>7 14 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.4636420682072639e-003</threshold> + <left_val>0.5928595066070557</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 13 5 2 -1.</_> + <_>7 14 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.8881669247057289e-005</threshold> + <left_val>0.3741397857666016</left_val> + <right_val>0.5170168876647949</right_val></_></_> + <_> + <!-- tree 79 --> + <_> + <!-- 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>6.6343429498374462e-003</threshold> + <left_val>0.5414987802505493</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 0 6 6 -1.</_> + <_>9 0 2 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0452634096145630</threshold> + <left_val>0.5180327296257019</left_val> + <right_val>0.1529684066772461</right_val></_></_> + <_> + <!-- tree 80 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 1 6 3 -1.</_> + <_>7 2 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.0646127462387085e-003</threshold> + <left_val>0.2515468001365662</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 12 6 2 -1.</_> + <_>0 13 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.7389548853971064e-004</threshold> + <left_val>0.5121998786926270</left_val> + <right_val>0.3725948929786682</right_val></_></_> + <_> + <!-- tree 81 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 3 11 2 -1.</_> + <_>6 4 11 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4877359717502259e-005</threshold> + <left_node>1</left_node> + <right_val>0.5532435774803162</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 0 8 6 -1.</_> + <_>16 0 4 3 2.</_> + <_>12 3 4 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0243211593478918</threshold> + <left_val>0.4960766136646271</left_val> + <right_val>0.5983315110206604</right_val></_></_> + <_> + <!-- tree 82 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 12 1 2 -1.</_> + <_>8 13 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.9931396865285933e-005</threshold> + <left_val>0.4163953065872192</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 8 1 12 -1.</_> + <_>8 12 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.6287760119885206e-003</threshold> + <left_val>0.5880144834518433</left_val> + <right_val>0.3399662971496582</right_val></_></_> + <_> + <!-- tree 83 --> + <_> + <!-- 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>3.8190539926290512e-003</threshold> + <left_node>1</left_node> + <right_val>0.7846621274948120</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 7 3 13 -1.</_> + <_>13 7 1 13 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0259891506284475</threshold> + <left_val>0.3288114070892334</left_val> + <right_val>0.5155087709426880</right_val></_></_> + <_> + <!-- tree 84 --> + <_> + <!-- 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>1.2062400346621871e-003</threshold> + <left_val>0.4596059918403626</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 13 1 3 -1.</_> + <_>3 14 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5557400183752179e-003</threshold> + <left_val>0.3126986920833588</left_val> + <right_val>0.7183399200439453</right_val></_></_> + <_> + <!-- tree 85 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 18 3 2 -1.</_> + <_>11 18 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.2691930644214153e-003</threshold> + <left_node>1</left_node> + <right_val>0.5274006128311157</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 11 2 1 -1.</_> + <_>11 11 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.3287249496206641e-004</threshold> + <left_val>0.4878666102886200</left_val> + <right_val>0.5615152716636658</right_val></_></_> + <_> + <!-- tree 86 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 10 5 9 -1.</_> + <_>1 13 5 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.5999699980020523e-003</threshold> + <left_node>1</left_node> + <right_val>0.5160812139511108</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 8 6 4 -1.</_> + <_>6 8 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0104961898177862</threshold> + <left_val>0.5701614022254944</left_val> + <right_val>0.3204850852489471</right_val></_></_> + <_> + <!-- tree 87 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 12 1 4 -1.</_> + <_>13 14 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4814930182183161e-005</threshold> + <left_val>0.5538837909698486</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 3 4 14 -1.</_> + <_>13 3 2 7 2.</_> + <_>11 10 2 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.4287078566849232e-004</threshold> + <left_val>0.5349429249763489</left_val> + <right_val>0.4472151100635529</right_val></_></_> + <_> + <!-- tree 88 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 12 1 4 -1.</_> + <_>6 14 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.8891949730459601e-004</threshold> + <left_val>0.5012837052345276</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 3 4 14 -1.</_> + <_>5 3 2 7 2.</_> + <_>7 10 2 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.0413521975278854e-003</threshold> + <left_val>0.2562935948371887</left_val> + <right_val>0.4503383040428162</right_val></_></_> + <_> + <!-- tree 89 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 18 3 2 -1.</_> + <_>11 18 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.9534705728292465e-003</threshold> + <left_node>1</left_node> + <right_val>0.2630499899387360</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 12 3 3 -1.</_> + <_>9 13 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.7908999472856522e-003</threshold> + <left_val>0.5756508708000183</left_val> + <right_val>0.4854863882064819</right_val></_></_> + <_> + <!-- tree 90 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 2 12 6 -1.</_> + <_>2 2 6 3 2.</_> + <_>8 5 6 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.2857100013643503e-003</threshold> + <left_node>1</left_node> + <right_val>0.4084751904010773</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 6 6 2 -1.</_> + <_>9 6 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.7063008211553097e-004</threshold> + <left_val>0.4073356091976166</left_val> + <right_val>0.5920240879058838</right_val></_></_></trees> + <stage_threshold>44.6829681396484380</stage_threshold> + <parent>15</parent> + <next>-1</next></_> + <_> + <!-- stage 17 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 0 18 12 -1.</_> + <_>7 0 6 12 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0630219429731369</threshold> + <left_val>0.3419382870197296</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 7 6 4 -1.</_> + <_>5 7 3 2 2.</_> + <_>8 9 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.8374609537422657e-003</threshold> + <left_val>0.6829563975334168</left_val> + <right_val>0.4404523074626923</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 10 4 -1.</_> + <_>5 9 10 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0464619509875774</threshold> + <left_val>0.4391745030879974</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 7 6 4 -1.</_> + <_>9 7 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0291525404900312</threshold> + <left_val>0.4601063132286072</left_val> + <right_val>0.6357936859130859</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 2 2 -1.</_> + <_>9 6 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4000290320836939e-005</threshold> + <left_node>1</left_node> + <right_val>0.3730010092258453</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 9 2 2 -1.</_> + <_>9 10 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.2757079675793648e-003</threshold> + <left_val>0.3093824088573456</left_val> + <right_val>0.5901370048522949</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 17 8 3 -1.</_> + <_>6 18 8 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3596529606729746e-003</threshold> + <left_val>0.4337565004825592</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 17 6 2 -1.</_> + <_>12 17 3 1 2.</_> + <_>9 18 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7991929780691862e-004</threshold> + <left_val>0.4217503964900971</left_val> + <right_val>0.5846847891807556</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 12 2 2 -1.</_> + <_>4 13 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4166639630275313e-005</threshold> + <left_node>1</left_node> + <right_val>0.4084691107273102</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 12 9 2 -1.</_> + <_>3 13 9 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.0252390539972112e-005</threshold> + <left_val>0.5087286829948425</left_val> + <right_val>0.7277184128761292</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 3 6 1 -1.</_> + <_>10 3 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.4320368692278862e-003</threshold> + <left_node>1</left_node> + <right_val>0.2967903017997742</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 3 4 6 -1.</_> + <_>11 3 2 3 2.</_> + <_>9 6 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.6682319953106344e-004</threshold> + <left_val>0.4110462963581085</left_val> + <right_val>0.5581219792366028</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 3 6 5 -1.</_> + <_>3 3 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.7436279021203518e-003</threshold> + <left_val>0.4287309944629669</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 0 2 18 -1.</_> + <_>2 6 2 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.2019240316003561e-003</threshold> + <left_val>0.4266195893287659</left_val> + <right_val>0.6444045901298523</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 2 4 9 -1.</_> + <_>14 5 4 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.7637941790744662e-004</threshold> + <left_node>1</left_node> + <right_val>0.4084824919700623</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 18 3 2 -1.</_> + <_>11 18 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.7901920732110739e-003</threshold> + <left_val>0.3181920945644379</left_val> + <right_val>0.5230693221092224</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 2 4 9 -1.</_> + <_>2 5 4 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.8914109356701374e-003</threshold> + <left_node>1</left_node> + <right_val>0.3548356890678406</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 18 3 2 -1.</_> + <_>8 18 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.6459292061626911e-003</threshold> + <left_val>0.5610597729682922</left_val> + <right_val>0.2693848907947540</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 14 3 3 -1.</_> + <_>10 15 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.8799369037151337e-003</threshold> + <left_val>0.6235408186912537</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 12 2 6 -1.</_> + <_>10 15 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0181474704295397</threshold> + <left_val>0.2861981987953186</left_val> + <right_val>0.5226848125457764</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 5 3 6 -1.</_> + <_>7 7 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1409220314817503e-004</threshold> + <left_node>1</left_node> + <right_val>0.3257833123207092</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 3 6 2 -1.</_> + <_>3 4 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.4334272863343358e-004</threshold> + <left_val>0.3882969021797180</left_val> + <right_val>0.5341166257858276</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 4 7 3 -1.</_> + <_>8 5 7 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.7602489572018385e-003</threshold> + <left_val>0.6353965997695923</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>13 6 2 3 -1.</_> + <_>13 7 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.9730569329112768e-003</threshold> + <left_val>0.5880761146545410</left_val> + <right_val>0.4593090116977692</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 8 2 12 -1.</_> + <_>8 12 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.4565239436924458e-003</threshold> + <left_node>1</left_node> + <right_val>0.3134010136127472</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 4 8 14 -1.</_> + <_>5 4 4 7 2.</_> + <_>9 11 4 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.9392010290175676e-004</threshold> + <left_val>0.5277131795883179</left_val> + <right_val>0.3604106903076172</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 1 20 8 -1.</_> + <_>10 1 10 4 2.</_> + <_>0 5 10 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0786430165171623</threshold> + <left_val>0.5290341973304749</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 0 12 2 -1.</_> + <_>4 1 12 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.5276869572699070e-003</threshold> + <left_val>0.4654479920864105</left_val> + <right_val>0.6044905185699463</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 1 20 8 -1.</_> + <_>0 1 10 4 2.</_> + <_>10 5 10 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0787167996168137</threshold> + <left_val>0.2541126906871796</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 0 12 2 -1.</_> + <_>4 1 12 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.7298499159514904e-003</threshold> + <left_val>0.4366919100284576</left_val> + <right_val>0.5822886228561401</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 6 3 -1.</_> + <_>9 5 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.2386557692661881e-004</threshold> + <left_node>1</left_node> + <right_val>0.5472692251205444</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 13 10 6 -1.</_> + <_>8 15 10 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0852672308683395</threshold> + <left_val>0.1461607962846756</left_val> + <right_val>0.5181810855865479</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 6 3 -1.</_> + <_>8 5 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0409811101853848</threshold> + <left_node>1</left_node> + <right_val>0.1270135045051575</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 3 6 1 -1.</_> + <_>8 3 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.7135749161243439e-003</threshold> + <left_val>0.4832684993743897</left_val> + <right_val>0.2223578989505768</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 18 9 2 -1.</_> + <_>14 18 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.8663940764963627e-003</threshold> + <left_val>0.5918928980827332</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>13 11 6 7 -1.</_> + <_>13 11 3 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0145596396178007</threshold> + <left_val>0.4761506915092468</left_val> + <right_val>0.5727223753929138</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 6 12 10 -1.</_> + <_>4 6 6 5 2.</_> + <_>10 11 6 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0100643103942275</threshold> + <left_val>0.3636730909347534</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 17 3 3 -1.</_> + <_>9 17 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.6274080630391836e-003</threshold> + <left_val>0.5271731019020081</left_val> + <right_val>0.2740525007247925</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 18 9 2 -1.</_> + <_>14 18 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.3421540390700102e-003</threshold> + <left_val>0.5497784018516541</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>13 11 6 8 -1.</_> + <_>13 11 3 8 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0246864091604948</threshold> + <left_val>0.6059895157814026</left_val> + <right_val>0.4960314035415649</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 16 2 2 -1.</_> + <_>4 17 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.9456120207905769e-004</threshold> + <left_node>1</left_node> + <right_val>0.3769465088844299</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 15 4 4 -1.</_> + <_>7 17 4 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.1714211218059063e-004</threshold> + <left_val>0.4062362015247345</left_val> + <right_val>0.5668215155601502</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 4 3 3 -1.</_> + <_>12 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.0793990697711706e-003</threshold> + <left_val>0.4618656933307648</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>13 6 2 3 -1.</_> + <_>13 7 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7982709687203169e-003</threshold> + <left_val>0.4867505133152008</left_val> + <right_val>0.6518449783325195</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 11 6 1 -1.</_> + <_>7 11 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.2287059982772917e-004</threshold> + <left_val>0.5677595734596252</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 10 3 1 -1.</_> + <_>8 10 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.2623921288177371e-004</threshold> + <left_val>0.3710733950138092</left_val> + <right_val>0.5676605105400085</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 12 20 4 -1.</_> + <_>0 14 20 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0667926818132401</threshold> + <left_val>0.2511521875858307</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 2 3 2 -1.</_> + <_>10 3 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.4869889710098505e-003</threshold> + <left_val>0.3886750936508179</left_val> + <right_val>0.5262253880500794</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 4 3 3 -1.</_> + <_>5 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.0454870797693729e-003</threshold> + <left_val>0.6557472944259644</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 5 4 3 -1.</_> + <_>5 6 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.8297587782144547e-003</threshold> + <left_val>0.5934106111526489</left_val> + <right_val>0.4285922050476074</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 8 4 3 -1.</_> + <_>8 9 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0722599690780044e-003</threshold> + <left_node>1</left_node> + <right_val>0.5426058769226074</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 4 2 12 -1.</_> + <_>10 8 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.7901195511221886e-003</threshold> + <left_val>0.5351303219795227</left_val> + <right_val>0.4834277927875519</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 3 4 3 -1.</_> + <_>0 4 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.1750381030142307e-003</threshold> + <left_val>0.2067168951034546</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 3 2 3 -1.</_> + <_>1 4 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1251230025663972e-003</threshold> + <left_val>0.5112252235412598</left_val> + <right_val>0.3468714058399200</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 1 4 11 -1.</_> + <_>16 1 2 11 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0106347100809217</threshold> + <left_val>0.4479008018970490</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>18 2 2 16 -1.</_> + <_>19 2 1 8 2.</_> + <_>18 10 1 8 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0117632197216153</threshold> + <left_val>0.6253901720046997</left_val> + <right_val>0.4968987107276917</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 8 6 12 -1.</_> + <_>3 8 2 12 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0923240631818771</threshold> + <left_node>1</left_node> + <right_val>0.2031303942203522</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 2 6 2 -1.</_> + <_>7 2 3 1 2.</_> + <_>10 3 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.8991080578416586e-003</threshold> + <left_val>0.5618721842765808</left_val> + <right_val>0.4046572148799896</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- 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>-0.0105103403329849</threshold> + <left_node>1</left_node> + <right_val>0.4943264126777649</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 6 6 2 -1.</_> + <_>12 6 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.4531312566250563e-004</threshold> + <left_val>0.5613427758216858</left_val> + <right_val>0.3845331966876984</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- 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.0041000619530678e-003</threshold> + <left_node>1</left_node> + <right_val>0.7759842276573181</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 3 3 5 -1.</_> + <_>2 3 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.8110528625547886e-003</threshold> + <left_val>0.4624733030796051</left_val> + <right_val>0.6286277174949646</right_val></_></_> + <_> + <!-- tree 31 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 3 4 6 -1.</_> + <_>16 5 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0279185809195042</threshold> + <left_val>0.2409314066171646</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 6 4 3 -1.</_> + <_>8 7 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1739399526268244e-003</threshold> + <left_val>0.5345504879951477</left_val> + <right_val>0.3507958054542542</right_val></_></_> + <_> + <!-- tree 32 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 1 3 -1.</_> + <_>8 15 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.0639587678015232e-003</threshold> + <left_val>0.6647101044654846</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 11 1 2 -1.</_> + <_>4 12 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.0017139185220003e-004</threshold> + <left_val>0.4998509883880615</left_val> + <right_val>0.3022165000438690</right_val></_></_> + <_> + <!-- tree 33 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 6 3 -1.</_> + <_>8 15 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.9214770291000605e-003</threshold> + <left_node>1</left_node> + <right_val>0.5919150710105896</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 15 7 3 -1.</_> + <_>7 16 7 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0138608301058412</threshold> + <left_val>0.6351767778396606</left_val> + <right_val>0.4993310868740082</right_val></_></_> + <_> + <!-- tree 34 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 12 2 8 -1.</_> + <_>9 16 2 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0230068508535624</threshold> + <left_node>1</left_node> + <right_val>0.1902336031198502</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 6 6 2 -1.</_> + <_>6 6 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.3857929734513164e-003</threshold> + <left_val>0.5253369212150574</left_val> + <right_val>0.3985860049724579</right_val></_></_> + <_> + <!-- tree 35 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 7 4 2 -1.</_> + <_>12 8 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2637410545721650e-003</threshold> + <left_val>0.4666104018688202</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 3 13 10 -1.</_> + <_>5 8 13 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0146752102300525</threshold> + <left_val>0.3823164999485016</left_val> + <right_val>0.5326632857322693</right_val></_></_> + <_> + <!-- tree 36 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 7 4 2 -1.</_> + <_>4 8 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.9535070061683655e-003</threshold> + <left_val>0.7063655853271484</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 8 16 2 -1.</_> + <_>0 8 8 1 2.</_> + <_>8 9 8 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.7189770005643368e-003</threshold> + <left_val>0.3813462853431702</left_val> + <right_val>0.5246735215187073</right_val></_></_> + <_> + <!-- tree 37 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 8 2 5 -1.</_> + <_>11 8 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.2484089499339461e-004</threshold> + <left_node>1</left_node> + <right_val>0.4791638851165772</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 0 6 13 -1.</_> + <_>10 0 3 13 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.5248658433556557e-004</threshold> + <left_val>0.4491218030452728</left_val> + <right_val>0.5370901226997376</right_val></_></_> + <_> + <!-- tree 38 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 6 4 2 -1.</_> + <_>1 7 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.9034568518400192e-003</threshold> + <left_node>1</left_node> + <right_val>0.2076473981142044</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 3 2 1 -1.</_> + <_>5 3 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4895649655954912e-005</threshold> + <left_val>0.4447635114192963</left_val> + <right_val>0.5667163133621216</right_val></_></_> + <_> + <!-- tree 39 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 8 2 5 -1.</_> + <_>11 8 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.7091601300053298e-004</threshold> + <left_val>0.5465071201324463</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 10 4 8 -1.</_> + <_>12 10 2 8 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.3084810022264719e-004</threshold> + <left_val>0.5493261814117432</left_val> + <right_val>0.4580708146095276</right_val></_></_> + <_> + <!-- tree 40 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 8 2 5 -1.</_> + <_>8 8 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.3893961487337947e-004</threshold> + <left_val>0.5501571893692017</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 10 4 8 -1.</_> + <_>6 10 2 8 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.3733746830839664e-005</threshold> + <left_val>0.5085790753364563</left_val> + <right_val>0.3305698037147522</right_val></_></_> + <_> + <!-- tree 41 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 7 9 12 -1.</_> + <_>9 7 3 12 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.8991485536098480e-003</threshold> + <left_val>0.4276469051837921</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 13 2 3 -1.</_> + <_>11 13 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0102533502504230</threshold> + <left_val>0.1123218014836311</left_val> + <right_val>0.5152723193168640</right_val></_></_> + <_> + <!-- tree 42 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 10 6 10 -1.</_> + <_>10 10 3 10 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0596374906599522</threshold> + <left_val>0.7386772036552429</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 11 4 8 -1.</_> + <_>8 11 2 4 2.</_> + <_>10 15 2 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0217071995139122</threshold> + <left_val>0.4996291995048523</left_val> + <right_val>0.1339413970708847</right_val></_></_> + <_> + <!-- tree 43 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 1 4 11 -1.</_> + <_>16 1 2 11 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.9107045680284500e-003</threshold> + <left_val>0.4679012000560761</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>18 2 2 4 -1.</_> + <_>18 2 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0109983002766967</threshold> + <left_val>0.6928656101226807</left_val> + <right_val>0.5012068152427673</right_val></_></_> + <_> + <!-- tree 44 --> + <_> + <!-- 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>7.4608891736716032e-004</threshold> + <left_node>1</left_node> + <right_val>0.5833582282066345</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 4 1 3 -1.</_> + <_>5 5 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.9539171373471618e-004</threshold> + <left_val>0.3826391100883484</left_val> + <right_val>0.5566350817680359</right_val></_></_> + <_> + <!-- tree 45 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 1 4 14 -1.</_> + <_>11 1 2 14 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0500541292130947</threshold> + <left_node>1</left_node> + <right_val>0.3002721071243286</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 2 12 3 -1.</_> + <_>8 2 4 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.2330660186707973e-003</threshold> + <left_val>0.5908042788505554</left_val> + <right_val>0.5000870823860169</right_val></_></_> + <_> + <!-- tree 46 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 1 4 14 -1.</_> + <_>7 1 2 14 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.6863380335271358e-003</threshold> + <left_val>0.3975034952163696</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 3 6 2 -1.</_> + <_>9 3 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0195849463343620e-003</threshold> + <left_val>0.3697685897350311</left_val> + <right_val>0.5756192803382874</right_val></_></_> + <_> + <!-- tree 47 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 0 18 4 -1.</_> + <_>8 0 6 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0202049203217030</threshold> + <left_val>0.6375268101692200</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 5 2 10 -1.</_> + <_>9 10 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1340379025787115e-003</threshold> + <left_val>0.5363265872001648</left_val> + <right_val>0.4433170855045319</right_val></_></_> + <_> + <!-- tree 48 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 6 3 4 -1.</_> + <_>9 6 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.8348889425396919e-003</threshold> + <left_val>0.5828999280929565</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 5 9 11 -1.</_> + <_>8 5 3 11 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.9489468112587929e-003</threshold> + <left_val>0.2680670917034149</left_val> + <right_val>0.4642885923385620</right_val></_></_> + <_> + <!-- tree 49 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 6 3 5 -1.</_> + <_>11 6 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.3030120064504445e-004</threshold> + <left_val>0.5475320219993591</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 9 6 5 -1.</_> + <_>8 9 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.0581009127199650e-003</threshold> + <left_val>0.5320833921432495</left_val> + <right_val>0.4646492898464203</right_val></_></_> + <_> + <!-- tree 50 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 6 3 5 -1.</_> + <_>8 6 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.1950011402368546e-004</threshold> + <left_val>0.5232744812965393</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 10 6 3 -1.</_> + <_>9 10 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.8620947422459722e-004</threshold> + <left_val>0.4935086071491242</left_val> + <right_val>0.3103117942810059</right_val></_></_> + <_> + <!-- tree 51 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 0 3 7 -1.</_> + <_>11 0 1 7 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.4936267919838428e-003</threshold> + <left_val>0.2883046865463257</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 3 20 12 -1.</_> + <_>0 9 20 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0156829301267862</threshold> + <left_val>0.3640313148498535</left_val> + <right_val>0.5368754863739014</right_val></_></_> + <_> + <!-- tree 52 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 2 2 -1.</_> + <_>10 7 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.2649750355631113e-003</threshold> + <left_val>0.6468631029129028</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 9 4 1 -1.</_> + <_>7 9 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.8463930832222104e-004</threshold> + <left_val>0.5259659886360169</left_val> + <right_val>0.3831427991390228</right_val></_></_> + <_> + <!-- tree 53 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 13 3 2 -1.</_> + <_>13 14 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.4492390006780624e-003</threshold> + <left_node>1</left_node> + <right_val>0.2086818963289261</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>16 9 4 6 -1.</_> + <_>16 9 2 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0231183208525181</threshold> + <left_val>0.4978533089160919</left_val> + <right_val>0.5961257219314575</right_val></_></_> + <_> + <!-- tree 54 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 15 6 3 -1.</_> + <_>7 16 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.0835159812122583e-003</threshold> + <left_node>1</left_node> + <right_val>0.5746421813964844</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 16 7 3 -1.</_> + <_>6 17 7 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1513150529935956e-003</threshold> + <left_val>0.3586845099925995</left_val> + <right_val>0.5363473892211914</right_val></_></_> + <_> + <!-- tree 55 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 14 9 6 -1.</_> + <_>11 16 9 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0361047089099884</threshold> + <left_node>1</left_node> + <right_val>0.2833136916160584</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>19 14 1 3 -1.</_> + <_>19 15 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.6256198654882610e-004</threshold> + <left_val>0.5477722287178040</left_val> + <right_val>0.4110532104969025</right_val></_></_> + <_> + <!-- tree 56 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 9 6 6 -1.</_> + <_>3 9 3 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.4635469783097506e-003</threshold> + <left_val>0.5990386009216309</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 19 9 1 -1.</_> + <_>3 19 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.8796829283237457e-003</threshold> + <left_val>0.5725253224372864</left_val> + <right_val>0.4149512052536011</right_val></_></_> + <_> + <!-- tree 57 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 14 9 6 -1.</_> + <_>11 16 9 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.1119500100612640e-003</threshold> + <left_node>1</left_node> + <right_val>0.5396351814270020</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 12 6 6 -1.</_> + <_>12 14 6 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.5932079665362835e-003</threshold> + <left_val>0.5379704236984253</left_val> + <right_val>0.3891302943229675</right_val></_></_> + <_> + <!-- tree 58 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 14 8 6 -1.</_> + <_>1 16 8 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.0014740340411663e-003</threshold> + <left_node>1</left_node> + <right_val>0.3714671134948731</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 1 3 2 -1.</_> + <_>9 1 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.0169539432972670e-004</threshold> + <left_val>0.5529567003250122</left_val> + <right_val>0.3755804896354675</right_val></_></_> + <_> + <!-- tree 59 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 2 2 4 -1.</_> + <_>18 2 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.6652329191565514e-003</threshold> + <left_node>1</left_node> + <right_val>0.5025773048400879</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>14 0 6 3 -1.</_> + <_>16 0 2 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.7315050829201937e-003</threshold> + <left_val>0.5850322246551514</left_val> + <right_val>0.4617573916912079</right_val></_></_> + <_> + <!-- tree 60 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 2 4 -1.</_> + <_>1 2 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3301590224727988e-003</threshold> + <left_node>1</left_node> + <right_val>0.5937700867652893</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 0 6 3 -1.</_> + <_>2 0 2 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.2648240923881531e-003</threshold> + <left_val>0.5645368099212647</left_val> + <right_val>0.3937624990940094</right_val></_></_> + <_> + <!-- tree 61 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 0 3 2 -1.</_> + <_>10 0 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.3251499086618423e-003</threshold> + <left_val>0.5182105898857117</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 1 2 2 -1.</_> + <_>12 1 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.0753740575164557e-003</threshold> + <left_val>0.3007416129112244</left_val> + <right_val>0.5196403861045837</right_val></_></_> + <_> + <!-- tree 62 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 0 3 2 -1.</_> + <_>9 0 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.3622138006612659e-004</threshold> + <left_val>0.3697580099105835</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 1 2 2 -1.</_> + <_>7 1 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.0082479497650638e-005</threshold> + <left_val>0.4327593147754669</left_val> + <right_val>0.5715808868408203</right_val></_></_> + <_> + <!-- tree 63 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 8 2 3 -1.</_> + <_>10 9 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.8722730241715908e-003</threshold> + <left_val>0.3473713099956513</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>13 15 6 2 -1.</_> + <_>13 16 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.2879058532416821e-004</threshold> + <left_val>0.5438259243965149</left_val> + <right_val>0.4453906118869782</right_val></_></_> + <_> + <!-- tree 64 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 12 2 2 -1.</_> + <_>8 12 1 1 2.</_> + <_>9 13 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3411579420790076e-003</threshold> + <left_node>1</left_node> + <right_val>0.6511713862419128</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 15 3 5 -1.</_> + <_>9 15 1 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.3681922405958176e-003</threshold> + <left_val>0.1443295031785965</left_val> + <right_val>0.4888199865818024</right_val></_></_> + <_> + <!-- tree 65 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 6 4 12 -1.</_> + <_>8 12 4 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.3305751215666533e-004</threshold> + <left_node>1</left_node> + <right_val>0.3951109051704407</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 6 7 8 -1.</_> + <_>7 10 7 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0746510233730078e-003</threshold> + <left_val>0.3910265862941742</left_val> + <right_val>0.5349503755569458</right_val></_></_> + <_> + <!-- tree 66 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 11 8 2 -1.</_> + <_>0 12 8 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0186100509017706</threshold> + <left_val>0.1275743991136551</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 11 2 2 -1.</_> + <_>8 11 1 1 2.</_> + <_>9 12 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3651419430971146e-003</threshold> + <left_val>0.5038288831710815</left_val> + <right_val>0.6951304078102112</right_val></_></_> + <_> + <!-- tree 67 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 7 12 1 -1.</_> + <_>11 7 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.3744421824812889e-003</threshold> + <left_val>0.5253443121910095</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 8 3 2 -1.</_> + <_>11 8 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.4163323044776917e-003</threshold> + <left_val>0.5011243820190430</left_val> + <right_val>0.7311332821846008</right_val></_></_> + <_> + <!-- tree 68 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 7 12 1 -1.</_> + <_>5 7 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.1413988694548607e-003</threshold> + <left_val>0.4953536093235016</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 5 8 2 -1.</_> + <_>6 5 4 1 2.</_> + <_>10 6 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.5847031287848949e-003</threshold> + <left_val>0.2535555958747864</left_val> + <right_val>0.6462442874908447</right_val></_></_> + <_> + <!-- tree 69 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 10 3 10 -1.</_> + <_>10 10 1 10 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0285652391612530</threshold> + <left_node>1</left_node> + <right_val>0.2330722063779831</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>16 0 2 4 -1.</_> + <_>16 0 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.3958800961263478e-004</threshold> + <left_val>0.4702244102954865</left_val> + <right_val>0.5544549226760864</right_val></_></_> + <_> + <!-- tree 70 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 10 3 10 -1.</_> + <_>9 10 1 10 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0314594581723213</threshold> + <left_node>1</left_node> + <right_val>0.0336896888911724</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 10 2 3 -1.</_> + <_>9 11 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.6011630222201347e-003</threshold> + <left_val>0.4787121117115021</left_val> + <right_val>0.6338351964950562</right_val></_></_> + <_> + <!-- tree 71 --> + <_> + <!-- 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>7.1835669223219156e-004</threshold> + <left_val>0.5431486964225769</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 14 7 6 -1.</_> + <_>12 16 7 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.5303089320659637e-003</threshold> + <left_val>0.4105832874774933</left_val> + <right_val>0.5403990745544434</right_val></_></_> + <_> + <!-- tree 72 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 1 3 1 -1.</_> + <_>7 1 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4129279879853129e-003</threshold> + <left_node>1</left_node> + <right_val>0.3105539977550507</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 0 2 4 -1.</_> + <_>3 0 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.5530709535814822e-004</threshold> + <left_val>0.4254471957683563</left_val> + <right_val>0.5447154045104981</right_val></_></_> + <_> + <!-- tree 73 --> + <_> + <!-- 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>3.1966410460881889e-004</threshold> + <left_node>1</left_node> + <right_val>0.6118361949920654</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 12 6 6 -1.</_> + <_>12 14 6 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.0411392003297806e-003</threshold> + <left_val>0.5290042161941528</left_val> + <right_val>0.4224787056446075</right_val></_></_> + <_> + <!-- tree 74 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 0 6 10 -1.</_> + <_>1 0 3 5 2.</_> + <_>4 5 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.7617880888283253e-003</threshold> + <left_val>0.4315345883369446</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 0 2 9 -1.</_> + <_>3 3 2 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.9374631121754646e-003</threshold> + <left_val>0.6629263162612915</left_val> + <right_val>0.3028964996337891</right_val></_></_> + <_> + <!-- tree 75 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 13 3 2 -1.</_> + <_>14 14 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.6497720498591661e-003</threshold> + <left_node>1</left_node> + <right_val>0.5491852760314941</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>15 2 3 2 -1.</_> + <_>15 3 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.8834417723119259e-003</threshold> + <left_val>0.3188554048538208</left_val> + <right_val>0.5184289216995239</right_val></_></_> + <_> + <!-- tree 76 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 13 5 2 -1.</_> + <_>2 14 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.7459187489002943e-004</threshold> + <left_node>1</left_node> + <right_val>0.3328830897808075</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 4 12 10 -1.</_> + <_>3 4 6 5 2.</_> + <_>9 9 6 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0153087796643376</threshold> + <left_val>0.3923608064651489</left_val> + <right_val>0.5235139131546021</right_val></_></_> + <_> + <!-- tree 77 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 1 14 6 -1.</_> + <_>5 3 14 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0322924517095089</threshold> + <left_node>1</left_node> + <right_val>0.5977646708488464</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>15 3 3 2 -1.</_> + <_>15 4 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.3842519517056644e-004</threshold> + <left_val>0.4541687965393066</left_val> + <right_val>0.5369428992271423</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>1.5429529594257474e-003</threshold> + <left_node>1</left_node> + <right_val>0.6318141222000122</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 14 6 6 -1.</_> + <_>2 16 6 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.4733028840273619e-003</threshold> + <left_val>0.3490633070468903</left_val> + <right_val>0.4759024977684021</right_val></_></_> + <_> + <!-- tree 79 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 13 8 3 -1.</_> + <_>6 14 8 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.0994939841330051e-003</threshold> + <left_node>1</left_node> + <right_val>0.5887197852134705</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 19 18 1 -1.</_> + <_>7 19 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.7541108690202236e-003</threshold> + <left_val>0.5961331725120544</left_val> + <right_val>0.4841983020305634</right_val></_></_> + <_> + <!-- tree 80 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 12 1 6 -1.</_> + <_>8 15 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0102331303060055</threshold> + <left_val>0.1705404072999954</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 0 14 15 -1.</_> + <_>0 5 14 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.2255450934171677</threshold> + <left_val>0.4779379963874817</left_val> + <right_val>0.0978796631097794</right_val></_></_> + <_> + <!-- tree 81 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 0 16 8 -1.</_> + <_>3 4 16 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0296665597707033</threshold> + <left_node>1</left_node> + <right_val>0.5822224020957947</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 1 8 12 -1.</_> + <_>6 7 8 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.8518449980765581e-003</threshold> + <left_val>0.5459626913070679</left_val> + <right_val>0.4610066115856171</right_val></_></_> + <_> + <!-- tree 82 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 3 3 3 -1.</_> + <_>6 3 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.7465328872203827e-004</threshold> + <left_node>1</left_node> + <right_val>0.3670322895050049</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 1 3 4 -1.</_> + <_>6 1 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4044740055396687e-005</threshold> + <left_val>0.4302386045455933</left_val> + <right_val>0.5691710710525513</right_val></_></_> + <_> + <!-- tree 83 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 14 4 6 -1.</_> + <_>17 14 2 3 2.</_> + <_>15 17 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0175794307142496</threshold> + <left_val>0.6917321085929871</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 11 6 8 -1.</_> + <_>15 11 3 4 2.</_> + <_>12 15 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0523816794157028</threshold> + <left_val>0.7110040187835693</left_val> + <right_val>0.5060154795646668</right_val></_></_> + <_> + <!-- tree 84 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 2 4 -1.</_> + <_>9 7 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0112421102821827</threshold> + <left_val>0.8769189119338989</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 11 3 1 -1.</_> + <_>7 11 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.6728400737047195e-003</threshold> + <left_val>0.6519191861152649</left_val> + <right_val>0.4546068906784058</right_val></_></_> + <_> + <!-- tree 85 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 3 2 14 -1.</_> + <_>12 3 1 14 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.5082760732620955e-003</threshold> + <left_val>0.5329865813255310</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 11 6 2 -1.</_> + <_>15 11 3 1 2.</_> + <_>12 12 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.1679710634052753e-003</threshold> + <left_val>0.5220459103584290</left_val> + <right_val>0.2953518927097321</right_val></_></_> + <_> + <!-- tree 86 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 5 2 -1.</_> + <_>0 3 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.7009900491684675e-004</threshold> + <left_node>1</left_node> + <right_val>0.5048633217811585</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 0 15 1 -1.</_> + <_>5 0 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0109570100903511</threshold> + <left_val>0.5837358236312866</left_val> + <right_val>0.3020085990428925</right_val></_></_> + <_> + <!-- tree 87 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 11 6 2 -1.</_> + <_>15 11 3 1 2.</_> + <_>12 12 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.3272513002157211e-003</threshold> + <left_val>0.3158063888549805</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 5 2 2 -1.</_> + <_>10 5 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.9798380637657829e-005</threshold> + <left_val>0.4386389851570129</left_val> + <right_val>0.5443211197853088</right_val></_></_> + <_> + <!-- tree 88 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 2 2 -1.</_> + <_>10 7 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.8244039276614785e-004</threshold> + <left_node>1</left_node> + <right_val>0.5625395774841309</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 0 2 10 -1.</_> + <_>9 0 1 5 2.</_> + <_>10 5 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.1364117795601487e-004</threshold> + <left_val>0.5281198024749756</left_val> + <right_val>0.3401407897472382</right_val></_></_> + <_> + <!-- tree 89 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 14 2 2 -1.</_> + <_>18 15 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.8008040497079492e-003</threshold> + <left_node>1</left_node> + <right_val>0.3471659123897553</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>13 11 4 9 -1.</_> + <_>13 14 4 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.9944779388606548e-003</threshold> + <left_val>0.4481697082519531</left_val> + <right_val>0.5385770201683044</right_val></_></_> + <_> + <!-- tree 90 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 13 2 2 -1.</_> + <_>8 13 1 1 2.</_> + <_>9 14 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.5625398342963308e-005</threshold> + <left_val>0.4492512941360474</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 8 4 3 -1.</_> + <_>7 9 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.3189922841265798e-004</threshold> + <left_val>0.4167312085628510</left_val> + <right_val>0.6021102070808411</right_val></_></_> + <_> + <!-- tree 91 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 9 4 2 -1.</_> + <_>8 10 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.9980219551362097e-004</threshold> + <left_val>0.4148428142070770</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>13 12 4 2 -1.</_> + <_>13 13 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.9060940505587496e-005</threshold> + <left_val>0.5592089891433716</left_val> + <right_val>0.4073210954666138</right_val></_></_> + <_> + <!-- tree 92 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 14 2 2 -1.</_> + <_>6 14 1 1 2.</_> + <_>7 15 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.9742690064013004e-004</threshold> + <left_val>0.6088914275169373</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 14 2 2 -1.</_> + <_>0 15 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4831830048933625e-004</threshold> + <left_val>0.5298305153846741</left_val> + <right_val>0.3761950135231018</right_val></_></_> + <_> + <!-- tree 93 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 13 6 3 -1.</_> + <_>7 14 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.9441029764711857e-003</threshold> + <left_node>1</left_node> + <right_val>0.4716084897518158</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 9 10 6 -1.</_> + <_>7 11 10 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.1374121010303497</threshold> + <left_val>0.5101336836814880</left_val> + <right_val>0.0467468015849590</right_val></_></_> + <_> + <!-- tree 94 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 9 12 4 -1.</_> + <_>6 9 4 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0884141772985458</threshold> + <left_val>0.1181868985295296</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 9 6 11 -1.</_> + <_>10 9 3 11 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0706102773547173</threshold> + <left_val>0.5119063258171082</left_val> + <right_val>0.7778441905975342</right_val></_></_> + <_> + <!-- tree 95 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 2 3 -1.</_> + <_>9 8 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.7188978902995586e-003</threshold> + <left_val>0.1874134987592697</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 14 4 3 -1.</_> + <_>9 15 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0151153998449445</threshold> + <left_val>0.4980027973651886</left_val> + <right_val>0.7005817890167236</right_val></_></_> + <_> + <!-- tree 96 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 3 3 17 -1.</_> + <_>3 3 1 17 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0671879863366485e-003</threshold> + <left_val>0.4482238888740540</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 11 6 3 -1.</_> + <_>0 12 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.0487911580130458e-004</threshold> + <left_val>0.6265752911567688</left_val> + <right_val>0.4402655065059662</right_val></_></_></trees> + <stage_threshold>47.7634506225585940</stage_threshold> + <parent>16</parent> + <next>-1</next></_> + <_> + <!-- stage 18 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 3 11 9 -1.</_> + <_>4 6 11 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0986907333135605</threshold> + <left_node>1</left_node> + <right_val>0.3999474942684174</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 2 6 11 -1.</_> + <_>3 2 3 11 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0623734183609486</threshold> + <left_val>0.5247784852981567</left_val> + <right_val>0.8193575739860535</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 0 4 5 -1.</_> + <_>13 0 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.9496519817039371e-003</threshold> + <left_val>0.3529816865921021</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 7 6 4 -1.</_> + <_>12 7 3 2 2.</_> + <_>9 9 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.9139147894456983e-004</threshold> + <left_val>0.5852727890014648</left_val> + <right_val>0.3245978057384491</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 8 2 -1.</_> + <_>9 7 4 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.5150408297777176e-004</threshold> + <left_val>0.3892816901206970</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 8 15 1 -1.</_> + <_>6 8 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1721949558705091e-003</threshold> + <left_val>0.4335052073001862</left_val> + <right_val>0.6520624160766602</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 12 12 2 -1.</_> + <_>8 12 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.4480642797425389e-004</threshold> + <left_node>1</left_node> + <right_val>0.4041135013103485</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>13 0 4 10 -1.</_> + <_>15 0 2 5 2.</_> + <_>13 5 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.6264840271323919e-003</threshold> + <left_val>0.5624982118606567</left_val> + <right_val>0.3967525064945221</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 9 2 2 -1.</_> + <_>9 10 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.9712688885629177e-004</threshold> + <left_val>0.3856112062931061</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 9 6 2 -1.</_> + <_>6 9 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.5984949208796024e-003</threshold> + <left_val>0.5997889041900635</left_val> + <right_val>0.4241614043712616</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 17 4 3 -1.</_> + <_>8 18 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.3080618381500244e-003</threshold> + <left_node>1</left_node> + <right_val>0.6660168766975403</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 3 9 2 -1.</_> + <_>11 3 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.6319877775385976e-004</threshold> + <left_val>0.4481379091739655</left_val> + <right_val>0.5583487749099731</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 3 9 2 -1.</_> + <_>6 3 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.0776469288393855e-004</threshold> + <left_val>0.3535459041595459</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 0 9 14 -1.</_> + <_>8 0 3 14 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.6223160568624735e-003</threshold> + <left_val>0.3409807085990906</left_val> + <right_val>0.5420687794685364</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 3 7 10 -1.</_> + <_>7 8 7 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0620614103972912</threshold> + <left_val>0.1934083998203278</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 8 13 3 -1.</_> + <_>4 9 13 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.4387189922854304e-004</threshold> + <left_val>0.4083626866340637</left_val> + <right_val>0.5490221977233887</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 12 14 4 -1.</_> + <_>3 12 7 2 2.</_> + <_>10 14 7 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0262399092316628</threshold> + <left_node>1</left_node> + <right_val>0.2285708039999008</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 12 4 2 -1.</_> + <_>8 13 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.1940297968685627e-004</threshold> + <left_val>0.4648667871952057</left_val> + <right_val>0.6017355918884277</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 10 9 8 -1.</_> + <_>6 14 9 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.3833119485061616e-004</threshold> + <left_node>1</left_node> + <right_val>0.3598038852214813</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 12 2 8 -1.</_> + <_>9 16 2 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5869759954512119e-003</threshold> + <left_val>0.4259651005268097</left_val> + <right_val>0.5476434826850891</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 12 3 3 -1.</_> + <_>8 13 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.7263417877256870e-003</threshold> + <left_val>0.6507238149642944</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 5 4 10 -1.</_> + <_>7 5 2 10 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0110061103478074</threshold> + <left_val>0.5149409770965576</left_val> + <right_val>0.3362984955310822</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 15 3 3 -1.</_> + <_>14 16 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.1445819921791553e-003</threshold> + <left_node>1</left_node> + <right_val>0.2672930061817169</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 6 13 3 -1.</_> + <_>4 7 13 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.7233798541128635e-003</threshold> + <left_val>0.5652182102203369</left_val> + <right_val>0.4298144876956940</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 15 3 3 -1.</_> + <_>3 16 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.8437406122684479e-003</threshold> + <left_node>1</left_node> + <right_val>0.1151885986328125</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 9 4 2 -1.</_> + <_>3 9 2 1 2.</_> + <_>5 10 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5124640412977897e-005</threshold> + <left_val>0.4373598098754883</left_val> + <right_val>0.5612128973007202</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 11 20 4 -1.</_> + <_>10 11 10 2 2.</_> + <_>0 13 10 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0399088710546494</threshold> + <left_val>0.5204648971557617</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 15 4 3 -1.</_> + <_>8 16 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.3903679363429546e-003</threshold> + <left_val>0.4813467860221863</left_val> + <right_val>0.6361209154129028</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 11 20 4 -1.</_> + <_>0 11 10 2 2.</_> + <_>10 13 10 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0399088710546494</threshold> + <left_val>0.1506870985031128</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 15 4 3 -1.</_> + <_>8 16 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.3903679363429546e-003</threshold> + <left_val>0.4581694900989533</left_val> + <right_val>0.6200240850448608</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 13 1 6 -1.</_> + <_>10 16 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.7005190066993237e-003</threshold> + <left_node>1</left_node> + <right_val>0.3432235121726990</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 1 18 2 -1.</_> + <_>11 1 9 1 2.</_> + <_>2 2 9 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0126237897202373</threshold> + <left_val>0.3088226914405823</left_val> + <right_val>0.5226737856864929</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 14 3 3 -1.</_> + <_>8 15 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0118066100403667</threshold> + <left_node>1</left_node> + <right_val>0.7187939286231995</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 1 6 1 -1.</_> + <_>6 1 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.4257229417562485e-003</threshold> + <left_val>0.3120814859867096</left_val> + <right_val>0.5065844058990479</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 13 1 3 -1.</_> + <_>11 14 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.9385299896821380e-004</threshold> + <left_val>0.4754584133625031</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>13 5 2 12 -1.</_> + <_>13 11 2 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0343881882727146</threshold> + <left_val>0.5261657834053040</left_val> + <right_val>0.3350174129009247</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 14 18 6 -1.</_> + <_>1 16 18 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0750099867582321</threshold> + <left_val>0.1713480949401856</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 13 1 3 -1.</_> + <_>8 14 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.9022492021322250e-004</threshold> + <left_val>0.4725801944732666</left_val> + <right_val>0.5956469178199768</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 13 6 3 -1.</_> + <_>7 14 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.5525289177894592e-003</threshold> + <left_val>0.6558222770690918</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 10 3 2 -1.</_> + <_>9 11 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3135520566720515e-004</threshold> + <left_val>0.4835400879383087</left_val> + <right_val>0.5586913824081421</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 1 3 3 -1.</_> + <_>6 1 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.7948658466339111e-003</threshold> + <left_node>1</left_node> + <right_val>0.2645705938339233</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 5 6 5 -1.</_> + <_>8 5 3 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.0124691072851419e-003</threshold> + <left_val>0.3657945096492767</left_val> + <right_val>0.5124772191047669</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 5 6 14 -1.</_> + <_>7 12 6 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.1178547963500023</threshold> + <left_val>0.2385654002428055</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 16 6 2 -1.</_> + <_>9 16 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5575019642710686e-003</threshold> + <left_val>0.5490474104881287</left_val> + <right_val>0.4274747967720032</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 2 12 -1.</_> + <_>1 2 1 12 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0155737595632672</threshold> + <left_val>0.6938900947570801</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 0 5 3 -1.</_> + <_>1 1 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.1854790393263102e-003</threshold> + <left_val>0.3645988106727600</left_val> + <right_val>0.5092526078224182</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 4 3 3 -1.</_> + <_>12 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.9272339306771755e-003</threshold> + <left_val>0.4685808122158051</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 6 3 3 -1.</_> + <_>12 7 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.4663668163120747e-003</threshold> + <left_val>0.4973410069942474</left_val> + <right_val>0.7726097106933594</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 4 3 3 -1.</_> + <_>5 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.6140360906720161e-003</threshold> + <left_val>0.6877465844154358</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 6 3 3 -1.</_> + <_>5 7 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.1512572206556797e-003</threshold> + <left_val>0.4788525104522705</left_val> + <right_val>0.6921657919883728</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- 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>2.7711640577763319e-003</threshold> + <left_val>0.5481839776039124</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 17 18 2 -1.</_> + <_>11 17 9 1 2.</_> + <_>2 18 9 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0128361098468304</threshold> + <left_val>0.3800162971019745</left_val> + <right_val>0.5204492807388306</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 3 2 2 -1.</_> + <_>9 4 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.4380050599575043e-003</threshold> + <left_val>0.2582435011863709</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 5 4 6 -1.</_> + <_>8 7 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1713329479098320e-003</threshold> + <left_val>0.4961163103580475</left_val> + <right_val>0.3215202987194061</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 0 8 6 -1.</_> + <_>9 2 8 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.2800728483125567e-004</threshold> + <left_node>1</left_node> + <right_val>0.5460423827171326</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 0 18 4 -1.</_> + <_>7 0 6 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.7982389852404594e-003</threshold> + <left_val>0.6046543717384338</left_val> + <right_val>0.4939922094345093</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 4 8 -1.</_> + <_>2 0 2 8 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.3543828912079334e-003</threshold> + <left_node>1</left_node> + <right_val>0.5291094183921814</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 4 6 9 -1.</_> + <_>2 4 2 9 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0146650401875377</threshold> + <left_val>0.5446122884750366</left_val> + <right_val>0.3567362129688263</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 4 18 2 -1.</_> + <_>7 4 6 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0302445106208324</threshold> + <left_val>0.5518329143524170</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 16 12 4 -1.</_> + <_>14 16 6 2 2.</_> + <_>8 18 6 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0566602088510990</threshold> + <left_val>0.6930978894233704</left_val> + <right_val>0.5093387961387634</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- 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>-5.6967479176819324e-003</threshold> + <left_val>0.3201526105403900</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 0 3 18 -1.</_> + <_>4 0 1 18 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0308067705482244</threshold> + <left_val>0.4989246129989624</left_val> + <right_val>0.2277054041624069</right_val></_></_> + <_> + <!-- tree 31 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 9 4 7 -1.</_> + <_>14 9 2 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.2748769260942936e-003</threshold> + <left_val>0.4810931086540222</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>15 14 2 2 -1.</_> + <_>15 15 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.0436900667846203e-003</threshold> + <left_val>0.5283867120742798</left_val> + <right_val>0.3255924880504608</right_val></_></_> + <_> + <!-- tree 32 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 9 4 7 -1.</_> + <_>4 9 2 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.6277956143021584e-003</threshold> + <left_val>0.6266536116600037</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 14 2 2 -1.</_> + <_>3 15 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.5113382879644632e-004</threshold> + <left_val>0.5097137093544006</left_val> + <right_val>0.3191910088062286</right_val></_></_> + <_> + <!-- tree 33 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 0 6 6 -1.</_> + <_>11 2 6 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.8188261725008488e-004</threshold> + <left_val>0.4549585878849030</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>14 0 2 6 -1.</_> + <_>15 0 1 3 2.</_> + <_>14 3 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0145949097350240</threshold> + <left_val>0.2645038962364197</left_val> + <right_val>0.5153868198394775</right_val></_></_> + <_> + <!-- tree 34 --> + <_> + <!-- 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>-1.2304580304771662e-003</threshold> + <left_val>0.6197584867477417</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 10 2 2 -1.</_> + <_>8 10 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.1867299801670015e-004</threshold> + <left_val>0.5469198822975159</left_val> + <right_val>0.4206855893135071</right_val></_></_> + <_> + <!-- tree 35 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 14 2 6 -1.</_> + <_>9 17 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0909959673881531e-003</threshold> + <left_val>0.4140760004520416</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 18 4 2 -1.</_> + <_>12 19 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.5210378700867295e-004</threshold> + <left_val>0.5476608872413635</left_val> + <right_val>0.4155021011829376</right_val></_></_> + <_> + <!-- tree 36 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 17 4 3 -1.</_> + <_>8 18 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.2563779540359974e-003</threshold> + <left_val>0.7160469293594360</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 18 8 2 -1.</_> + <_>2 19 8 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4701850013807416e-003</threshold> + <left_val>0.5240808129310608</left_val> + <right_val>0.3729662895202637</right_val></_></_> + <_> + <!-- tree 37 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 9 16 3 -1.</_> + <_>2 10 16 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1472719779703766e-004</threshold> + <left_val>0.4033798873424530</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 9 2 2 -1.</_> + <_>9 10 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.0506469774991274e-003</threshold> + <left_val>0.5263985991477966</left_val> + <right_val>0.3560093045234680</right_val></_></_> + <_> + <!-- tree 38 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 14 2 4 -1.</_> + <_>5 14 1 2 2.</_> + <_>6 16 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.6269949739798903e-004</threshold> + <left_val>0.4569799900054932</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 9 4 2 -1.</_> + <_>8 9 2 1 2.</_> + <_>10 10 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.6365550477057695e-003</threshold> + <left_val>0.3042570948600769</left_val> + <right_val>0.5868253707885742</right_val></_></_> + <_> + <!-- tree 39 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 2 5 -1.</_> + <_>9 5 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.4893293678760529e-003</threshold> + <left_node>1</left_node> + <right_val>0.4914157092571259</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 9 3 2 -1.</_> + <_>10 9 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.8107408694922924e-003</threshold> + <left_val>0.4918529987335205</left_val> + <right_val>0.6266962885856628</right_val></_></_> + <_> + <!-- tree 40 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 9 3 2 -1.</_> + <_>9 9 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.5583951547741890e-004</threshold> + <left_node>1</left_node> + <right_val>0.5633236169815064</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 8 3 6 -1.</_> + <_>9 8 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.2017690353095531e-003</threshold> + <left_val>0.5553916096687317</left_val> + <right_val>0.3827646076679230</right_val></_></_> + <_> + <!-- tree 41 --> + <_> + <!-- 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>2.7908938936889172e-003</threshold> + <left_val>0.5498697757720947</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 17 16 2 -1.</_> + <_>10 17 8 1 2.</_> + <_>2 18 8 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.8228569533675909e-003</threshold> + <left_val>0.4382283091545105</left_val> + <right_val>0.5424032807350159</right_val></_></_> + <_> + <!-- tree 42 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 12 3 8 -1.</_> + <_>9 12 1 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.2495508939027786e-003</threshold> + <left_val>0.2888121902942658</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 10 1 3 -1.</_> + <_>3 11 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.8744522286579013e-004</threshold> + <left_val>0.3472655117511749</left_val> + <right_val>0.5076370835304260</right_val></_></_> + <_> + <!-- tree 43 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 14 10 6 -1.</_> + <_>14 14 5 3 2.</_> + <_>9 17 5 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.5174440816044807e-003</threshold> + <left_val>0.4661205112934113</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>14 13 3 6 -1.</_> + <_>14 15 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0101513797417283</threshold> + <left_val>0.3744775056838989</left_val> + <right_val>0.5294001102447510</right_val></_></_> + <_> + <!-- tree 44 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 19 18 1 -1.</_> + <_>7 19 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.1399952024221420e-003</threshold> + <left_node>1</left_node> + <right_val>0.4660485088825226</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 10 15 2 -1.</_> + <_>7 10 5 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.7078551724553108e-003</threshold> + <left_val>0.4175061881542206</left_val> + <right_val>0.6916306018829346</right_val></_></_> + <_> + <!-- tree 45 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 17 16 3 -1.</_> + <_>4 18 16 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0419810414314270</threshold> + <left_node>1</left_node> + <right_val>0.2018215060234070</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 6 4 9 -1.</_> + <_>8 9 4 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0142729999497533</threshold> + <left_val>0.7511197924613953</left_val> + <right_val>0.5032083988189697</right_val></_></_> + <_> + <!-- tree 46 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 16 2 4 -1.</_> + <_>9 16 1 2 2.</_> + <_>10 18 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.0869521908462048e-003</threshold> + <left_node>1</left_node> + <right_val>0.2504513859748840</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 5 10 8 -1.</_> + <_>5 9 10 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7606799956411123e-003</threshold> + <left_val>0.3301401138305664</left_val> + <right_val>0.5218337178230286</right_val></_></_> + <_> + <!-- tree 47 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 1 4 2 -1.</_> + <_>13 1 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2550549581646919e-004</threshold> + <left_val>0.4614442884922028</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>14 0 3 6 -1.</_> + <_>14 2 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.9503209516406059e-003</threshold> + <left_val>0.4619950056076050</left_val> + <right_val>0.5247030258178711</right_val></_></_> + <_> + <!-- tree 48 --> + <_> + <!-- 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>-1.1312420247122645e-003</threshold> + <left_val>0.6314368247985840</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 1 6 1 -1.</_> + <_>9 1 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.6983180539682508e-003</threshold> + <left_val>0.3401306867599487</left_val> + <right_val>0.5055527091026306</right_val></_></_> + <_> + <!-- tree 49 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 11 3 3 -1.</_> + <_>9 12 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0114578204229474</threshold> + <left_node>1</left_node> + <right_val>0.4939996004104614</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 9 3 3 -1.</_> + <_>13 9 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.4962565451860428e-003</threshold> + <left_val>0.2965450882911682</left_val> + <right_val>0.5194367766380310</right_val></_></_> + <_> + <!-- tree 50 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 11 3 3 -1.</_> + <_>8 12 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0119190895929933</threshold> + <left_node>1</left_node> + <right_val>0.7886998057365418</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 9 3 3 -1.</_> + <_>6 9 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.4416420646011829e-003</threshold> + <left_val>0.5106986761093140</left_val> + <right_val>0.2967146039009094</right_val></_></_> + <_> + <!-- tree 51 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 11 1 3 -1.</_> + <_>10 12 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.7857811013236642e-004</threshold> + <left_val>0.5714371204376221</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 9 6 4 -1.</_> + <_>10 9 3 2 2.</_> + <_>7 11 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.0312711130827665e-003</threshold> + <left_val>0.4481200873851776</left_val> + <right_val>0.5384911894798279</right_val></_></_> + <_> + <!-- tree 52 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 7 2 2 -1.</_> + <_>4 7 1 1 2.</_> + <_>5 8 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5262430533766747e-003</threshold> + <left_val>0.6193568706512451</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 7 3 1 -1.</_> + <_>6 7 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.2860880494117737e-003</threshold> + <left_val>0.4339885115623474</left_val> + <right_val>0.7697299122810364</right_val></_></_> + <_> + <!-- tree 53 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 3 2 3 -1.</_> + <_>18 4 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.5010920837521553e-003</threshold> + <left_node>1</left_node> + <right_val>0.3171389102935791</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>13 1 4 2 -1.</_> + <_>13 1 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0125876702368259</threshold> + <left_val>0.5246698856353760</left_val> + <right_val>0.4241208136081696</right_val></_></_> + <_> + <!-- tree 54 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 1 4 2 -1.</_> + <_>5 1 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.6207490009255707e-004</threshold> + <left_val>0.4231899976730347</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 0 5 2 -1.</_> + <_>3 1 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.4701730075757951e-005</threshold> + <left_val>0.4174138903617859</left_val> + <right_val>0.5919603705406189</right_val></_></_> + <_> + <!-- tree 55 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 7 6 4 -1.</_> + <_>17 7 3 2 2.</_> + <_>14 9 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.8084698179736733e-004</threshold> + <left_val>0.4277389049530029</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 8 16 2 -1.</_> + <_>4 9 16 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.8851212058216333e-004</threshold> + <left_val>0.3720161020755768</left_val> + <right_val>0.5226818919181824</right_val></_></_> + <_> + <!-- tree 56 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 11 5 6 -1.</_> + <_>2 13 5 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.3369069676846266e-003</threshold> + <left_val>0.5478066802024841</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 16 2 4 -1.</_> + <_>5 16 1 2 2.</_> + <_>6 18 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6688359901309013e-003</threshold> + <left_val>0.3628678917884827</left_val> + <right_val>0.6150004863739014</right_val></_></_> + <_> + <!-- tree 57 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>15 6 2 12 -1.</_> + <_>16 6 1 6 2.</_> + <_>15 12 1 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.0844469438306987e-004</threshold> + <left_val>0.4747075140476227</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>13 3 6 16 -1.</_> + <_>15 3 2 16 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.4617560449987650e-003</threshold> + <left_val>0.4580138027667999</left_val> + <right_val>0.5585681796073914</right_val></_></_> + <_> + <!-- tree 58 --> + <_> + <!-- 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.0189613103866577</threshold> + <left_val>0.5298801064491272</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 1 10 13 -1.</_> + <_>10 1 5 13 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.1734731048345566</threshold> + <left_val>0.3698385059833527</left_val> + <right_val>0.8498619794845581</right_val></_></_> + <_> + <!-- tree 59 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 5 2 2 -1.</_> + <_>12 5 1 1 2.</_> + <_>11 6 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.0020549709443003e-004</threshold> + <left_node>1</left_node> + <right_val>0.5565661787986755</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>13 5 1 3 -1.</_> + <_>13 6 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0967060225084424e-003</threshold> + <left_val>0.4795713126659393</left_val> + <right_val>0.6286259889602661</right_val></_></_> + <_> + <!-- tree 60 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 4 2 4 -1.</_> + <_>7 4 1 2 2.</_> + <_>8 6 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5107099898159504e-004</threshold> + <left_val>0.4052405953407288</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 5 6 4 -1.</_> + <_>10 5 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.4463501069694757e-003</threshold> + <left_val>0.6173015236854553</left_val> + <right_val>0.4414263963699341</right_val></_></_> + <_> + <!-- tree 61 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 4 4 6 -1.</_> + <_>14 4 2 3 2.</_> + <_>12 7 2 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.5176620632410049e-003</threshold> + <left_node>1</left_node> + <right_val>0.3570570945739746</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 11 7 6 -1.</_> + <_>12 13 7 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0358121097087860</threshold> + <left_val>0.3151328861713409</left_val> + <right_val>0.5252702832221985</right_val></_></_> + <_> + <!-- tree 62 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 6 6 6 -1.</_> + <_>7 6 2 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0211554002016783</threshold> + <left_val>0.6124721169471741</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 8 2 2 -1.</_> + <_>9 9 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.9890940580517054e-004</threshold> + <left_val>0.5169975757598877</left_val> + <right_val>0.3596271872520447</right_val></_></_> + <_> + <!-- tree 63 --> + <_> + <!-- 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.5613760333508253e-003</threshold> + <left_node>1</left_node> + <right_val>0.4914987981319428</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>14 7 4 4 -1.</_> + <_>16 7 2 2 2.</_> + <_>14 9 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.7120860330760479e-004</threshold> + <left_val>0.4546211063861847</left_val> + <right_val>0.5395811796188355</right_val></_></_> + <_> + <!-- tree 64 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 6 2 -1.</_> + <_>7 5 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0215970296412706</threshold> + <left_val>0.1903133988380432</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 19 18 1 -1.</_> + <_>7 19 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0249472297728062</threshold> + <left_val>0.6974077224731445</left_val> + <right_val>0.4967716038227081</right_val></_></_> + <_> + <!-- tree 65 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 3 3 3 -1.</_> + <_>12 4 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.8725979607552290e-003</threshold> + <left_val>0.4748947918415070</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>16 0 2 3 -1.</_> + <_>16 1 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.3912719488143921e-003</threshold> + <left_val>0.5180178284645081</left_val> + <right_val>0.2924321889877319</right_val></_></_> + <_> + <!-- tree 66 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 3 3 3 -1.</_> + <_>5 4 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.1552399098873138e-003</threshold> + <left_val>0.7665870189666748</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 0 2 3 -1.</_> + <_>2 1 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1715660113841295e-003</threshold> + <left_val>0.5215551257133484</left_val> + <right_val>0.3365719020366669</right_val></_></_> + <_> + <!-- tree 67 --> + <_> + <!-- 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.2330369791015983e-003</threshold> + <left_node>1</left_node> + <right_val>0.6260957717895508</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 13 1 6 -1.</_> + <_>10 16 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.0785901364870369e-004</threshold> + <left_val>0.4533509910106659</left_val> + <right_val>0.5386489033699036</right_val></_></_> + <_> + <!-- tree 68 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 7 10 2 -1.</_> + <_>0 7 5 1 2.</_> + <_>5 8 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.6437609125860035e-004</threshold> + <left_val>0.4103496074676514</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 10 6 2 -1.</_> + <_>3 11 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1600199650274590e-004</threshold> + <left_val>0.5830391049385071</left_val> + <right_val>0.4304105937480927</right_val></_></_> + <_> + <!-- tree 69 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 18 4 2 -1.</_> + <_>12 19 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0127187203615904</threshold> + <left_val>0.2132582962512970</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 18 2 2 -1.</_> + <_>13 18 1 1 2.</_> + <_>12 19 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.9431880041956902e-005</threshold> + <left_val>0.4872891008853912</left_val> + <right_val>0.5458915233612061</right_val></_></_> + <_> + <!-- tree 70 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 19 2 1 -1.</_> + <_>7 19 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.3913689549081028e-004</threshold> + <left_val>0.3974364995956421</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 4 2 16 -1.</_> + <_>0 4 1 8 2.</_> + <_>1 12 1 8 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0180263407528400</threshold> + <left_val>0.7568550705909729</left_val> + <right_val>0.5045611858367920</right_val></_></_> + <_> + <!-- tree 71 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 1 4 9 -1.</_> + <_>16 4 4 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.9179181009531021e-003</threshold> + <left_node>1</left_node> + <right_val>0.3966299891471863</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 2 1 2 -1.</_> + <_>10 3 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1839679791592062e-004</threshold> + <left_val>0.4198082983493805</left_val> + <right_val>0.5435804128646851</right_val></_></_> + <_> + <!-- tree 72 --> + <_> + <!-- 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>-3.9474181830883026e-003</threshold> + <left_val>0.6369457840919495</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 15 1 4 -1.</_> + <_>4 17 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.0050919273635373e-005</threshold> + <left_val>0.5269566774368286</left_val> + <right_val>0.3812243044376373</right_val></_></_> + <_> + <!-- tree 73 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 20 4 -1.</_> + <_>10 2 10 2 2.</_> + <_>0 4 10 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.1423643752932549e-003</threshold> + <left_node>1</left_node> + <right_val>0.4156762957572937</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>14 5 2 8 -1.</_> + <_>14 9 2 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1305440168362111e-004</threshold> + <left_val>0.3523533046245575</left_val> + <right_val>0.5349454283714294</right_val></_></_> + <_> + <!-- tree 74 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 12 4 5 -1.</_> + <_>7 12 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.0855850016232580e-004</threshold> + <left_node>1</left_node> + <right_val>0.4403322041034699</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 13 9 6 -1.</_> + <_>0 15 9 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3130389852449298e-003</threshold> + <left_val>0.6058161258697510</left_val> + <right_val>0.4468218982219696</right_val></_></_> + <_> + <!-- tree 75 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 14 11 3 -1.</_> + <_>9 15 11 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.9134768992662430e-003</threshold> + <left_node>1</left_node> + <right_val>0.4825705885887146</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 14 7 3 -1.</_> + <_>7 15 7 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.9645769391208887e-003</threshold> + <left_val>0.4835998117923737</left_val> + <right_val>0.6039277911186218</right_val></_></_> + <_> + <!-- tree 76 --> + <_> + <!-- 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>1.7772549763321877e-003</threshold> + <left_node>1</left_node> + <right_val>0.6871827244758606</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 7 2 7 -1.</_> + <_>7 7 1 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.7136349864304066e-003</threshold> + <left_val>0.2842220962047577</left_val> + <right_val>0.5145428180694580</right_val></_></_> + <_> + <!-- tree 77 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 5 1 3 -1.</_> + <_>14 6 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.1027478184551001e-004</threshold> + <left_node>1</left_node> + <right_val>0.6024426221847534</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>13 4 4 3 -1.</_> + <_>13 5 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7460630042478442e-003</threshold> + <left_val>0.4756610095500946</left_val> + <right_val>0.5721154212951660</right_val></_></_> + <_> + <!-- tree 78 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 7 4 4 -1.</_> + <_>2 7 2 2 2.</_> + <_>4 9 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.8068278809078038e-004</threshold> + <left_node>1</left_node> + <right_val>0.4931069016456604</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 9 13 6 -1.</_> + <_>2 12 13 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.8228890150785446e-003</threshold> + <left_val>0.3311698138713837</left_val> + <right_val>0.6227598190307617</right_val></_></_> + <_> + <!-- tree 79 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 1 3 4 -1.</_> + <_>11 1 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.3000478073954582e-003</threshold> + <left_node>1</left_node> + <right_val>0.5232092738151550</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 8 5 2 -1.</_> + <_>9 9 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.4951299059903249e-005</threshold> + <left_val>0.3995231986045837</left_val> + <right_val>0.5314797759056091</right_val></_></_> + <_> + <!-- tree 80 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 14 11 3 -1.</_> + <_>0 15 11 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.2752458937466145e-003</threshold> + <left_val>0.4481619894504547</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 11 2 8 -1.</_> + <_>8 15 2 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.8162579983472824e-003</threshold> + <left_val>0.3907971978187561</left_val> + <right_val>0.6671640872955322</right_val></_></_> + <_> + <!-- tree 81 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 11 10 6 -1.</_> + <_>5 14 10 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4112279750406742e-003</threshold> + <left_val>0.5357010960578919</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 13 15 5 -1.</_> + <_>10 13 5 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.3062034100294113e-003</threshold> + <left_val>0.4770965874195099</left_val> + <right_val>0.5570099949836731</right_val></_></_> + <_> + <!-- tree 82 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 10 1 10 -1.</_> + <_>8 15 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.2164839319884777e-003</threshold> + <left_val>0.4947124123573303</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 14 6 2 -1.</_> + <_>6 14 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.9868631176650524e-003</threshold> + <left_val>0.5241307020187378</left_val> + <right_val>0.2512654960155487</right_val></_></_> + <_> + <!-- tree 83 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 14 7 3 -1.</_> + <_>7 15 7 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.6664260551333427e-003</threshold> + <left_node>1</left_node> + <right_val>0.4619553983211517</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 16 9 3 -1.</_> + <_>7 17 9 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0105812298133969</threshold> + <left_val>0.6301718950271606</left_val> + <right_val>0.4973031878471375</right_val></_></_> + <_> + <!-- tree 84 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 7 3 3 -1.</_> + <_>8 8 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.3366491124033928e-003</threshold> + <left_node>1</left_node> + <right_val>0.2870970070362091</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 5 1 6 -1.</_> + <_>3 8 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.9318940252996981e-004</threshold> + <left_val>0.4252805113792419</left_val> + <right_val>0.5579246878623962</right_val></_></_> + <_> + <!-- tree 85 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 5 11 2 -1.</_> + <_>6 6 11 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.1375334411859512e-003</threshold> + <left_val>0.5747315883636475</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 0 3 2 -1.</_> + <_>10 0 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.4809150490909815e-003</threshold> + <left_val>0.5203374028205872</left_val> + <right_val>0.3903566896915436</right_val></_></_> + <_> + <!-- tree 86 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 1 3 -1.</_> + <_>5 6 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.8749779388308525e-004</threshold> + <left_node>1</left_node> + <right_val>0.5534321069717407</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 7 3 2 -1.</_> + <_>9 7 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.2194919660687447e-004</threshold> + <left_val>0.5338044166564941</left_val> + <right_val>0.3925840854644775</right_val></_></_> + <_> + <!-- tree 87 --> + <_> + <!-- 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.9790111631155014e-003</threshold> + <left_val>0.4144316017627716</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 4 6 4 -1.</_> + <_>8 4 3 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1439629597589374e-003</threshold> + <left_val>0.4701372981071472</left_val> + <right_val>0.5281736254692078</right_val></_></_> + <_> + <!-- tree 88 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 16 3 4 -1.</_> + <_>9 16 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.5542130507528782e-003</threshold> + <left_node>1</left_node> + <right_val>0.2527256011962891</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 13 2 6 -1.</_> + <_>9 13 1 3 2.</_> + <_>10 16 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0288399644196033e-003</threshold> + <left_val>0.5605146288871765</left_val> + <right_val>0.4297856092453003</right_val></_></_> + <_> + <!-- tree 89 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 8 3 1 -1.</_> + <_>10 8 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.7234670231118798e-003</threshold> + <left_node>1</left_node> + <right_val>0.4839682877063751</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 5 18 15 -1.</_> + <_>2 10 18 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.5758669972419739</threshold> + <left_val>0.5110502839088440</left_val> + <right_val>0.0804893299937248</right_val></_></_></trees> + <stage_threshold>44.2512817382812500</stage_threshold> + <parent>17</parent> + <next>-1</next></_> + <_> + <!-- stage 19 --> + <trees> + <_> + <!-- tree 0 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 3 6 2 -1.</_> + <_>4 3 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.6640521399676800e-003</threshold> + <left_val>0.3828920125961304</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 6 6 2 -1.</_> + <_>9 6 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.9905522763729095e-003</threshold> + <left_val>0.4858429133892059</left_val> + <right_val>0.7354959249496460</right_val></_></_> + <_> + <!-- tree 1 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 17 4 3 -1.</_> + <_>8 18 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.7154200039803982e-003</threshold> + <left_node>1</left_node> + <right_val>0.6723223924636841</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 13 2 3 -1.</_> + <_>10 14 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1257929727435112e-003</threshold> + <left_val>0.4429577887058258</left_val> + <right_val>0.6070777773857117</right_val></_></_> + <_> + <!-- tree 2 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 10 20 4 -1.</_> + <_>0 12 20 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.1789010912179947e-004</threshold> + <left_node>1</left_node> + <right_val>0.3076345026493073</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 7 6 4 -1.</_> + <_>5 7 3 2 2.</_> + <_>8 9 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0492859873920679e-003</threshold> + <left_val>0.5593643784523010</left_val> + <right_val>0.3651022911071777</right_val></_></_> + <_> + <!-- tree 3 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 12 1 2 -1.</_> + <_>11 13 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.5453929740469903e-005</threshold> + <left_val>0.4277968108654022</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 10 2 3 -1.</_> + <_>10 11 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.9015709878876805e-004</threshold> + <left_val>0.4583545029163361</left_val> + <right_val>0.5284683108329773</right_val></_></_> + <_> + <!-- tree 4 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 5 2 2 -1.</_> + <_>9 6 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6071660502348095e-004</threshold> + <left_node>1</left_node> + <right_val>0.3798192143440247</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 4 1 10 -1.</_> + <_>4 9 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.2961107576265931e-004</threshold> + <left_val>0.3850437104701996</left_val> + <right_val>0.5939688086509705</right_val></_></_> + <_> + <!-- tree 5 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 18 4 2 -1.</_> + <_>11 18 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.6682569296099246e-004</threshold> + <left_val>0.4123024940490723</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 18 3 2 -1.</_> + <_>12 19 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.3492540165316314e-004</threshold> + <left_val>0.5760599970817566</left_val> + <right_val>0.4237645864486694</right_val></_></_> + <_> + <!-- tree 6 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 6 16 6 -1.</_> + <_>0 6 8 3 2.</_> + <_>8 9 8 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0108416797593236</threshold> + <left_val>0.3929921090602875</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 6 4 12 -1.</_> + <_>7 12 4 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0120778298005462</threshold> + <left_val>0.5761923193931580</left_val> + <right_val>0.2780444920063019</right_val></_></_> + <_> + <!-- tree 7 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 18 4 2 -1.</_> + <_>11 18 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.2128869313746691e-003</threshold> + <left_val>0.4794507026672363</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 18 3 2 -1.</_> + <_>12 19 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0152661902830005</threshold> + <left_val>0.0740558803081512</left_val> + <right_val>0.5153577923774719</right_val></_></_> + <_> + <!-- tree 8 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 12 1 2 -1.</_> + <_>8 13 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.7929533543065190e-005</threshold> + <left_node>1</left_node> + <right_val>0.5858737826347351</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 13 1 3 -1.</_> + <_>8 14 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7633590323384851e-004</threshold> + <left_val>0.3567610979080200</left_val> + <right_val>0.5598962903022766</right_val></_></_> + <_> + <!-- tree 9 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 18 4 2 -1.</_> + <_>11 18 2 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.1311381654813886e-004</threshold> + <left_node>1</left_node> + <right_val>0.5346850752830505</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>14 12 4 6 -1.</_> + <_>14 12 2 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.2630451023578644e-003</threshold> + <left_val>0.4782536923885346</left_val> + <right_val>0.5456753969192505</right_val></_></_> + <_> + <!-- tree 10 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 0 3 4 -1.</_> + <_>7 0 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.9503918960690498e-003</threshold> + <left_val>0.2831811904907227</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 0 2 8 -1.</_> + <_>4 0 1 4 2.</_> + <_>5 4 1 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.9864578866399825e-004</threshold> + <left_val>0.5485215783119202</left_val> + <right_val>0.4159697890281677</right_val></_></_> + <_> + <!-- tree 11 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 17 9 3 -1.</_> + <_>14 17 3 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0114325201138854</threshold> + <left_val>0.5639101266860962</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>16 2 4 5 -1.</_> + <_>16 2 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.3339172154664993e-003</threshold> + <left_val>0.4596984088420868</left_val> + <right_val>0.5931242704391480</right_val></_></_> + <_> + <!-- tree 12 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 5 9 -1.</_> + <_>0 5 5 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.3193257451057434e-003</threshold> + <left_node>1</left_node> + <right_val>0.3230620026588440</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 2 3 2 -1.</_> + <_>8 2 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.2479918920435011e-004</threshold> + <left_val>0.3795293867588043</left_val> + <right_val>0.5408611297607422</right_val></_></_> + <_> + <!-- tree 13 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>11 17 9 3 -1.</_> + <_>14 17 3 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.1118943020701408</threshold> + <left_val>0.1132297962903976</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>16 2 4 5 -1.</_> + <_>16 2 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.5553781352937222e-003</threshold> + <left_val>0.6339370012283325</left_val> + <right_val>0.4838770925998688</right_val></_></_> + <_> + <!-- tree 14 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 17 9 3 -1.</_> + <_>3 17 3 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.0337029173970222e-003</threshold> + <left_val>0.5665255188941956</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 2 4 5 -1.</_> + <_>2 2 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0148336803540587</threshold> + <left_val>0.6751418113708496</left_val> + <right_val>0.4140945076942444</right_val></_></_> + <_> + <!-- tree 15 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 11 10 9 -1.</_> + <_>5 14 10 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.7506724521517754e-003</threshold> + <left_node>1</left_node> + <right_val>0.3561258912086487</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 6 3 3 -1.</_> + <_>9 7 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6645010327920318e-003</threshold> + <left_val>0.5347279906272888</left_val> + <right_val>0.3649779856204987</right_val></_></_> + <_> + <!-- tree 16 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 17 5 3 -1.</_> + <_>3 18 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.4900820404291153e-003</threshold> + <left_node>1</left_node> + <right_val>0.2754656076431274</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 5 4 7 -1.</_> + <_>9 5 2 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1133110383525491e-003</threshold> + <left_val>0.4225992858409882</left_val> + <right_val>0.5629178881645203</right_val></_></_> + <_> + <!-- tree 17 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 8 2 5 -1.</_> + <_>9 8 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.4940755516290665e-003</threshold> + <left_val>0.4906036853790283</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 2 18 2 -1.</_> + <_>2 3 18 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5396620146930218e-003</threshold> + <left_val>0.4007051885128021</left_val> + <right_val>0.5380709171295166</right_val></_></_> + <_> + <!-- tree 18 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 8 15 6 -1.</_> + <_>7 8 5 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.1343495994806290</threshold> + <left_node>1</left_node> + <right_val>0.2214671969413757</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 8 2 5 -1.</_> + <_>10 8 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.4940755516290665e-003</threshold> + <left_val>0.7353156208992004</left_val> + <right_val>0.5005033016204834</right_val></_></_> + <_> + <!-- tree 19 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 10 4 6 -1.</_> + <_>12 12 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0200117900967598</threshold> + <left_node>1</left_node> + <right_val>0.3327906131744385</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>14 3 6 2 -1.</_> + <_>14 4 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.8875009845942259e-003</threshold> + <left_val>0.3915289044380188</left_val> + <right_val>0.5401849746704102</right_val></_></_> + <_> + <!-- tree 20 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 5 2 3 -1.</_> + <_>5 6 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.1842782199382782e-003</threshold> + <left_node>1</left_node> + <right_val>0.7176604866981506</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 6 3 3 -1.</_> + <_>4 7 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6976969782263041e-003</threshold> + <left_val>0.4526978135108948</left_val> + <right_val>0.6076912879943848</right_val></_></_> + <_> + <!-- tree 21 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 12 3 3 -1.</_> + <_>14 13 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.9219978973269463e-003</threshold> + <left_node>1</left_node> + <right_val>0.2569833993911743</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 12 11 3 -1.</_> + <_>6 13 11 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0118031995370984</threshold> + <left_val>0.4999637901782990</left_val> + <right_val>0.5958228111267090</right_val></_></_> + <_> + <!-- tree 22 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 2 3 6 -1.</_> + <_>1 4 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.7703449428081512e-003</threshold> + <left_val>0.3459093868732452</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 0 4 7 -1.</_> + <_>3 0 2 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.1174899302423000e-003</threshold> + <left_val>0.4515126943588257</left_val> + <right_val>0.5829715728759766</right_val></_></_> + <_> + <!-- tree 23 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 8 3 4 -1.</_> + <_>10 8 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.4801411032676697e-003</threshold> + <left_val>0.4807392060756683</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 9 2 2 -1.</_> + <_>10 10 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.6078789960592985e-003</threshold> + <left_val>0.3462216854095459</left_val> + <right_val>0.5201594829559326</right_val></_></_> + <_> + <!-- tree 24 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 8 3 4 -1.</_> + <_>9 8 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.7252747938036919e-003</threshold> + <left_val>0.6599853038787842</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 4 10 10 -1.</_> + <_>4 9 10 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.2325618714094162e-003</threshold> + <left_val>0.2821828126907349</left_val> + <right_val>0.5125284790992737</right_val></_></_> + <_> + <!-- tree 25 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 10 3 2 -1.</_> + <_>10 10 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.9571950957179070e-004</threshold> + <left_val>0.4883818924427033</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 10 3 2 -1.</_> + <_>9 11 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5021569561213255e-004</threshold> + <left_val>0.4829918146133423</left_val> + <right_val>0.5428717136383057</right_val></_></_> + <_> + <!-- tree 26 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 10 3 2 -1.</_> + <_>9 10 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.8489659093320370e-004</threshold> + <left_val>0.4434598982334137</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 4 14 12 -1.</_> + <_>2 4 7 6 2.</_> + <_>9 10 7 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0961926504969597</threshold> + <left_val>0.2256636023521423</left_val> + <right_val>0.5956227779388428</right_val></_></_> + <_> + <!-- tree 27 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 12 1 6 -1.</_> + <_>10 15 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1053519556298852e-003</threshold> + <left_val>0.4527224004268646</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 3 8 16 -1.</_> + <_>11 3 4 8 2.</_> + <_>7 11 4 8 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.1021504029631615</threshold> + <left_val>0.2844349145889282</left_val> + <right_val>0.5186452865600586</right_val></_></_> + <_> + <!-- tree 28 --> + <_> + <!-- 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>3.0147889629006386e-003</threshold> + <left_node>1</left_node> + <right_val>0.3808999061584473</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 2 8 8 -1.</_> + <_>6 2 4 4 2.</_> + <_>10 6 4 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.6131648384034634e-003</threshold> + <left_val>0.5718699097633362</left_val> + <right_val>0.4262563884258270</right_val></_></_> + <_> + <!-- tree 29 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 5 4 2 -1.</_> + <_>12 5 2 1 2.</_> + <_>10 6 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5197630273178220e-003</threshold> + <left_node>1</left_node> + <right_val>0.5942718982696533</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 4 3 3 -1.</_> + <_>12 5 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0141972796991467</threshold> + <left_val>0.7731103897094727</left_val> + <right_val>0.4997653961181641</right_val></_></_> + <_> + <!-- tree 30 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 19 12 1 -1.</_> + <_>8 19 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0138188796117902</threshold> + <left_val>0.6681138277053833</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 2 3 1 -1.</_> + <_>9 2 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.0701329018920660e-004</threshold> + <left_val>0.3305608034133911</left_val> + <right_val>0.4749974906444550</right_val></_></_> + <_> + <!-- tree 31 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 17 4 3 -1.</_> + <_>13 18 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.3537531793117523e-003</threshold> + <left_val>0.2860932946205139</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 14 6 3 -1.</_> + <_>7 15 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.4771059229969978e-003</threshold> + <left_val>0.6188883185386658</left_val> + <right_val>0.4842100143432617</right_val></_></_> + <_> + <!-- tree 32 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 14 2 3 -1.</_> + <_>9 15 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6923650400713086e-003</threshold> + <left_node>1</left_node> + <right_val>0.6070249080657959</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 15 6 3 -1.</_> + <_>7 16 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.8652542065829039e-004</threshold> + <left_val>0.3782689869403839</left_val> + <right_val>0.5368196964263916</right_val></_></_> + <_> + <!-- tree 33 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 18 3 2 -1.</_> + <_>11 18 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.5826620403677225e-003</threshold> + <left_val>0.3690209984779358</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>14 12 2 3 -1.</_> + <_>14 13 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.7307639829814434e-003</threshold> + <left_val>0.3857114911079407</left_val> + <right_val>0.5318108797073364</right_val></_></_> + <_> + <!-- tree 34 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 10 4 6 -1.</_> + <_>4 12 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0218715704977512</threshold> + <left_node>1</left_node> + <right_val>0.2327008992433548</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 13 3 2 -1.</_> + <_>4 14 3 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5010299648565706e-005</threshold> + <left_val>0.5560722947120667</left_val> + <right_val>0.4301410019397736</right_val></_></_> + <_> + <!-- tree 35 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 16 2 3 -1.</_> + <_>9 17 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.3583700209856033e-003</threshold> + <left_node>1</left_node> + <right_val>0.6767637729644775</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 18 3 2 -1.</_> + <_>11 18 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.0057549960911274e-003</threshold> + <left_val>0.5194904208183289</left_val> + <right_val>0.3612853884696960</right_val></_></_> + <_> + <!-- tree 36 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 18 3 2 -1.</_> + <_>8 18 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.9030070398002863e-003</threshold> + <left_val>0.3237845003604889</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 10 4 2 -1.</_> + <_>1 11 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.8506693243980408e-003</threshold> + <left_val>0.1194851994514465</left_val> + <right_val>0.4991723895072937</right_val></_></_> + <_> + <!-- tree 37 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 4 6 3 -1.</_> + <_>12 5 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.7093670796602964e-003</threshold> + <left_node>1</left_node> + <right_val>0.4854960143566132</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>14 4 1 3 -1.</_> + <_>14 5 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4138079714030027e-003</threshold> + <left_val>0.4872322976589203</left_val> + <right_val>0.5903577804565430</right_val></_></_> + <_> + <!-- tree 38 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 4 6 3 -1.</_> + <_>2 5 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.0300198644399643e-003</threshold> + <left_node>1</left_node> + <right_val>0.6547315716743469</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 4 1 3 -1.</_> + <_>5 5 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.7925681620836258e-004</threshold> + <left_val>0.5849273204803467</left_val> + <right_val>0.4554230868816376</right_val></_></_> + <_> + <!-- tree 39 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 12 3 3 -1.</_> + <_>14 13 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3984439428895712e-003</threshold> + <left_node>1</left_node> + <right_val>0.4064626097679138</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>15 12 2 3 -1.</_> + <_>15 13 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.3372107474133372e-004</threshold> + <left_val>0.5399543046951294</left_val> + <right_val>0.4152809977531433</right_val></_></_> + <_> + <!-- tree 40 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 16 4 3 -1.</_> + <_>3 17 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0105510596185923</threshold> + <left_node>1</left_node> + <right_val>0.1796680986881256</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 0 4 2 -1.</_> + <_>8 1 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.8344102550763637e-005</threshold> + <left_val>0.4251863062381744</left_val> + <right_val>0.5413522720336914</right_val></_></_> + <_> + <!-- tree 41 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 20 1 -1.</_> + <_>0 0 10 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0410223081707954</threshold> + <left_node>1</left_node> + <right_val>0.5228124856948853</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 7 3 4 -1.</_> + <_>10 7 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.5065628625452518e-003</threshold> + <left_val>0.4853743016719818</left_val> + <right_val>0.6093444228172302</right_val></_></_> + <_> + <!-- tree 42 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 0 20 1 -1.</_> + <_>10 0 10 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0410223081707954</threshold> + <left_node>1</left_node> + <right_val>0.2205024063587189</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 7 3 4 -1.</_> + <_>9 7 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.3961377125233412e-004</threshold> + <left_val>0.5692731738090515</left_val> + <right_val>0.4468756914138794</right_val></_></_> + <_> + <!-- tree 43 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 6 19 3 -1.</_> + <_>1 7 19 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0686960369348526</threshold> + <left_val>0.1483314037322998</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 7 4 2 -1.</_> + <_>12 8 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.8447940237820148e-003</threshold> + <left_val>0.6211283802986145</left_val> + <right_val>0.4966601133346558</right_val></_></_> + <_> + <!-- tree 44 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 8 3 3 -1.</_> + <_>7 9 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.0959919355809689e-003</threshold> + <left_val>0.2294671982526779</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 7 3 3 -1.</_> + <_>8 7 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.2068301700055599e-003</threshold> + <left_val>0.6407091021537781</left_val> + <right_val>0.4748562872409821</right_val></_></_> + <_> + <!-- tree 45 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 9 16 3 -1.</_> + <_>2 10 16 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.1332789957523346e-004</threshold> + <left_node>1</left_node> + <right_val>0.5354936122894287</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 4 2 12 -1.</_> + <_>9 8 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.1175677999854088</threshold> + <left_val>0.5136978030204773</left_val> + <right_val>0.0105957398191094</right_val></_></_> + <_> + <!-- tree 46 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 3 2 5 -1.</_> + <_>8 3 1 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.9354289987822995e-005</threshold> + <left_val>0.3711803853511810</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 7 2 3 -1.</_> + <_>9 8 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.3173691742122173e-003</threshold> + <left_val>0.1712073981761932</left_val> + <right_val>0.5061758160591126</right_val></_></_> + <_> + <!-- tree 47 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 14 4 3 -1.</_> + <_>9 15 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0149414995685220</threshold> + <left_node>1</left_node> + <right_val>0.6729118824005127</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 8 6 4 -1.</_> + <_>10 8 3 2 2.</_> + <_>7 10 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.0789399277418852e-003</threshold> + <left_val>0.4410645961761475</left_val> + <right_val>0.5444027781486511</right_val></_></_> + <_> + <!-- tree 48 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 2 2 -1.</_> + <_>10 7 1 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.0736219640821218e-004</threshold> + <left_val>0.5568910837173462</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 5 6 6 -1.</_> + <_>7 5 2 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.1247111037373543e-003</threshold> + <left_val>0.5023869276046753</left_val> + <right_val>0.3562405109405518</right_val></_></_> + <_> + <!-- tree 49 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 1 3 6 -1.</_> + <_>10 1 1 6 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.8919378574937582e-004</threshold> + <left_node>1</left_node> + <right_val>0.5456786155700684</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 5 12 2 -1.</_> + <_>8 5 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0101795801892877</threshold> + <left_val>0.5545138716697693</left_val> + <right_val>0.4622310996055603</right_val></_></_> + <_> + <!-- tree 50 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 2 6 4 -1.</_> + <_>6 2 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.7506109327077866e-003</threshold> + <left_node>1</left_node> + <right_val>0.4942536056041718</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 7 8 2 -1.</_> + <_>4 8 8 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0106013296172023</threshold> + <left_val>0.2961233854293823</left_val> + <right_val>0.5964338779449463</right_val></_></_> + <_> + <!-- tree 51 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 6 14 6 -1.</_> + <_>10 6 7 3 2.</_> + <_>3 9 7 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.1466780714690685e-003</threshold> + <left_val>0.5495228767395020</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 6 14 3 -1.</_> + <_>3 6 7 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0763211473822594</threshold> + <left_val>0.5173959136009216</left_val> + <right_val>0.2940216958522797</right_val></_></_> + <_> + <!-- tree 52 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 5 2 2 -1.</_> + <_>0 6 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5027689514681697e-003</threshold> + <left_val>0.3106299936771393</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 13 4 3 -1.</_> + <_>8 14 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0122666703537107</threshold> + <left_val>0.4651150107383728</left_val> + <right_val>0.6846613883972168</right_val></_></_> + <_> + <!-- tree 53 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 0 3 20 -1.</_> + <_>14 0 1 20 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0311185792088509</threshold> + <left_node>1</left_node> + <right_val>0.5226057171821594</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 8 10 3 -1.</_> + <_>10 9 10 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0289055891335011</threshold> + <left_val>0.5182244181632996</left_val> + <right_val>0.2705428004264832</right_val></_></_> + <_> + <!-- tree 54 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 0 3 20 -1.</_> + <_>5 0 1 20 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0475983805954456</threshold> + <left_node>1</left_node> + <right_val>0.1109512001276016</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 8 10 3 -1.</_> + <_>0 9 10 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0308085493743420</threshold> + <left_val>0.4938625097274780</left_val> + <right_val>0.1404110938310623</right_val></_></_> + <_> + <!-- tree 55 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 5 3 4 -1.</_> + <_>13 5 1 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.1277810446918011e-004</threshold> + <left_node>1</left_node> + <right_val>0.4392356872558594</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 7 12 4 -1.</_> + <_>10 7 4 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0789699628949165</threshold> + <left_val>0.5216552019119263</left_val> + <right_val>0.2294113934040070</right_val></_></_> + <_> + <!-- tree 56 --> + <_> + <!-- 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.0102579500526190</threshold> + <left_val>0.6176652908325195</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 17 6 2 -1.</_> + <_>1 18 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.2604889925569296e-003</threshold> + <left_val>0.5236222743988037</left_val> + <right_val>0.3328965902328491</right_val></_></_> + <_> + <!-- tree 57 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 8 6 12 -1.</_> + <_>17 8 3 6 2.</_> + <_>14 14 3 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0334904603660107</threshold> + <left_node>1</left_node> + <right_val>0.4866186976432800</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>18 5 2 2 -1.</_> + <_>18 6 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.9202767442911863e-004</threshold> + <left_val>0.4116407036781311</left_val> + <right_val>0.5395640134811401</right_val></_></_> + <_> + <!-- tree 58 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 16 4 2 -1.</_> + <_>3 16 2 1 2.</_> + <_>5 17 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.0320750738610514e-005</threshold> + <left_node>1</left_node> + <right_val>0.5610736012458801</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 16 6 2 -1.</_> + <_>4 16 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.4369680583477020e-004</threshold> + <left_val>0.5621389150619507</left_val> + <right_val>0.3461203873157501</right_val></_></_> + <_> + <!-- tree 59 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 8 6 12 -1.</_> + <_>17 8 3 6 2.</_> + <_>14 14 3 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0334904603660107</threshold> + <left_node>1</left_node> + <right_val>0.4896762073040009</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>18 5 2 2 -1.</_> + <_>18 6 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.9202767442911863e-004</threshold> + <left_val>0.4305404126644135</left_val> + <right_val>0.5340713858604431</right_val></_></_> + <_> + <!-- tree 60 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 16 9 2 -1.</_> + <_>8 16 3 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.0550889894366264e-003</threshold> + <left_val>0.5544999837875366</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 14 6 6 -1.</_> + <_>3 14 3 3 2.</_> + <_>6 17 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.4353571720421314e-003</threshold> + <left_val>0.6038540005683899</left_val> + <right_val>0.3746592998504639</right_val></_></_> + <_> + <!-- tree 61 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 8 6 12 -1.</_> + <_>17 8 3 6 2.</_> + <_>14 14 3 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0841704234480858</threshold> + <left_node>1</left_node> + <right_val>0.5007348060607910</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 7 2 12 -1.</_> + <_>11 11 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.7419027909636497e-003</threshold> + <left_val>0.5298097133636475</left_val> + <right_val>0.4716145098209381</right_val></_></_> + <_> + <!-- tree 62 --> + <_> + <!-- 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.0102781504392624</threshold> + <left_node>1</left_node> + <right_val>0.6269375085830689</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 7 2 12 -1.</_> + <_>7 11 2 4 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.8800862170755863e-003</threshold> + <left_val>0.5154827833175659</left_val> + <right_val>0.3813040852546692</right_val></_></_> + <_> + <!-- tree 63 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 12 1 2 -1.</_> + <_>14 13 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.9679190346505493e-006</threshold> + <left_node>1</left_node> + <right_val>0.4440239965915680</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 13 8 1 -1.</_> + <_>12 13 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.2419527461752295e-004</threshold> + <left_val>0.4697534143924713</left_val> + <right_val>0.5485504269599915</right_val></_></_> + <_> + <!-- tree 64 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 3 16 6 -1.</_> + <_>0 6 16 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.5268318392336369e-003</threshold> + <left_val>0.5513604879379273</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 4 8 2 -1.</_> + <_>1 4 4 1 2.</_> + <_>5 5 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.6128671430051327e-004</threshold> + <left_val>0.3618639111518860</left_val> + <right_val>0.5838456749916077</right_val></_></_> + <_> + <!-- tree 65 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>14 12 1 2 -1.</_> + <_>14 13 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.4810510221868753e-003</threshold> + <left_node>1</left_node> + <right_val>0.2523222863674164</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>15 12 2 3 -1.</_> + <_>15 13 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.0480589699000120e-003</threshold> + <left_val>0.4117257893085480</left_val> + <right_val>0.5392996072769165</right_val></_></_> + <_> + <!-- tree 66 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 16 3 3 -1.</_> + <_>8 17 3 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.1287907883524895e-003</threshold> + <left_val>0.6726329922676086</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 12 1 2 -1.</_> + <_>5 13 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.1682329932227731e-004</threshold> + <left_val>0.5041192770004273</left_val> + <right_val>0.3607729077339172</right_val></_></_> + <_> + <!-- tree 67 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 4 3 15 -1.</_> + <_>14 4 1 15 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0399094782769680</threshold> + <left_val>0.1563739031553268</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>17 3 2 6 -1.</_> + <_>18 3 1 3 2.</_> + <_>17 6 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.5859459526836872e-003</threshold> + <left_val>0.4891980886459351</left_val> + <right_val>0.5779845118522644</right_val></_></_> + <_> + <!-- tree 68 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 4 3 15 -1.</_> + <_>5 4 1 15 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0226902291178703</threshold> + <left_val>0.2186879068613052</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 3 2 6 -1.</_> + <_>1 3 1 3 2.</_> + <_>2 6 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.0916070789098740e-003</threshold> + <left_val>0.4771577119827271</left_val> + <right_val>0.6099231243133545</right_val></_></_> + <_> + <!-- tree 69 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 15 12 4 -1.</_> + <_>7 17 12 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0247154198586941</threshold> + <left_val>0.3463996946811676</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 0 19 3 -1.</_> + <_>1 1 19 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0134194502606988</threshold> + <left_val>0.3630692958831787</left_val> + <right_val>0.5252196192741394</right_val></_></_> + <_> + <!-- tree 70 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 17 10 2 -1.</_> + <_>3 17 5 1 2.</_> + <_>8 18 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.0629472136497498e-003</threshold> + <left_val>0.6666321754455566</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 5 10 15 -1.</_> + <_>2 10 10 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.0921030081808567e-003</threshold> + <left_val>0.3399547040462494</left_val> + <right_val>0.5035697817802429</right_val></_></_> + <_> + <!-- tree 71 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 8 3 4 -1.</_> + <_>13 10 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0259618591517210</threshold> + <left_val>0.5036802887916565</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>19 13 1 2 -1.</_> + <_>19 14 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7908669542521238e-004</threshold> + <left_val>0.5418530702590942</left_val> + <right_val>0.4318976998329163</right_val></_></_> + <_> + <!-- tree 72 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 8 3 4 -1.</_> + <_>4 10 3 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.1546850223094225e-003</threshold> + <left_val>0.7221025228500366</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 13 1 2 -1.</_> + <_>0 14 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.1397759662941098e-003</threshold> + <left_val>0.3320972919464111</left_val> + <right_val>0.5024433732032776</right_val></_></_> + <_> + <!-- tree 73 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 7 2 12 -1.</_> + <_>12 13 2 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0478402115404606</threshold> + <left_val>0.1938765048980713</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>14 7 2 2 -1.</_> + <_>15 7 1 1 2.</_> + <_>14 8 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.1577088995836675e-004</threshold> + <left_val>0.4802188873291016</left_val> + <right_val>0.5730714797973633</right_val></_></_> + <_> + <!-- tree 74 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 3 8 2 -1.</_> + <_>5 4 8 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.4247039477340877e-004</threshold> + <left_val>0.4262515008449554</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 2 2 6 -1.</_> + <_>0 4 2 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4479350065812469e-003</threshold> + <left_val>0.5719171166419983</left_val> + <right_val>0.4064153134822846</right_val></_></_> + <_> + <!-- tree 75 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 2 2 12 -1.</_> + <_>19 2 1 6 2.</_> + <_>18 8 1 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0157015100121498</threshold> + <left_val>0.4995726048946381</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>18 1 1 2 -1.</_> + <_>18 2 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.7805729769170284e-004</threshold> + <left_val>0.5289286971092224</left_val> + <right_val>0.4581728875637054</right_val></_></_> + <_> + <!-- tree 76 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 2 2 12 -1.</_> + <_>0 2 1 6 2.</_> + <_>1 8 1 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.9010509606450796e-003</threshold> + <left_val>0.6012148261070252</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 1 1 2 -1.</_> + <_>1 2 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.0830519497394562e-004</threshold> + <left_val>0.5057976841926575</left_val> + <right_val>0.3599432110786438</right_val></_></_> + <_> + <!-- tree 77 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>16 4 4 14 -1.</_> + <_>18 4 2 7 2.</_> + <_>16 11 2 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0515300296247005</threshold> + <left_node>1</left_node> + <right_val>0.4991796910762787</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 14 1 6 -1.</_> + <_>10 17 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.7163449956569821e-004</threshold> + <left_val>0.4675469994544983</left_val> + <right_val>0.5374773144721985</right_val></_></_> + <_> + <!-- tree 78 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 4 4 14 -1.</_> + <_>0 4 2 7 2.</_> + <_>2 11 2 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0236142799258232</threshold> + <left_node>1</left_node> + <right_val>0.6586478948593140</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 14 1 6 -1.</_> + <_>9 17 1 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.6427798699587584e-004</threshold> + <left_val>0.3853296041488648</left_val> + <right_val>0.5196040272712708</right_val></_></_> + <_> + <!-- tree 79 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 14 4 3 -1.</_> + <_>9 15 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.6903959959745407e-003</threshold> + <left_node>1</left_node> + <right_val>0.6004235744476318</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 7 12 2 -1.</_> + <_>8 7 4 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.8789530992507935e-003</threshold> + <left_val>0.3293227851390839</left_val> + <right_val>0.5245236754417419</right_val></_></_> + <_> + <!-- tree 80 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 8 4 3 -1.</_> + <_>0 9 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.8537332117557526e-003</threshold> + <left_val>0.2565914094448090</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 7 2 2 -1.</_> + <_>4 7 1 1 2.</_> + <_>5 8 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.9893810693174601e-004</threshold> + <left_val>0.4615494012832642</left_val> + <right_val>0.5942432284355164</right_val></_></_> + <_> + <!-- tree 81 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 7 2 1 -1.</_> + <_>13 7 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.3354700058698654e-004</threshold> + <left_val>0.5487375855445862</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 4 4 5 -1.</_> + <_>11 4 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0165109997615218e-003</threshold> + <left_val>0.4578359127044678</left_val> + <right_val>0.5426927804946899</right_val></_></_> + <_> + <!-- tree 82 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 8 3 3 -1.</_> + <_>5 8 1 3 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>9.1216771397739649e-004</threshold> + <left_node>1</left_node> + <right_val>0.3939461112022400</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>0 3 8 1 -1.</_> + <_>4 3 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.0080259526148438e-003</threshold> + <left_val>0.4049789905548096</left_val> + <right_val>0.5520703792572022</right_val></_></_> + <_> + <!-- tree 83 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>13 7 2 1 -1.</_> + <_>13 7 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.3102490629535168e-004</threshold> + <left_node>1</left_node> + <right_val>0.4879088997840881</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>14 7 3 2 -1.</_> + <_>15 7 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.5228749988600612e-004</threshold> + <left_val>0.4844943881034851</left_val> + <right_val>0.5512825846672058</right_val></_></_> + <_> + <!-- tree 84 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 7 2 1 -1.</_> + <_>6 7 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.2130969844292849e-004</threshold> + <left_node>1</left_node> + <right_val>0.4367971122264862</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>3 7 3 2 -1.</_> + <_>4 7 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.5112989785848185e-005</threshold> + <left_val>0.6425955295562744</left_val> + <right_val>0.4881826937198639</right_val></_></_> + <_> + <!-- tree 85 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>18 5 2 2 -1.</_> + <_>18 6 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.0125829400494695e-004</threshold> + <left_node>1</left_node> + <right_val>0.5372099280357361</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 14 2 2 -1.</_> + <_>13 14 1 1 2.</_> + <_>12 15 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.5766851184889674e-004</threshold> + <left_val>0.5834553241729736</left_val> + <right_val>0.4869078099727631</right_val></_></_> + <_> + <!-- tree 86 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>0 5 2 2 -1.</_> + <_>0 6 2 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.2220421386882663e-004</threshold> + <left_node>1</left_node> + <right_val>0.3824636936187744</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 14 2 2 -1.</_> + <_>6 14 1 1 2.</_> + <_>7 15 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.4663359615951777e-003</threshold> + <left_val>0.4813488125801086</left_val> + <right_val>0.6966739296913147</right_val></_></_> + <_> + <!-- tree 87 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 12 6 5 -1.</_> + <_>9 12 2 5 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0495477095246315</threshold> + <left_val>0.0539276599884033</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 17 5 2 -1.</_> + <_>12 18 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3017569435760379e-003</threshold> + <left_val>0.5337455868721008</left_val> + <right_val>0.4160748124122620</right_val></_></_> + <_> + <!-- tree 88 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 11 6 3 -1.</_> + <_>4 11 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.4914530590176582e-003</threshold> + <left_val>0.5997437238693237</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 9 6 3 -1.</_> + <_>4 9 3 3 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6592369647696614e-003</threshold> + <left_val>0.3727185130119324</left_val> + <right_val>0.5115634202957153</right_val></_></_> + <_> + <!-- tree 89 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 7 2 12 -1.</_> + <_>12 13 2 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.4695458859205246e-003</threshold> + <left_val>0.5252035260200501</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>8 7 5 3 -1.</_> + <_>8 8 5 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.9810269847512245e-003</threshold> + <left_val>0.5256717801094055</left_val> + <right_val>0.3934406042098999</right_val></_></_> + <_> + <!-- tree 90 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 7 2 12 -1.</_> + <_>6 13 2 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0385369807481766</threshold> + <left_val>0.2061924934387207</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 2 9 18 -1.</_> + <_>4 2 3 18 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.2827565073966980</threshold> + <left_val>0.0618832111358643</left_val> + <right_val>0.4925057888031006</right_val></_></_> + <_> + <!-- tree 91 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 17 5 2 -1.</_> + <_>12 18 5 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-9.0301828458905220e-003</threshold> + <left_val>0.3157590031623840</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 7 12 2 -1.</_> + <_>4 7 6 2 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0438662692904472</threshold> + <left_val>0.2033682018518448</left_val> + <right_val>0.5164769887924194</right_val></_></_> + <_> + <!-- tree 92 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>6 7 6 1 -1.</_> + <_>8 7 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.5701069757342339e-003</threshold> + <left_val>0.6611183285713196</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>7 3 3 2 -1.</_> + <_>8 3 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.3362410720437765e-003</threshold> + <left_val>0.2807789146900177</left_val> + <right_val>0.4962876141071320</right_val></_></_> + <_> + <!-- tree 93 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 4 3 1 -1.</_> + <_>10 4 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>5.3960331715643406e-003</threshold> + <left_val>0.5146387815475464</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>11 11 3 1 -1.</_> + <_>12 11 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.6297608856111765e-003</threshold> + <left_val>0.6284487843513489</left_val> + <right_val>0.4955588877201080</right_val></_></_> + <_> + <!-- tree 94 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>8 4 3 1 -1.</_> + <_>9 4 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-3.8577478844672441e-003</threshold> + <left_val>0.1486748009920120</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 11 3 1 -1.</_> + <_>7 11 1 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.3963800156489015e-003</threshold> + <left_val>0.4701338112354279</left_val> + <right_val>0.6320971846580505</right_val></_></_> + <_> + <!-- tree 95 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>12 13 6 6 -1.</_> + <_>12 15 6 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-8.8699469342827797e-003</threshold> + <left_node>1</left_node> + <right_val>0.5286818146705627</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>14 13 1 6 -1.</_> + <_>14 15 1 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-7.0626288652420044e-004</threshold> + <left_val>0.4648370146751404</left_val> + <right_val>0.5333210229873657</right_val></_></_> + <_> + <!-- tree 96 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>2 13 6 6 -1.</_> + <_>2 15 6 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.2645810171961784e-003</threshold> + <left_val>0.5084878206253052</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>1 5 18 1 -1.</_> + <_>7 5 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0615721009671688</threshold> + <left_val>0.3629625141620636</left_val> + <right_val>0.8757156729698181</right_val></_></_> + <_> + <!-- tree 97 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>4 7 12 2 -1.</_> + <_>10 7 6 1 2.</_> + <_>4 8 6 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.5381980016827583e-003</threshold> + <left_node>1</left_node> + <right_val>0.4856696128845215</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 1 8 10 -1.</_> + <_>10 1 4 5 2.</_> + <_>6 6 4 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.0877899155020714e-003</threshold> + <left_val>0.4584116041660309</left_val> + <right_val>0.5420240759849548</right_val></_></_> + <_> + <!-- tree 98 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>3 13 4 3 -1.</_> + <_>3 14 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.4308601431548595e-003</threshold> + <left_node>1</left_node> + <right_val>0.2707302868366242</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 13 4 3 -1.</_> + <_>6 14 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>7.0455260574817657e-003</threshold> + <left_val>0.5057486891746521</left_val> + <right_val>0.7026523947715759</right_val></_></_> + <_> + <!-- tree 99 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 14 4 3 -1.</_> + <_>9 15 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.3246440105140209e-003</threshold> + <left_node>1</left_node> + <right_val>0.4827278852462769</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 9 2 3 -1.</_> + <_>12 10 2 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.0276601288933307e-005</threshold> + <left_val>0.4247249066829681</left_val> + <right_val>0.5508763194084168</right_val></_></_> + <_> + <!-- tree 100 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 14 4 3 -1.</_> + <_>7 15 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0180845595896244</threshold> + <left_node>1</left_node> + <right_val>0.8104801177978516</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>9 0 2 1 -1.</_> + <_>10 0 1 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.4693520329892635e-004</threshold> + <left_val>0.5154619216918945</left_val> + <right_val>0.3514379858970642</right_val></_></_> + <_> + <!-- tree 101 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 0 10 5 -1.</_> + <_>5 0 5 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0269310399889946</threshold> + <left_node>1</left_node> + <right_val>0.4886888861656189</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 6 8 7 -1.</_> + <_>6 6 4 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-4.2346641421318054e-003</threshold> + <left_val>0.4622378051280975</left_val> + <right_val>0.5382478237152100</right_val></_></_> + <_> + <!-- tree 102 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 0 10 5 -1.</_> + <_>10 0 5 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0269471108913422</threshold> + <left_node>1</left_node> + <right_val>0.6366596221923828</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>6 6 8 7 -1.</_> + <_>10 6 4 7 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>4.6446882188320160e-003</threshold> + <left_val>0.5368506908416748</left_val> + <right_val>0.3765429854393005</right_val></_></_> + <_> + <!-- tree 103 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 9 10 8 -1.</_> + <_>10 9 5 4 2.</_> + <_>5 13 5 4 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-6.9577661342918873e-003</threshold> + <left_val>0.4234687089920044</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>10 0 4 10 -1.</_> + <_>12 0 2 5 2.</_> + <_>10 5 2 5 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>8.7609712500125170e-004</threshold> + <left_val>0.4672406017780304</left_val> + <right_val>0.5350683927536011</right_val></_></_> + <_> + <!-- tree 104 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>1 4 8 3 -1.</_> + <_>1 5 8 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>1.6103329835459590e-003</threshold> + <left_node>1</left_node> + <right_val>0.5732762813568115</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 4 8 3 -1.</_> + <_>4 5 8 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-1.2848590267822146e-003</threshold> + <left_val>0.5481799244880676</left_val> + <right_val>0.3784593045711517</right_val></_></_> + <_> + <!-- tree 105 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>9 7 4 3 -1.</_> + <_>9 8 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>0.0102435396984220</threshold> + <left_val>0.5155907273292542</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>12 8 3 12 -1.</_> + <_>12 14 3 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>2.6889349101111293e-004</threshold> + <left_val>0.5353189706802368</left_val> + <right_val>0.4387153983116150</right_val></_></_> + <_> + <!-- tree 106 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>7 7 4 3 -1.</_> + <_>7 8 4 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>3.7903659977018833e-003</threshold> + <left_val>0.5032002925872803</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>5 8 3 12 -1.</_> + <_>5 14 3 6 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0293696802109480</threshold> + <left_val>0.5873538851737976</left_val> + <right_val>0.2215445041656494</right_val></_></_> + <_> + <!-- tree 107 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>10 0 7 6 -1.</_> + <_>10 2 7 2 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>6.0743088833987713e-003</threshold> + <left_node>1</left_node> + <right_val>0.5417029857635498</right_val></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>2 1 18 1 -1.</_> + <_>8 1 6 1 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-0.0127107203006744</threshold> + <left_val>0.6056511998176575</left_val> + <right_val>0.4985181987285614</right_val></_></_> + <_> + <!-- tree 108 --> + <_> + <!-- root node --> + <feature> + <rects> + <_>5 0 3 8 -1.</_> + <_>6 0 1 8 3.</_></rects> + <tilted>0</tilted></feature> + <threshold>-5.9445449151098728e-003</threshold> + <left_val>0.3352069854736328</left_val> + <right_node>1</right_node></_> + <_> + <!-- node 1 --> + <feature> + <rects> + <_>4 7 4 2 -1.</_> + <_>4 8 4 1 2.</_></rects> + <tilted>0</tilted></feature> + <threshold>-2.8927479870617390e-003</threshold> + <left_val>0.6929240822792053</left_val> + <right_val>0.4778220057487488</right_val></_></_></trees> + <stage_threshold>53.7555694580078130</stage_threshold> + <parent>18</parent> + <next>-1</next></_></stages></haarcascade_frontalface_alt2> +</opencv_storage> diff --git a/examples/data/lbpcascade_frontalcatface.xml b/examples/data/lbpcascade_frontalcatface.xml new file mode 100644 index 00000000..4d8d8299 --- /dev/null +++ b/examples/data/lbpcascade_frontalcatface.xml @@ -0,0 +1,3336 @@ +<?xml version="1.0"?> +<!---------------------------------------------------------------------------- + A frontal cat face detector using LBP features. + + Contributed by Joseph Howse (josephhowse@nummist.com). + + More information can be found in the following publications and + presentations: + + Joseph Howse. OpenCV for Secret Agents (book). Packt Publishing, January + 2015. + Joseph Howse. "Training Detectors and Recognizers in Python and OpenCV" + (tutorial). ISMAR 2014. September 9, 2014. + http://nummist.com/opencv/Howse_ISMAR_20140909.pdf + Joseph Howse. "Training Intelligent Camera Systems with Python and OpenCV" + (webcast). O’Reilly Media. June 17, 2014. + http://www.oreilly.com/pub/e/3077 + + Build scripts and demo applications can be found in the following repository: + https://bitbucket.org/Joe_Howse/angora-blue + + KNOWN LIMITATIONS: + + Sometimes, the detector mistakenly thinks that a human face is a cat face. In + situations where either a human or a cat might be encountered, use both a + human face detector and a cat face detector. Then, if a detected human face + and a detected cat face intersect, reject the cat face. + + An upright subject is assumed. In situations where the cat's face might be + sideways or upside down (e.g. the cat is rolling over), try various rotations + of the input image. + + ////////////////////////////////////////////////////////////////////////// + | Contributors License Agreement + | 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. + | + | Copyright (c) 2014, Joseph Howse (Nummist Media Corporation Limited, + | Halifax, Nova Scotia, Canada). All rights reserved. + | + | Redistribution and use in source and binary forms, with or without + | modification, are permitted provided that the following conditions are + | met: + | + | * Redistributions of source code must retain the above copyright + | notice, this list of conditions and the following disclaimer. + | * Redistributions 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 Contributor may not 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 + | 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. Back to + | Top + ////////////////////////////////////////////////////////////////////////// + ----------------------------------------------------------------------------> +<opencv_storage> +<cascade> + <stageType>BOOST</stageType> + <featureType>LBP</featureType> + <height>24</height> + <width>24</width> + <stageParams> + <boostType>GAB</boostType> + <minHitRate>9.9900001287460327e-01</minHitRate> + <maxFalseAlarm>5.0000000000000000e-01</maxFalseAlarm> + <weightTrimRate>9.4999999999999996e-01</weightTrimRate> + <maxDepth>1</maxDepth> + <maxWeakCount>100</maxWeakCount></stageParams> + <featureParams> + <maxCatCount>256</maxCatCount> + <featSize>1</featSize></featureParams> + <stageNum>15</stageNum> + <stages> + <!-- stage 0 --> + <_> + <maxWeakCount>12</maxWeakCount> + <stageThreshold>-1.7687875032424927e+00</stageThreshold> + <weakClassifiers> + <_> + <internalNodes> + 0 -1 102 391095182 -138018897 -1311235414 -134348801 + -10637363 1363472332 -168428565 -170000676</internalNodes> + <leafValues> + -3.8902848958969116e-01 7.1630239486694336e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 266 135269620 1065172957 761273949 1068766640 + -285409281 -1075109889 -1073808385 -1073741825</internalNodes> + <leafValues> + -5.0905084609985352e-01 4.4736129045486450e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 248 -882714626 -11077637 -2105857 -5255203 1594642431 + -536896039 -540028929 266295295</internalNodes> + <leafValues> + -4.0175846219062805e-01 4.7943404316902161e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 5 -617089137 -547883529 1911919584 -2239534 1530067199 + 1140914475 -1092824836 1892022980</internalNodes> + <leafValues> + -4.2830348014831543e-01 4.3316614627838135e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 103 251265151 2145342812 1501453661 1046368729 + -1975908196 -1105592134 -1897996802 -88470342</internalNodes> + <leafValues> + -3.9837184548377991e-01 4.3966090679168701e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 249 653255651 -1427184958 -736368649 -1052429 + 1722766161 -455896464 1155912595 -167811855</internalNodes> + <leafValues> + -3.9872139692306519e-01 3.9076396822929382e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 98 -147601648 452990736 522407185 1599686417 1169689768 + 407507112 -1762308210 1599803223</internalNodes> + <leafValues> + -4.5285862684249878e-01 3.3382070064544678e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 300 805836816 1351616140 -1629160611 -1696006160 + 2133143824 -1418430667 -1093085224 -91160647</internalNodes> + <leafValues> + -5.1765918731689453e-01 2.8637027740478516e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 206 771753813 1072999924 -637907073 1072594908 + 1022435188 -1073874507 -1076936836 -1141628556</internalNodes> + <leafValues> + -4.5144259929656982e-01 3.3236411213874817e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 188 -1493176369 712675187 -55120016 -1570308126 + 1845183255 -34048033 -1090533504 -386469418</internalNodes> + <leafValues> + -3.6836385726928711e-01 4.1354060173034668e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 84 1876927247 -103435857 821334050 -202379046 + -607136374 354198146 1878387370 -455740474</internalNodes> + <leafValues> + -3.8681995868682861e-01 3.9221677184104919e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 85 70388767 580795691 -83467 1051082410 -287417474 + -1076176594 -57439237 -1430597793</internalNodes> + <leafValues> + -4.1789534687995911e-01 3.5533955693244934e-01</leafValues></_></weakClassifiers></_> + <!-- stage 1 --> + <_> + <maxWeakCount>13</maxWeakCount> + <stageThreshold>-1.3504111766815186e+00</stageThreshold> + <weakClassifiers> + <_> + <internalNodes> + 0 -1 52 -582353192 453631753 -1189740547 490856223 + -1064974120 -1927684129 1413925631 151519071</internalNodes> + <leafValues> + -2.2373910248279572e-01 6.7752838134765625e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 213 -576729857 454575443 -1996943617 536547583 + 206047452 -1088734277 210521343 264241151</internalNodes> + <leafValues> + -3.7819463014602661e-01 5.2549731731414795e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 275 -249562832 -1181502479 -174589519 -1112427119 + -1189167448 -1081345300 -1534158677 -1431310669</internalNodes> + <leafValues> + -3.6486798524856567e-01 4.7923672199249268e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 125 -1405482952 -15924365 -1147527681 -10535319 + -1345282310 -1103133 698354938 -1073790989</internalNodes> + <leafValues> + -4.9668836593627930e-01 3.3489266037940979e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 82 2135911182 -553255561 -1835761112 -744206088 + 1443360719 1427151980 -208273409 -136971281</internalNodes> + <leafValues> + -4.9673599004745483e-01 2.9186215996742249e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 64 750650461 -1141224741 228524031 1066678329 + -392530945 -1119829 -389633282 -1909948481</internalNodes> + <leafValues> + -4.4474920630455017e-01 3.3087861537933350e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 80 -8364 961050640 -337450 -170150085 -4518372 + 1480613384 -270925857 -203948193</internalNodes> + <leafValues> + -3.6774283647537231e-01 3.8125535845756531e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 304 -1279559723 488853328 894958865 523048208 + -1111801861 -1113625095 -1410843652 -1615113099</internalNodes> + <leafValues> + -3.8107365369796753e-01 3.7617510557174683e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 27 -1038891372 866262272 -1124866827 -7307515 2556056 + -1947784189 145625549 -537934001</internalNodes> + <leafValues> + -3.3774635195732117e-01 4.1467228531837463e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 214 1313841735 674152170 -1368464720 -1597048870 + 759309825 -295860621 1709856085 -587729545</internalNodes> + <leafValues> + -4.4100293517112732e-01 3.1954705715179443e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 310 -1432731787 -1219055176 -42533035 -1126253868 + -2038280772 -55715078 -2000923144 -1936931520</internalNodes> + <leafValues> + -4.3541839718818665e-01 3.1401738524436951e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 180 232552309 -537305729 -3104297 -67617446 -1211085572 + -20391173 -1968567044 -1392871087</internalNodes> + <leafValues> + -3.7881413102149963e-01 3.5767501592636108e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 46 -36984309 904630143 823414970 -188744006 1900770270 + 1399711367 -473188378 1969748707</internalNodes> + <leafValues> + -4.0620720386505127e-01 3.2759186625480652e-01</leafValues></_></weakClassifiers></_> + <!-- stage 2 --> + <_> + <maxWeakCount>19</maxWeakCount> + <stageThreshold>-1.3998974561691284e+00</stageThreshold> + <weakClassifiers> + <_> + <internalNodes> + 0 -1 128 -236980233 16777207 -520752904 15793139 -168428203 + 1979187191 -33554947 -251920389</internalNodes> + <leafValues> + -1.7629407346248627e-01 6.5313565731048584e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 230 -871825409 -580752385 -713042433 -540061189 + 1549092351 -587761733 1607687679 1577014783</internalNodes> + <leafValues> + -3.1553706526756287e-01 4.9262753129005432e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 79 -545306406 -547422721 474111600 -570360321 -507816 + 1599364180 -11379845 -12845089</internalNodes> + <leafValues> + -3.5872745513916016e-01 4.0607807040214539e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 123 -252337502 -658267397 7348400 821935103 1377280435 + 47000107 -169347101 939467707</internalNodes> + <leafValues> + -5.1070415973663330e-01 2.8583043813705444e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 45 645859107 708031411 -168346241 -1330581136 81734679 + 783767351 -45193 -34341517</internalNodes> + <leafValues> + -4.4308465719223022e-01 2.9009637236595154e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 241 -154214396 268437506 -680474855 864167714 684199820 + 204876327 -52122180 -262209</internalNodes> + <leafValues> + -4.1140288114547729e-01 2.9478433728218079e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 265 -1365070662 -380938 -1139629826 -35735556 49021040 + 1020949984 219416278 -293</internalNodes> + <leafValues> + -3.9993125200271606e-01 2.9704034328460693e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 295 217001265 -1346379851 1862205053 -1342447907 + 30277700 -1360008036 -1988349004 -287317952</internalNodes> + <leafValues> + -3.9956006407737732e-01 2.8518736362457275e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 72 290390927 534606847 534719466 -100668033 1607942091 + 1499327373 -101712435 -187324532</internalNodes> + <leafValues> + -3.0478826165199280e-01 3.7477290630340576e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 136 -285336459 2110655990 -2013274145 2143051605 + -1124443700 -9506961 -604210584 -1161806512</internalNodes> + <leafValues> + -4.0837058424949646e-01 2.7444043755531311e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 55 -62093618 -581362746 -141373441 -10940834 -840543755 + -638181765 1859448799 996361303</internalNodes> + <leafValues> + -3.7610772252082825e-01 3.0961802601814270e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 110 -404233212 134685696 -2016460609 -1379932177 + -540018825 -2163729 806790835 607646519</internalNodes> + <leafValues> + -4.3601146340370178e-01 2.5886246562004089e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 288 -907025790 1474249959 -806494209 -10763 -283444824 + -364917254 -828244229 -1968201565</internalNodes> + <leafValues> + -2.6032009720802307e-01 4.3652611970901489e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 229 -18370769 -757680277 788372142 -2072580242 + -273678553 745365463 -134222107 -229310606</internalNodes> + <leafValues> + -3.0938607454299927e-01 3.5719990730285645e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 14 -1603048865 578088863 -390533121 140676846 + -495489499 -42082971 -4194586 -26017865</internalNodes> + <leafValues> + -3.1977957487106323e-01 3.4310647845268250e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 249 1643111394 -1211240000 1172894578 -641010466 + 1181712375 -2068614428 1433624934 -671119917</internalNodes> + <leafValues> + -3.3919364213943481e-01 3.4504517912864685e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 164 1606136599 587595301 -21521312 1079307891 62912316 + 2136471350 -285346140 -201854089</internalNodes> + <leafValues> + -3.1577944755554199e-01 3.4307131171226501e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 152 785525471 1052180799 1060533999 2120755670 + 1316621908 -270833354 -73736274 -361439920</internalNodes> + <leafValues> + -3.4840318560600281e-01 3.2924604415893555e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 47 1824524292 1056221828 -268623876 -1615033532 + -2092452655 144493033 502744181 -571159100</internalNodes> + <leafValues> + -4.5537719130516052e-01 2.4885603785514832e-01</leafValues></_></weakClassifiers></_> + <!-- stage 3 --> + <_> + <maxWeakCount>17</maxWeakCount> + <stageThreshold>-1.6478537321090698e+00</stageThreshold> + <weakClassifiers> + <_> + <internalNodes> + 0 -1 99 1126020910 939511791 -137494546 -134218002 928378510 + 667815688 -131090 -21878</internalNodes> + <leafValues> + -1.9173553586006165e-01 6.1490827798843384e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 277 1066647483 -1078215472 -1078199809 -515 -1078280194 + -1079276888 -1112801281 -1077956952</internalNodes> + <leafValues> + -2.5890102982521057e-01 6.0596489906311035e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 280 -1347239939 -1078263848 -6291491 -1073954860 + -1883246595 -1075838997 -1346850307 -1393015372</internalNodes> + <leafValues> + -3.5419720411300659e-01 3.9201220870018005e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 148 -16582767 990936848 -15337711 1024534289 522275741 + 530061615 1786740027 2132761183</internalNodes> + <leafValues> + -4.0790781378746033e-01 3.3604335784912109e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 33 -1024790785 -33673381 -715534345 -5787889 -721957121 + -17850629 -654587205 -27048190</internalNodes> + <leafValues> + -3.0084383487701416e-01 4.4991242885589600e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 173 779038223 578498431 -26263553 -1430717601 + 1426011767 1870118397 -726064396 -184550929</internalNodes> + <leafValues> + -4.3469619750976562e-01 2.9388919472694397e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 207 11804501 -1074249744 2143289343 -1359077380 + 2074886065 -262692 -1075003396 -1431820272</internalNodes> + <leafValues> + -4.1263562440872192e-01 2.8357046842575073e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 263 -620104768 -83232282 -548406795 -1074143767 + -828717122 -21770245 251698105 68943602</internalNodes> + <leafValues> + -3.8648277521133423e-01 3.0467325448989868e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 161 1391411790 41936415 -228930992 1892677067 + 1987802112 1480885434 -1362 -219942962</internalNodes> + <leafValues> + -3.7596645951271057e-01 3.1412878632545471e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 308 248452913 1071429554 -251663105 -538972499 + -1928628395 -1346437645 -1745150348 -33596064</internalNodes> + <leafValues> + -3.9380916953086853e-01 2.8952071070671082e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 182 626985985 930297719 -286325569 -147866829 + -692197027 2138802865 -117701012 -118164992</internalNodes> + <leafValues> + -3.5818240046501160e-01 3.2363671064376831e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 60 -1361623937 -1149491078 5825754 508056955 -88210408 + -655077264 -1174209542 -33408130</internalNodes> + <leafValues> + -3.4054177999496460e-01 3.2592311501502991e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 184 1311738415 -1375274001 1726672390 -829228034 + 2070837764 1208920803 -67308591 -419954764</internalNodes> + <leafValues> + -3.7184986472129822e-01 2.8876912593841553e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 1 -566173704 -81765536 -328487245 -545227007 8650948 + -1342492099 8915104 -307505157</internalNodes> + <leafValues> + -3.2362362742424011e-01 3.4028744697570801e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 223 787644405 1052643324 -671622145 1060113936 + -1543669260 -621052931 497506684 -1094759403</internalNodes> + <leafValues> + -3.8580575585365295e-01 2.8625565767288208e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 90 -754941560 -598092101 -1244290303 281751211 + 1463866633 1153550863 -806946917 38740739</internalNodes> + <leafValues> + -4.4092047214508057e-01 2.5595286488533020e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 167 184294525 -9037826 -21186597 -1497857980 + -1372058532 -1233225231 -341673474 -1099675796</internalNodes> + <leafValues> + -3.7506091594696045e-01 2.9585087299346924e-01</leafValues></_></weakClassifiers></_> + <!-- stage 4 --> + <_> + <maxWeakCount>21</maxWeakCount> + <stageThreshold>-1.4008288383483887e+00</stageThreshold> + <weakClassifiers> + <_> + <internalNodes> + 0 -1 233 -814746846 -2111111254 -553913104 -2097922 + 2004869959 1141046786 1970763104 -134744334</internalNodes> + <leafValues> + -2.1097929775714874e-01 5.7671958208084106e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 75 -8396836 -538976473 -16384001 -10879489 -8397857 + -10498599 -45416465 1595899679</internalNodes> + <leafValues> + -3.1942996382713318e-01 4.6969848871231079e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 4 -824512541 -524299 -344077 -72323247 206048519 + -1409589837 -84975873 -805875917</internalNodes> + <leafValues> + -3.0280569195747375e-01 4.2180880904197693e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 290 -1086615304 956872912 223108216 496889077 + -1097860099 956429808 1601034749 -538976769</internalNodes> + <leafValues> + -3.5371550917625427e-01 3.6481952667236328e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 146 -85985781 1914665473 1351604943 269482959 + -118967717 -264571342 -71633409 -1574764609</internalNodes> + <leafValues> + -3.5075160861015320e-01 3.4602758288383484e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 81 -579450864 489695248 -568994533 -671905287 + 1484394168 -21040594 -1132978498 -4358</internalNodes> + <leafValues> + -3.6514815688133240e-01 3.3418157696723938e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 300 -1071640576 -1794071852 -589571267 -622299971 + -128680624 -107152175 -1128604712 -93782371</internalNodes> + <leafValues> + -4.3749809265136719e-01 2.3641848564147949e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 43 1347789474 278788859 1078009887 1450704633 755482275 + -890279806 1601682923 1145430595</internalNodes> + <leafValues> + -4.5889991521835327e-01 2.1458856761455536e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 58 -67811136 507778012 2090770170 940627838 -547680164 + -564395008 -1507362 -15533189</internalNodes> + <leafValues> + -3.4613710641860962e-01 2.9729354381561279e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 40 708451453 915939127 2086399799 -1364049961 + -111247856 -278977 -562528515 -1432293672</internalNodes> + <leafValues> + -4.1620507836341858e-01 2.4089127779006958e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 279 -1423966541 177905074 -199210 -1914114123 + 1966529760 -2068908586 -574294864 -841165576</internalNodes> + <leafValues> + -3.1373840570449829e-01 3.2040333747863770e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 198 1848585743 -1940984369 1618804030 -285289836 + 1994084869 67912305 -4262128 -303498266</internalNodes> + <leafValues> + -3.6098247766494751e-01 2.7308923006057739e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 83 -34593 -42134437 83156217 534796439 -206958426 + -542489456 -301270341 -1910021</internalNodes> + <leafValues> + -2.1900075674057007e-01 4.6534782648086548e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 255 -732498730 808132612 -1694705650 637674514 + -856846932 790002977 -1523128100 -5768257</internalNodes> + <leafValues> + -3.0635869503021240e-01 3.2308223843574524e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 142 -272658653 -2039881873 -1093759223 -295772198 + 316478038 1219928242 1542411604 -523960875</internalNodes> + <leafValues> + -4.1005435585975647e-01 2.4376337230205536e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 314 -1886651248 233035768 -579887888 -644481803 + -1924527808 72215888 -813972012 -838860849</internalNodes> + <leafValues> + -3.2710522413253784e-01 3.0177840590476990e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 109 -626385315 -541893255 -289637249 -2982538 + -285318128 -335644678 -934750724 -1937614</internalNodes> + <leafValues> + -3.0264344811439514e-01 3.4333297610282898e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 127 -675308008 858262544 -546392130 -86565344 889738476 + 1024209296 -539034113 -17892421</internalNodes> + <leafValues> + -3.0164864659309387e-01 3.2610884308815002e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 76 289874895 1574889454 356325874 -1257339986 + 2013148111 1523362314 -209587256 -51584276</internalNodes> + <leafValues> + -3.0728715658187866e-01 3.2666257023811340e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 191 207364212 -1629723097 -69798913 180098791 445713016 + -1342538933 -112443395 -1436022512</internalNodes> + <leafValues> + -4.8653569817543030e-01 2.1469378471374512e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 316 -1891992271 534502897 -709725931 -1133905467 + -1625325679 -1098169635 -1377993223 -1175556795</internalNodes> + <leafValues> + -3.6775574088096619e-01 2.6944977045059204e-01</leafValues></_></weakClassifiers></_> + <!-- stage 5 --> + <_> + <maxWeakCount>20</maxWeakCount> + <stageThreshold>-1.6370692253112793e+00</stageThreshold> + <weakClassifiers> + <_> + <internalNodes> + 0 -1 246 -544546902 -40378434 1166794751 -553126475 + 1124863438 -6684681 252464271 252663279</internalNodes> + <leafValues> + -1.7677198350429535e-01 5.9283459186553955e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 105 -1688231254 -136322578 542408738 -167781650 + -284181590 88186882 -74646534 -254480182</internalNodes> + <leafValues> + -4.8250266909599304e-01 3.2293373346328735e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 291 -1087113223 -1153788752 923893117 -40019499 + -1444151361 -1145394000 520297981 -7610887</internalNodes> + <leafValues> + -2.7269113063812256e-01 4.6762999892234802e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 163 1431698391 477399807 5291197 215258107 2097218267 + 1070271552 1347764095 1497759739</internalNodes> + <leafValues> + -3.6439743638038635e-01 3.3937779068946838e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 49 -494878000 -1408572168 -1216184843 -570753839 + -1385631492 -1572929 -37698091 -140075</internalNodes> + <leafValues> + -3.4019106626510620e-01 3.2104432582855225e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 101 -15196934 2140689563 1546672602 1591347871 + 1544035576 488135152 2142261182 -4259841</internalNodes> + <leafValues> + -3.7342280149459839e-01 2.8317087888717651e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 195 -357586129 70201151 -292424585 -1468006937 + 1163158101 45494091 -492310284 -184618257</internalNodes> + <leafValues> + -4.2928436398506165e-01 2.4182404577732086e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 219 -346034385 -1509510400 -590396300 -56691898 + -2013314811 -11148442 -790137804 -17301901</internalNodes> + <leafValues> + -3.2902050018310547e-01 3.1380781531333923e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 276 51573280 869252567 856020959 2136817673 -1825854798 + 1786292453 1863958011 51070032</internalNodes> + <leafValues> + -4.4989612698554993e-01 2.3105476796627045e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 239 221945781 -1351824656 -369627303 -1616906768 + 180205816 -1398111810 986511580 -2002860624</internalNodes> + <leafValues> + -3.3374428749084473e-01 3.0387389659881592e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 88 -12577 1384049145 -1464090918 -265274512 1423314444 + -330608884 -134684970 -95228557</internalNodes> + <leafValues> + -2.9424193501472473e-01 3.4667330980300903e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 215 -349179217 304152230 -725813534 -44849422 206658671 + 1343674791 1143817582 1414000887</internalNodes> + <leafValues> + -3.5459104180335999e-01 2.8464645147323608e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 44 2135942447 398828943 961557154 -85264382 -9441329 + 191609019 -135559186 1155988198</internalNodes> + <leafValues> + -3.9276805520057678e-01 2.5532895326614380e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 322 792354069 -1442292348 -725922817 -1075650752 + -1345299339 -270808291 1585978709 -1363456899</internalNodes> + <leafValues> + -4.0223011374473572e-01 2.3934543132781982e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 51 -963985190 526195161 -632047017 -13433768 -170216210 + -584509261 -1147462405 2122007633</internalNodes> + <leafValues> + -3.5836115479469299e-01 2.7841308712959290e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 69 1282640527 -221028405 -1115770991 -1312815327 + 518459295 -5010698 -8421409 -52340448</internalNodes> + <leafValues> + -3.0360385775566101e-01 3.3305782079696655e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 63 240090463 1543015448 1657994909 1376934782 + 2121953054 -3393448 774963198 -364875945</internalNodes> + <leafValues> + -3.1514179706573486e-01 3.1063860654830933e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 149 -117441008 272437552 -2011728848 808844177 + -115035003 901066096 -392587048 829815772</internalNodes> + <leafValues> + -4.1897901892662048e-01 2.3548045754432678e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 41 -285250029 -1342531469 -1337688454 76061301 + 2140621344 -26490526 -230505742 -498673325</internalNodes> + <leafValues> + -3.7328067421913147e-01 2.7277800440788269e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 186 197109511 113539807 -1663738276 652405614 + 2079664708 -1381909716 -83887277 -340414799</internalNodes> + <leafValues> + -3.5890376567840576e-01 2.7977034449577332e-01</leafValues></_></weakClassifiers></_> + <!-- stage 6 --> + <_> + <maxWeakCount>24</maxWeakCount> + <stageThreshold>-1.3530069589614868e+00</stageThreshold> + <weakClassifiers> + <_> + <internalNodes> + 0 -1 231 -607125512 -203956738 -146407939 -549587593 + 2106032085 -7015967 1296916047 1465901007</internalNodes> + <leafValues> + -1.2551400065422058e-01 6.0037857294082642e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 126 -12041 289127191 -1961178373 858913587 -452995643 + -573056619 -184946437 811859443</internalNodes> + <leafValues> + -3.1659016013145447e-01 4.0386086702346802e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 305 -4456453 -1079345488 1601271281 1566430416 + -1145044997 -1363497544 -807547663 -846209539</internalNodes> + <leafValues> + -2.4047850072383881e-01 4.9467754364013672e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 205 -422843738 730826189 -93007646 -1561338070 + 1156254860 82792447 -359691576 -318767125</internalNodes> + <leafValues> + -4.0942522883415222e-01 2.8118029236793518e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 130 286242991 1430640951 -1315388758 -5384739 + 1897658847 1435892732 -241122312 -190447622</internalNodes> + <leafValues> + -3.6675310134887695e-01 2.7747273445129395e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 155 -360711817 1044065557 -202645505 -1078462473 + -554253058 -123796300 -991681292 -117447077</internalNodes> + <leafValues> + -2.2938863933086395e-01 4.3152013421058655e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 33 -486546689 -38568697 -114385161 -1078244605 + -1398768130 -16859405 -387455605 -95221245</internalNodes> + <leafValues> + -2.5292310118675232e-01 4.0669292211532593e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 197 -347108861 707787566 -271712714 -286066182 + 1660625669 23327763 -118368650 -16781581</internalNodes> + <leafValues> + -3.2693040370941162e-01 2.9912397265434265e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 48 44773155 -1400496480 -2013315201 -2130179 1928269391 + 2146108387 1871512017 -2109991</internalNodes> + <leafValues> + -3.1153312325477600e-01 3.0730357766151428e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 277 817397681 -4651712 -1074593921 -4489223 -1464975366 + -1080350544 1068207931 -1414856608</internalNodes> + <leafValues> + -2.7505692839622498e-01 3.4677764773368835e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 158 -805311870 -1362548369 -186736224 -220270666 + 1742698308 1147947264 -251792176 -789512618</internalNodes> + <leafValues> + -3.9942753314971924e-01 2.4047489464282990e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 65 -700036014 -675626240 648044818 -69944464 -389395237 + -215288017 -1719763715 -12588054</internalNodes> + <leafValues> + -3.1749448180198669e-01 3.0149078369140625e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 319 765664309 -1401170476 -22282241 -1880818860 + -1688316571 -52824373 -1730200112 -889291499</internalNodes> + <leafValues> + -3.5558241605758667e-01 2.6557549834251404e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 272 -1164201800 -1716289100 -378039852 -1409579788 + -1121338919 -1880520559 932112367 -1869871980</internalNodes> + <leafValues> + -3.6485251784324646e-01 2.5734969973564148e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 22 -21049601 -25866473 325725 1043995470 1592678894 + -269259937 -971837858 -8913089</internalNodes> + <leafValues> + -2.2092992067337036e-01 4.2716163396835327e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 138 763233167 2004741991 -277961986 -35997914 897543679 + 1741158903 -50331667 -1578882304</internalNodes> + <leafValues> + -3.0084916949272156e-01 3.0936580896377563e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 92 66036611 -2133412515 2136683646 -184645734 + 1604280073 202711897 -1379383 -186590399</internalNodes> + <leafValues> + -3.7059250473976135e-01 2.4178710579872131e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 196 -326382376 361846096 -2045590179 1068964371 + 705563792 -1156488704 1208497115 994048507</internalNodes> + <leafValues> + -3.2299432158470154e-01 2.8366291522979736e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 70 -412097022 -1568018524 -1342448718 -1253932 + -1260185663 1258275150 -2067820288 -138937659</internalNodes> + <leafValues> + -3.5679051280021667e-01 2.6213440299034119e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 193 -714615533 270672129 -306258157 -1758339253 + 47808742 -1902768889 -1941903362 -893526249</internalNodes> + <leafValues> + -2.5351437926292419e-01 3.6361116170883179e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 165 -318796273 1227399022 -1369769360 -675875878 + -344383672 1582766693 -86017024 -344793116</internalNodes> + <leafValues> + -3.1456202268600464e-01 3.0873265862464905e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 93 -1045815078 -235892117 -571835429 -3460789 + -108525848 -1332172392 -1503517894 -1300229375</internalNodes> + <leafValues> + -3.5038644075393677e-01 2.6677846908569336e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 15 -1976677263 3726998 -392242219 214976380 -1048834204 + -44384235 -81855380 -1434428367</internalNodes> + <leafValues> + -4.1762107610702515e-01 2.1475161612033844e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 166 209059956 2146573530 1933758719 -1670893344 + -1206231472 -538281424 -605128624 -88975308</internalNodes> + <leafValues> + -4.3403875827789307e-01 2.1460674703121185e-01</leafValues></_></weakClassifiers></_> + <!-- stage 7 --> + <_> + <maxWeakCount>26</maxWeakCount> + <stageThreshold>-1.3850060701370239e+00</stageThreshold> + <weakClassifiers> + <_> + <internalNodes> + 0 -1 250 1118499839 -796332098 -134253196 -6424581 259721215 + -1671186434 100533499 1170700287</internalNodes> + <leafValues> + -1.3170924782752991e-01 5.9910702705383301e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 94 1570566140 2106702270 -577478657 -9625925 -36328709 + -75449636 -394325 1071454459</internalNodes> + <leafValues> + -3.8424813747406006e-01 3.6834198236465454e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 278 -136315975 -1187792752 1568109937 -571788877 + -86048838 -1433673030 -1190563400 -1984968261</internalNodes> + <leafValues> + -2.4970491230487823e-01 4.7245621681213379e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 155 -25692619 573911103 -1090519553 -1074266633 + -1124204292 -1199619017 -387151619 -1363171157</internalNodes> + <leafValues> + -2.7256563305854797e-01 4.0413850545883179e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 299 270536720 -82027755 -542052353 -744834006 + -172433515 -36839897 -2048335877 -1356137589</internalNodes> + <leafValues> + -3.7769773602485657e-01 2.3510186374187469e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 102 133774 1573843455 290464424 -173151905 771865036 + 1430562872 -109382744 -246039832</internalNodes> + <leafValues> + -4.7037139534950256e-01 2.0533446967601776e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 127 -618460144 -1256386552 -1618575940 1972721552 + 469800920 504635776 -846791686 -69254485</internalNodes> + <leafValues> + -3.5127875208854675e-01 2.5741419196128845e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 85 150343455 40910711 -1248068641 -1430372421 + -1895949361 -21272033 -331755522 -1431642114</internalNodes> + <leafValues> + -3.7984871864318848e-01 2.3182238638401031e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 286 -13916067 -237157163 1069283741 -7496371 + -1771992641 -17192227 -1383805025 -2004252467</internalNodes> + <leafValues> + -2.1929037570953369e-01 3.8296228647232056e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 206 9705301 508957948 -71469059 431911901 2041834229 + -1141023756 -38047748 -1432742364</internalNodes> + <leafValues> + -3.4404903650283813e-01 2.3727993667125702e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 162 -28187574 1457201967 -1799949806 542855772 + -22592508 347566595 -86051214 -254279713</internalNodes> + <leafValues> + -3.3773353695869446e-01 2.3899486660957336e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 78 1353317010 -353449217 14955059 233737419 1113314227 + 1272002592 2140215167 200230499</internalNodes> + <leafValues> + -3.5864931344985962e-01 2.1981315314769745e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 50 -592281584 1487966200 -1782854276 -34165307 + -911392552 -588393496 -86945955 -302569115</internalNodes> + <leafValues> + -2.9026558995246887e-01 2.8104048967361450e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 301 -14053072 -2064062200 -928567823 -983051523 + 201916593 150423153 -1859132687 -1929383993</internalNodes> + <leafValues> + -3.2115238904953003e-01 2.5486564636230469e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 79 -607531822 2133030428 1045592120 922799991 + -1958080364 -631876516 -551887109 -116138161</internalNodes> + <leafValues> + -2.5716596841812134e-01 3.1999784708023071e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 7 -1897370238 2088280021 -27542220 955523029 143789824 + -1732421377 -270311431 -319465097</internalNodes> + <leafValues> + -3.7310892343521118e-01 2.2297158837318420e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 151 2117994623 -596493511 -42869249 2123389533 + -102873516 -1194255940 -1077870956 -1369933454</internalNodes> + <leafValues> + -3.2065725326538086e-01 2.5838941335678101e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 141 -402659162 81777018 894353956 -1095917410 + 1470100164 1429538625 -675679616 22405077</internalNodes> + <leafValues> + -3.9470222592353821e-01 2.1365866065025330e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 306 371408657 -1390197839 -610304075 -1084394735 + 740911518 -1442850385 1025329500 -1085287308</internalNodes> + <leafValues> + -3.5310438275337219e-01 2.2878694534301758e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 96 1411399848 1607023650 2118667161 2115963630 + 825267416 -1300086616 866705067 1593838595</internalNodes> + <leafValues> + -3.7212049961090088e-01 2.1682462096214294e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 236 -1023751505 1654647882 654289424 -221611870 + 1142643327 2865238 1353434162 -218116589</internalNodes> + <leafValues> + -3.0895259976387024e-01 2.7360698580741882e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 25 263714691 -1500250442 -572970029 1029922655 + 461194497 -1384691616 902512981 -831890189</internalNodes> + <leafValues> + -2.8019675612449646e-01 2.8230005502700806e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 157 -1009275158 703439343 1881063906 1407643120 + 1357622212 1698779727 -175905592 -799737222</internalNodes> + <leafValues> + -3.0597987771034241e-01 2.6828068494796753e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 24 -655102849 1007864659 -1544094705 -1540313220 + -574699 -47065227 -1091975362 -278732953</internalNodes> + <leafValues> + -2.5672450661659241e-01 3.1667667627334595e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 103 347621596 1037305260 358617181 753140063 -61713220 + -610393186 -1106322953 -1350956022</internalNodes> + <leafValues> + -3.4445220232009888e-01 2.3471401631832123e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 36 -923349290 256096042 219291806 -1155987134 + 2086406828 1508909094 -2007541485 1887403703</internalNodes> + <leafValues> + -3.4530115127563477e-01 2.3805907368659973e-01</leafValues></_></weakClassifiers></_> + <!-- stage 8 --> + <_> + <maxWeakCount>25</maxWeakCount> + <stageThreshold>-1.2224140167236328e+00</stageThreshold> + <weakClassifiers> + <_> + <internalNodes> + 0 -1 252 -22088450 -5325374 -33282 -258 12993776 67429556 + 1976988926 2005923583</internalNodes> + <leafValues> + -1.9573126733303070e-01 5.5536717176437378e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 216 -941896961 288549683 -965260547 1902502507 + 1287981311 723742655 1157582079 1173354239</internalNodes> + <leafValues> + -3.0669313669204712e-01 3.6197665333747864e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 62 -571494657 -2099490 -25243649 -1082480054 2115633354 + -130996 -329130274 -67419570</internalNodes> + <leafValues> + -2.4500623345375061e-01 4.3682980537414551e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 19 -704647185 2113917365 -428522838 -141168226 + 2123362255 2006664827 -254348594 1459088068</internalNodes> + <leafValues> + -3.4159252047538757e-01 2.9353541135787964e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 285 -138414285 -168045645 -550635909 -240708569 + -36728913 -33554641 -890270582 -2069142366</internalNodes> + <leafValues> + -2.8817924857139587e-01 3.4106674790382385e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 77 -220544510 -184926009 2147213135 -209757985 + -1177821558 -139002180 845151099 -8701317</internalNodes> + <leafValues> + -2.2831186652183533e-01 3.8085940480232239e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 256 12976195 569051735 -1044464777 -2131832853 + -1863693873 -889525281 -1060633090 -1024468017</internalNodes> + <leafValues> + -3.4035223722457886e-01 2.4870637059211731e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 225 243431285 -1342397538 -1074439183 -198404 + -1147339787 -1950366979 -1176535299 -1346857836</internalNodes> + <leafValues> + -2.9916274547576904e-01 2.7878564596176147e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 188 -389107962 -1496674445 -1433293856 1650520822 + -808004780 -340166498 -270808076 -327882656</internalNodes> + <leafValues> + -3.6523097753524780e-01 2.3002453148365021e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 249 -1561660693 -1964116758 -433068176 -674252332 + 76465114 -86789644 1441781719 -167788045</internalNodes> + <leafValues> + -2.6452550292015076e-01 3.1857562065124512e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 30 83758542 1352905533 -25477260 888412977 1174342621 + -581240613 -1877419 954559493</internalNodes> + <leafValues> + -3.9430552721023560e-01 2.1380217373371124e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 291 1060338993 -1618339632 1411411797 -1111518927 + 1038801631 -1171216200 431569585 -1627714192</internalNodes> + <leafValues> + -3.1174781918525696e-01 2.5848281383514404e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 174 1272433155 241160959 -187054076 -1428167714 + 1656705620 178680903 -188744156 -92279086</internalNodes> + <leafValues> + -3.5104271769523621e-01 2.4124261736869812e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 100 276007552 1448970891 1624277552 1433388541 + -144262781 203467264 -747113541 1187367471</internalNodes> + <leafValues> + -4.3640381097793579e-01 2.0373314619064331e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 265 514492090 -1084506382 802327294 -574652997 + -1951495942 -1115800400 -1734105369 -745080833</internalNodes> + <leafValues> + -2.6869311928749084e-01 3.1262946128845215e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 238 975568369 -1078429260 -1073778819 -1246039248 + -566763547 -574710393 -1879731715 -1363518280</internalNodes> + <leafValues> + -2.5506082177162170e-01 3.2470330595970154e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 116 -197130578 -69435407 25570034 -1282381057 + -736477475 1078203406 1072176808 -224107914</internalNodes> + <leafValues> + -3.3984503149986267e-01 2.3857665061950684e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 170 2006936207 2046780255 -906124674 -571018634 + 465548283 1995414095 -469838166 -53152636</internalNodes> + <leafValues> + -2.3337669670581818e-01 3.4638467431068420e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 261 -1993607757 -1984171376 -1964620068 -1377319864 + -878651982 2100351409 -540935193 -846344047</internalNodes> + <leafValues> + -2.5745591521263123e-01 3.1530505418777466e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 129 -585119904 520350737 -1602237728 387020659 + -316697172 -809628610 -341521253 1937239575</internalNodes> + <leafValues> + -3.0576938390731812e-01 2.6786401867866516e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 303 -48760835 -1382112048 1192391029 185356020 + 783760374 -1402171184 959454513 -1431708544</internalNodes> + <leafValues> + -2.8466665744781494e-01 3.0237734317779541e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 321 700150833 -1366030659 -590225483 -1393138060 + -1364311531 -1142207587 2140558729 -1370849424</internalNodes> + <leafValues> + -3.7198981642723083e-01 2.3002536594867706e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 228 -1054166 -201341212 1923367016 1916986952 + 1992519431 1213361995 1174402160 -1068174746</internalNodes> + <leafValues> + -3.4979847073554993e-01 2.4561876058578491e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 106 -1907696549 -268565315 -1342514689 336742770 + -1439790339 -1301623122 -1342275841 -1440064298</internalNodes> + <leafValues> + -2.6577013731002808e-01 3.1544610857963562e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 210 -257776636 1915752044 -1649103180 -1095126373 + 1422147805 210473965 2022068463 1660109363</internalNodes> + <leafValues> + -3.7758591771125793e-01 2.2004681825637817e-01</leafValues></_></weakClassifiers></_> + <!-- stage 9 --> + <_> + <maxWeakCount>28</maxWeakCount> + <stageThreshold>-1.3550500869750977e+00</stageThreshold> + <weakClassifiers> + <_> + <internalNodes> + 0 -1 273 1595195384 1608363967 1595766713 -572674083 + 1226391547 -44180227 2111155631 531574527</internalNodes> + <leafValues> + -1.6430117189884186e-01 5.4418802261352539e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 39 -5377 -57601 538639103 -347397 -17301286 -4259105 + -79502593 -77380901</internalNodes> + <leafValues> + -1.8002209067344666e-01 5.2194720506668091e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 177 1566441471 2103287797 492075325 500554077 + 1998422011 -155652 192249721 1565917179</internalNodes> + <leafValues> + -2.7448469400405884e-01 3.6767318844795227e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 3 -524550921 -1669383407 -574620225 -33590459 143458441 + 256708119 -805314577 -6324385</internalNodes> + <leafValues> + -2.3465685546398163e-01 4.0192386507987976e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 71 -846691120 1439476212 -690689 -2237091 -67570179 + -36374787 -2105923 -143907</internalNodes> + <leafValues> + -2.7146762609481812e-01 3.2002952694892883e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 294 263139089 -581452515 2010774399 -2106799 477955253 + -72357417 267875276 -1097712</internalNodes> + <leafValues> + -3.5161617398262024e-01 2.3248630762100220e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 302 -15214854 -1884686344 167600312 -581052707 37857013 + 202028220 1305828853 -840957953</internalNodes> + <leafValues> + -2.6214873790740967e-01 3.0466583371162415e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 169 613396271 2003697295 1738141229 -35000540 + 2147446463 2131993840 -10096690 1690603136</internalNodes> + <leafValues> + -3.2117179036140442e-01 2.4732810258865356e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 281 -1355025965 -1961980240 -671219723 -283264688 + -1936837383 -1952736320 -908474131 -875701004</internalNodes> + <leafValues> + -2.4713008105754852e-01 3.1776627898216248e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 232 -894441054 -1880167002 -853281294 -338827028 + 1348301603 877440640 1936192066 -135400158</internalNodes> + <leafValues> + -3.2655048370361328e-01 2.4388861656188965e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 288 -553260894 -155221559 -13927175 -570440513 + -23332102 -93644167 -1079190789 -1431330575</internalNodes> + <leafValues> + -2.4785453081130981e-01 3.1448525190353394e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 190 672423253 -570534469 -174826505 -1074210146 + -1158800264 -67208458 802954712 -1079507880</internalNodes> + <leafValues> + -3.1213754415512085e-01 2.4356111884117126e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 120 -656932646 -651487779 -956164148 2064599515 + -136440708 -588512112 -1232981444 -1979159765</internalNodes> + <leafValues> + -3.0449146032333374e-01 2.5301957130432129e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 243 1649084931 672964650 -1105808049 713205535 + 2036285460 -666148121 -55162040 -18875393</internalNodes> + <leafValues> + -3.4271252155303955e-01 2.1502359211444855e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 11 -152656190 868356183 -501591426 -201146046 + -546953661 1982915527 -759837225 -751304729</internalNodes> + <leafValues> + -3.6129125952720642e-01 2.0629832148551941e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 107 -537765384 1060665394 2014012895 -1179616664 + -22202433 -1074072677 -147975202 -1147076845</internalNodes> + <leafValues> + -2.9889339208602905e-01 2.5862032175064087e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 199 -328257785 -858932225 -530764942 -826874930 + -82742922 -1671853892 -218380796 -169018736</internalNodes> + <leafValues> + -3.1055119633674622e-01 2.3958165943622589e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 154 494120195 870143859 -1647046746 -36192340 + 2139319279 803383587 -332148852 -175859072</internalNodes> + <leafValues> + -2.8900969028472900e-01 2.5575104355812073e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 282 -811560420 827561735 -12914945 -80101505 + -1361111928 -1185616587 -1407676420 -16973965</internalNodes> + <leafValues> + -2.4221476912498474e-01 3.2213848829269409e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 28 -1807515438 -1923894982 -1525713421 -149168351 + 240714426 -1418935881 419048443 426584671</internalNodes> + <leafValues> + -3.4787160158157349e-01 2.1293328702449799e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 147 -67165120 453708801 -1303483468 895300097 + 1246742261 -70442158 -982536969 1920335427</internalNodes> + <leafValues> + -3.4275433421134949e-01 2.1697193384170532e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 84 1234906919 2103279727 -1846517728 -34739694 + 2067706763 1171293376 -104744214 -776609560</internalNodes> + <leafValues> + -3.1936296820640564e-01 2.2989478707313538e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 203 -148968905 1008481713 788164595 781877169 240125438 + -1348980802 1087673598 -1963666822</internalNodes> + <leafValues> + -2.6340115070343018e-01 2.8225165605545044e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 292 1837891345 -1365117008 931935797 254075285 + 1304075707 781882527 284906609 -1998401051</internalNodes> + <leafValues> + -3.0138608813285828e-01 2.5070127844810486e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 31 -2141011328 549559667 -576755279 -1989065165 + -1471060704 -1108160073 -825160949 47662087</internalNodes> + <leafValues> + -4.3011611700057983e-01 1.7580580711364746e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 244 -747375089 -747702293 -219779522 1890186598 + -335684346 1718484847 1643894306 -206110753</internalNodes> + <leafValues> + -2.3109740018844604e-01 3.2786485552787781e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 26 -1784488175 -36553927 -1031147589 -369559501 + 125374437 -1648822347 8863969 -1498451769</internalNodes> + <leafValues> + -2.3490820825099945e-01 3.1162357330322266e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 259 -1071476830 -1095595629 -1920078129 -1444498449 + 810365109 -21630982 -290653569 -392299569</internalNodes> + <leafValues> + -2.5611433386802673e-01 2.9074308276176453e-01</leafValues></_></weakClassifiers></_> + <!-- stage 10 --> + <_> + <maxWeakCount>30</maxWeakCount> + <stageThreshold>-1.3058989048004150e+00</stageThreshold> + <weakClassifiers> + <_> + <internalNodes> + 0 -1 262 -270610434 -134359075 -811399758 -681603633 + -286489698 -45513244 -1344862513 227476639</internalNodes> + <leafValues> + -1.0025274008512497e-01 5.5719470977783203e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 104 -2097187 353744664 -1074283233 962461525 -17042946 + 2132074456 -185611768 173014862</internalNodes> + <leafValues> + -2.9774430394172668e-01 3.4969726204872131e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 192 -201591309 322053939 -66051 -1150042305 -1967374172 + 720309079 -1929450275 -9437249</internalNodes> + <leafValues> + -2.2600507736206055e-01 3.8218078017234802e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 290 -1690568520 1023449340 21823732 -1646306859 + -661660161 529059241 -845261571 -536871425</internalNodes> + <leafValues> + -2.8739321231842041e-01 2.8658962249755859e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 253 -894497022 101363498 -1468636304 174491490 + 1306476909 100485583 -17240860 -50397265</internalNodes> + <leafValues> + -4.0346711874008179e-01 1.7318421602249146e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 83 -318927617 2139044223 -1908122241 779509119 + -1159705360 -13892400 239132095 -1357193897</internalNodes> + <leafValues> + -2.4370998144149780e-01 3.1787887215614319e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 240 -1372487695 -1464797736 -608961161 -539484303 + -1347732485 -1107560232 -1770177028 -1397928683</internalNodes> + <leafValues> + -2.9030051827430725e-01 2.7010890841484070e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 162 -77617562 1723582851 -388105926 10740970 2075870464 + 2001443802 -264144166 1912602619</internalNodes> + <leafValues> + -3.6183983087539673e-01 2.2234350442886353e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 209 1737993987 575323583 570599958 -1593901402 + -537396969 800103342 -218367233 -210440396</internalNodes> + <leafValues> + -3.0187138915061951e-01 2.5218242406845093e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 115 -3692022 -33948459 -1481085416 -169476374 + -243802706 1464294676 -138677344 -135135404</internalNodes> + <leafValues> + -2.6393750309944153e-01 2.8237256407737732e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 38 652099711 -1564843973 104523854 704662580 2126923816 + -548581264 -351211922 -63803885</internalNodes> + <leafValues> + -3.4199714660644531e-01 2.2430206835269928e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 42 -1969279581 -1481414863 -1376914049 -1163909120 + 207957301 -1456248013 -347640417 -358126493</internalNodes> + <leafValues> + -3.2226172089576721e-01 2.3534862697124481e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 97 -721363152 486579872 1882454289 1434257045 + 1611566751 1289487287 996045691 1005540351</internalNodes> + <leafValues> + -3.4691241383552551e-01 2.1465319395065308e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 146 -613203450 1931612930 -221388928 2357067 -95798262 + -1633004965 -1206355046 -1305281858</internalNodes> + <leafValues> + -3.5142555832862854e-01 2.2679552435874939e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 53 726466699 -783429313 -1320406822 -3624870 1498638047 + 293610405 -545215571 -171145013</internalNodes> + <leafValues> + -2.8053104877471924e-01 2.5749957561492920e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 258 -96033630 1940556686 -497037664 -252647058 + 703265613 1079474790 -142103350 1089273046</internalNodes> + <leafValues> + -3.5805869102478027e-01 1.9770637154579163e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 29 801058311 -1581801242 219502516 -808469412 264711475 + -2031926289 805127380 -840973936</internalNodes> + <leafValues> + -3.1421071290969849e-01 2.3023897409439087e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 172 -431184122 -2146638145 -899408814 -320144615 + -413827291 -936008940 -1376277757 -856306909</internalNodes> + <leafValues> + -3.3034646511077881e-01 2.2980070114135742e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 296 -1934886608 -1103642657 -1616127841 2141970916 + 12401112 -606080117 -28750412 -975218195</internalNodes> + <leafValues> + -3.0468633770942688e-01 2.4846823513507843e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 137 1828669055 -890173322 1056472031 1383623540 + -356497848 -275776488 -319784148 -45023438</internalNodes> + <leafValues> + -2.7311590313911438e-01 2.6826277375221252e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 9 1255206898 -1667881507 -10989766 54528930 1868912080 + 1497103356 -1096470729 195839511</internalNodes> + <leafValues> + -3.6629125475883484e-01 2.0582148432731628e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 221 -374953039 110804926 -627248196 1059075576 + -271092824 -1449292838 -306539616 -1950680360</internalNodes> + <leafValues> + -2.7446079254150391e-01 2.6614543795585632e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 77 1079656456 1450316376 -44560086 -86675865 821811859 + -203750610 1644870683 862142514</internalNodes> + <leafValues> + -4.1938367486000061e-01 1.8087086081504822e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 0 -290783281 -303164749 -1470447878 -1487432078 + 916915428 -88877852 1774133328 -992512778</internalNodes> + <leafValues> + -2.6362594962120056e-01 2.8109744191169739e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 234 -756036050 839304974 -287378334 -278205702 + 1541888379 252445922 1089229910 1534066638</internalNodes> + <leafValues> + -3.4634828567504883e-01 2.1581955254077911e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 276 855880616 863230344 1428139679 995299387 + -1190999286 -2031656420 -1089224709 393285777</internalNodes> + <leafValues> + -3.6159241199493408e-01 2.0310276746749878e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 311 -1512047005 -894771811 -1683823949 -598542090 + 1995129382 -1899059137 1498537061 -838863787</internalNodes> + <leafValues> + -2.8326958417892456e-01 2.6241222023963928e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 315 -1885077515 -1147345968 765009497 190991824 + -1880224543 -1444116296 170661041 -1880099595</internalNodes> + <leafValues> + -2.7807191014289856e-01 2.6050725579261780e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 135 -678450904 253998378 1239066322 -181932174 + 1262701314 1057893524 -413531308 -638649353</internalNodes> + <leafValues> + -3.3198201656341553e-01 2.2709015011787415e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 34 -581179905 -1848462196 1945061311 -5371768 + -323621137 -1263209313 1589634526 -1983639545</internalNodes> + <leafValues> + -2.1172577142715454e-01 3.3727011084556580e-01</leafValues></_></weakClassifiers></_> + <!-- stage 11 --> + <_> + <maxWeakCount>28</maxWeakCount> + <stageThreshold>-1.5291974544525146e+00</stageThreshold> + <weakClassifiers> + <_> + <internalNodes> + 0 -1 118 1460139419 453084545 822327865 822292283 352929169 + 417272919 135004047 924844031</internalNodes> + <leafValues> + -8.1135094165802002e-02 5.6568491458892822e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 80 -715128994 1934810962 -672407682 -201597282 + -35692578 809635668 -268435489 2147483551</internalNodes> + <leafValues> + -3.0913391709327698e-01 3.3743736147880554e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 247 1122941951 1555757531 -694977567 -540019318 + 137302463 470541003 1136607934 1306525407</internalNodes> + <leafValues> + -3.1006491184234619e-01 3.1824222207069397e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 260 -720124792 -536901237 1570357663 -1650861895 + -552270619 -68301133 1302024703 79667599</internalNodes> + <leafValues> + -3.3839857578277588e-01 2.5523734092712402e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 17 -8193062 -6097966 -1548746898 -379066394 143198346 + -645407521 12583050 -472923985</internalNodes> + <leafValues> + -2.4971115589141846e-01 3.2822373509407043e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 237 -825768185 724487963 -390073602 -330379374 + 1107247415 83879931 -993921692 -452988933</internalNodes> + <leafValues> + -3.0709245800971985e-01 2.5717419385910034e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 58 -824653104 303085300 -1502438950 676395390 + 1255626832 486565974 -815202689 -79167553</internalNodes> + <leafValues> + -3.6309060454368591e-01 2.1628698706626892e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 67 1465723394 105884335 67529631 1199566589 -1622165645 + 1811892772 1549268950 1652352758</internalNodes> + <leafValues> + -4.2343840003013611e-01 1.6860494017601013e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 185 148088327 717657079 -1158484046 -2098532510 + 1870465024 1417516115 -834205 -218760204</internalNodes> + <leafValues> + -3.6911985278129578e-01 2.0653814077377319e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 300 -461242368 1477725653 -1215242820 -90145126 + 1009057732 -704735 -1141055496 -1162708353</internalNodes> + <leafValues> + -3.3464974164962769e-01 2.3336097598075867e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 293 -1423237359 -1789315824 -574109707 -1107438219 + -1363603597 -872691976 -1657075536 -1933638287</internalNodes> + <leafValues> + -3.1273457407951355e-01 2.2250117361545563e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 194 -144708057 1699343849 -1838157252 -177211402 + -159396385 625472608 -135793676 -521076854</internalNodes> + <leafValues> + -2.3151670396327972e-01 3.2311838865280151e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 168 -1931608011 913325143 -171254849 266627963 + -2035524252 -69309961 -81258276 -98535933</internalNodes> + <leafValues> + -2.9178491234779358e-01 2.4896363914012909e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 91 1087671428 82841500 -823391220 -1623396996 + -102804027 1314177621 -1141613796 -1074263479</internalNodes> + <leafValues> + -3.8546600937843323e-01 1.7542295157909393e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 159 -933240178 -1878596882 1715175556 -1194366298 + 411894039 268454833 -470873276 719547915</internalNodes> + <leafValues> + -3.2228979468345642e-01 2.1430800855159760e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 220 -939528673 -1404420134 -369182724 1002367802 + 15711733 -1410089155 1960793528 -2001995142</internalNodes> + <leafValues> + -2.3454265296459198e-01 3.0028054118156433e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 113 536155908 470828324 -1369532674 -1364256937 + 965751798 -35376543 78916068 750088039</internalNodes> + <leafValues> + -4.0617641806602478e-01 1.7761451005935669e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 35 -2071666137 -5603018 -1985288769 -1434200 1335921899 + -1427144213 -1685518963 -1157538765</internalNodes> + <leafValues> + -2.4697369337081909e-01 2.9498186707496643e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 229 -288895194 586300718 -1428684760 -123278042 + -121959099 946274053 -1125681616 1348531058</internalNodes> + <leafValues> + -4.4196075201034546e-01 1.6939437389373779e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 21 -1431908781 2021291639 -1130434561 980837616 + 575372375 -335353565 -1127443082 -8913481</internalNodes> + <leafValues> + -2.6150795817375183e-01 2.7675113081932068e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 68 -1163937995 319737271 1308060341 -1120611591 + 215961812 -849912353 -825672464 -121636649</internalNodes> + <leafValues> + -2.0028464496135712e-01 3.5346552729606628e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 112 -1550874873 68414247 420172119 -1144260794 + -508579405 -134226281 -772565676 -204480722</internalNodes> + <leafValues> + -2.3316873610019684e-01 3.1222426891326904e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 218 -541341454 531709016 -1735251873 1326185481 + -1933801064 801776562 1154066423 1474264822</internalNodes> + <leafValues> + -2.6953682303428650e-01 2.8109839558601379e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 181 1280835701 -19635873 -1961789445 -282569314 + 2036092788 -1093054601 -151512323 -1347805666</internalNodes> + <leafValues> + -2.9192847013473511e-01 2.4838224053382874e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 95 -311496552 764872328 -498934241 504728371 + -1669213988 -1084285404 -22094113 977657810</internalNodes> + <leafValues> + -3.2798394560813904e-01 2.1226845681667328e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 309 -830243037 -455709770 -1109557600 -394303915 + 1283690960 229296318 285572161 -1936864027</internalNodes> + <leafValues> + -2.9578369855880737e-01 2.4679656326770782e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 202 -353374409 -1910657629 -153750156 -291312600 + -824232155 -1323582188 -1830135484 -1029119357</internalNodes> + <leafValues> + -3.0753603577613831e-01 2.2499974071979523e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 150 1861152279 336385887 -1598237261 1532541780 + 717032533 845016530 -1129812228 -567126192</internalNodes> + <leafValues> + -2.7896767854690552e-01 2.5912684202194214e-01</leafValues></_></weakClassifiers></_> + <!-- stage 12 --> + <_> + <maxWeakCount>32</maxWeakCount> + <stageThreshold>-1.3275781869888306e+00</stageThreshold> + <weakClassifiers> + <_> + <internalNodes> + 0 -1 235 -9764865 -604078086 -173773889 -2987398 1279545343 + -557388648 1149200335 1195863143</internalNodes> + <leafValues> + -2.8027681633830070e-02 5.9074550867080688e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 55 -715992866 -553046580 -170277985 -572962233 + -202911505 -562080269 -271361 2136426523</internalNodes> + <leafValues> + -2.9094350337982178e-01 3.2474684715270996e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 2 -285213773 -97321424 -537973381 -1342251019 249102355 + 186786323 -1879110913 -269489669</internalNodes> + <leafValues> + -2.4952219426631927e-01 3.3645749092102051e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 274 -5251811 -46589657 -577170147 -1649982203 -147523 + -67650657 -307360312 -1945170545</internalNodes> + <leafValues> + -2.2803187370300293e-01 3.6403229832649231e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 132 1587350780 -6418628 -1073864867 -604037841 + -21053701 -1195406672 784076557 -1342501334</internalNodes> + <leafValues> + -2.9213908314704895e-01 2.7659067511558533e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 176 -826544217 -1431112397 -488065488 540212786 + -722778748 1157429871 -270294508 -520884270</internalNodes> + <leafValues> + -3.5402128100395203e-01 2.1517892181873322e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 87 712975879 79833691 -1361412946 686460060 -691335585 + -125978095 -285512546 -1430258133</internalNodes> + <leafValues> + -3.1391125917434692e-01 2.2734560072422028e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 200 -976172866 286760680 6735455 198928110 1074026973 + 990905122 10809743 255401983</internalNodes> + <leafValues> + -3.5261881351470947e-01 2.0989060401916504e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 224 774125429 -1154154632 -439675595 -1111523364 + 1012643517 -1454645 798207228 -1128575640</internalNodes> + <leafValues> + -2.6199585199356079e-01 2.7558091282844543e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 251 -487657822 48661756 -659241758 -135899942 + 1153834238 10993310 1170301815 1424455655</internalNodes> + <leafValues> + -4.2886912822723389e-01 1.5836857259273529e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 61 721311231 -1653716997 -1990311447 531501487 + -1965404323 -67478251 171464059 184576600</internalNodes> + <leafValues> + -3.8873490691184998e-01 1.7470200359821320e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 57 327418278 390873324 -1341550100 -1277569843 + 890828715 1352696428 762946218 871965384</internalNodes> + <leafValues> + -4.1405329108238220e-01 1.7622730135917664e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 313 1356549140 -1631210850 -1129431843 -1204505275 + -93539215 -1074227267 -386080853 -84902083</internalNodes> + <leafValues> + -2.7681437134742737e-01 2.4316167831420898e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 157 -488643058 180875078 -329274718 -236520514 + -1068514035 1145270019 1940253924 1625680036</internalNodes> + <leafValues> + -3.7754905223846436e-01 1.7737720906734467e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 318 1568653099 -1486684233 -270730127 -320215680 + -1480112719 -1448552587 444420880 -859837569</internalNodes> + <leafValues> + -2.4140998721122742e-01 2.7932175993919373e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 189 -17905025 1950769147 -402672769 2131932059 + 251095909 -1377736845 -268624424 1935896624</internalNodes> + <leafValues> + -2.4894605576992035e-01 2.6565098762512207e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 56 -229980658 1379962893 -378530289 -1286386082 + 541532620 -136587009 1094085135 657713951</internalNodes> + <leafValues> + -2.9520252346992493e-01 2.2449728846549988e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 271 -383212128 -6160449 -283244184 -1365334076 + 762609077 1768025015 -304497187 -1344499269</internalNodes> + <leafValues> + -2.8447866439819336e-01 2.4520753324031830e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 212 -823677149 33812366 63268002 -1196492664 759909893 + 73716507 -306223978 -151718043</internalNodes> + <leafValues> + -3.2939437031745911e-01 2.0377136766910553e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 312 -1374019853 713820810 -669067463 -2043769806 + -847767619 -1963069954 1575449037 -450464140</internalNodes> + <leafValues> + -3.0366918444633484e-01 2.2024095058441162e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 114 -470316922 717621094 -923928028 -172110089 + -175398463 2009855399 -194461440 -243275802</internalNodes> + <leafValues> + -2.6190644502639771e-01 2.6647549867630005e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 140 1951769299 1434447053 -1336849665 -721425681 + -78677193 -2902011 2045245095 -24300714</internalNodes> + <leafValues> + -2.2465880215167999e-01 3.1657159328460693e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 267 -861210968 688568240 -1004941864 -35890713 + -662335344 8503434 1128813796 1162868189</internalNodes> + <leafValues> + -4.0748140215873718e-01 1.7012281715869904e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 8 1909956778 -958730002 73759065 117326543 -170462666 + -1638796928 -144999309 1180628626</internalNodes> + <leafValues> + -3.5050147771835327e-01 1.8371912837028503e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 156 -1007695481 -1609761269 -218186094 -1297941718 + 1501545350 1348355905 -785383567 -535441850</internalNodes> + <leafValues> + -2.8428241610527039e-01 2.3429898917675018e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 277 -1088815371 1060734832 -4223497 1069000375 + -1138325508 -23580996 -1751106129 -1447097814</internalNodes> + <leafValues> + -2.0420564711093903e-01 3.2137596607208252e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 10 1861672606 -668397946 1208024407 1141008467 + 793079834 440603748 -705639574 -48042237</internalNodes> + <leafValues> + -2.7496239542961121e-01 2.4548499286174774e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 283 -992769979 767790999 -48337487 2140880143 215229748 + 418870965 -320966916 1188940671</internalNodes> + <leafValues> + -2.7034837007522583e-01 2.4777430295944214e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 254 -1532809423 -1493812843 -1351098395 -172312296 + -515160205 -1498547284 268291320 -1426238432</internalNodes> + <leafValues> + -2.8834709525108337e-01 2.2686660289764404e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 298 -3221657 908227842 -305164460 942829106 -357061387 + -1565722789 -9463718 -360710886</internalNodes> + <leafValues> + -2.2364138066768646e-01 3.1509658694267273e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 93 -735960950 -170126554 1909113855 -23709394 + -1542321238 -737632549 -361625941 867181313</internalNodes> + <leafValues> + -3.6416807770729065e-01 1.8943277001380920e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 297 -1427114189 -1498618581 -224896028 -1392720912 + -1096677881 -972078302 -136092556 -1000345005</internalNodes> + <leafValues> + -3.1630918383598328e-01 2.1302369236946106e-01</leafValues></_></weakClassifiers></_> + <!-- stage 13 --> + <_> + <maxWeakCount>36</maxWeakCount> + <stageThreshold>-1.3241410255432129e+00</stageThreshold> + <weakClassifiers> + <_> + <internalNodes> + 0 -1 264 -1886982258 -671648799 -570864297 -606221228 + -1899852152 -3292975 236208719 1600077909</internalNodes> + <leafValues> + -7.9810082912445068e-02 5.2391374111175537e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 226 -85990657 570624531 -67109121 992976191 -386251554 + -1965159927 -83923475 -1048581</internalNodes> + <leafValues> + -1.8287384510040283e-01 4.4005623459815979e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 278 -37758029 -1146357328 -206212751 -572669447 + -3147333 -1147409733 -67110471 -1714697285</internalNodes> + <leafValues> + -2.0388080179691315e-01 4.2895537614822388e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 145 -183247173 1023475216 1027163441 1527839249 + 1017224859 1738217629 1060840379 1065315199</internalNodes> + <leafValues> + -3.3740916848182678e-01 2.1529236435890198e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 103 481318143 -134278060 -1749526563 -1078064995 + -1372435270 -1162474278 -358945793 -1431173974</internalNodes> + <leafValues> + -2.8765675425529480e-01 2.5110965967178345e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 268 -805831759 -302189077 -591401807 -305135631 + 144606625 -1936723969 -992408563 -855769927</internalNodes> + <leafValues> + -2.2018201649188995e-01 3.1721305847167969e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 20 -534756686 1380106992 -2462279 -33757488 28591869 + -456875269 -1610624779 -1051657</internalNodes> + <leafValues> + -2.6303508877754211e-01 2.5412750244140625e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 175 1342137858 36560521 -895533800 683603905 1263879488 + 1146407324 -1450126 -537788681</internalNodes> + <leafValues> + -3.6383235454559326e-01 1.9057570397853851e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 66 1340125183 -134742145 -692979981 -1214109056 + 1289101223 -4839633 -21061633 -369409502</internalNodes> + <leafValues> + -2.0452670753002167e-01 3.3919993042945862e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 269 193720319 100840569 -303833102 763156184 978053077 + 531299573 -839056136 -1929622308</internalNodes> + <leafValues> + -2.7057421207427979e-01 2.3806059360504150e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 73 1437966890 -129122561 1958795834 1556596447 + 1598142386 1074702522 -272384141 1205144459</internalNodes> + <leafValues> + -3.8338547945022583e-01 1.6122914850711823e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 171 1115680527 115249619 895027446 -92284851 1599078213 + 1735769988 -537445018 1689910980</internalNodes> + <leafValues> + -3.6433032155036926e-01 1.7980493605136871e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 144 1900011604 2069139451 1149891166 -1061030506 + 1967646922 -937402003 -1292100642 838974267</internalNodes> + <leafValues> + -3.2297107577323914e-01 1.9990013539791107e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 86 1216302162 1437731410 -1922152432 -586510344 + -937599750 1065827099 -621007040 -86024589</internalNodes> + <leafValues> + -2.9406809806823730e-01 2.1769714355468750e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 288 34597026 -688274366 1187213567 -671108889 + -1508901182 -1308638469 -559091031 247438496</internalNodes> + <leafValues> + -3.2822206616401672e-01 1.8776336312294006e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 207 103046517 133606652 1733190463 -1346628911 + -1148684048 -22091807 901613945 -1460139888</internalNodes> + <leafValues> + -3.3780130743980408e-01 1.9073766469955444e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 111 -746085629 -676994367 1095924655 -1122388257 + -212397823 -202506293 -205057719 -208672818</internalNodes> + <leafValues> + -1.8210665881633759e-01 3.4192490577697754e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 74 -425549783 -371579759 -351371857 -575060349 + -1214609473 -88295489 419684031 -1624088569</internalNodes> + <leafValues> + -2.2647747397422791e-01 2.7878209948539734e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 208 -894491986 -219288836 1075036258 -1764754717 + -573582558 -1807733298 1383817634 -196806592</internalNodes> + <leafValues> + -2.9377350211143494e-01 2.1309736371040344e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 201 -2106874097 -931096794 1687396770 -828052004 + 2015982962 -126682795 -294764796 -323961156</internalNodes> + <leafValues> + -3.2405611872673035e-01 1.9604462385177612e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 222 -2042653379 1051606960 500611353 1069291288 + -1453967943 -1919900749 -547038872 -1414655812</internalNodes> + <leafValues> + -2.6115354895591736e-01 2.3542492091655731e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 314 -588537600 -844176924 1464154596 -637687780 + 487338116 68063409 -1978163723 -840968737</internalNodes> + <leafValues> + -2.7114504575729370e-01 2.3290830850601196e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 317 120290097 -1288032431 1651243885 -76200631 + -2068365766 -1346655489 246316733 -1108265899</internalNodes> + <leafValues> + -3.0526432394981384e-01 2.0446150004863739e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 16 -822121865 -768454985 -352395443 -367512593 + -1091080379 -202815919 -279024202 -28119438</internalNodes> + <leafValues> + -2.4680423736572266e-01 2.5303974747657776e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 39 -624497410 423430210 168884479 707847256 -901496725 + 2133000198 -1135412149 1936933458</internalNodes> + <leafValues> + -3.1653416156768799e-01 2.0023956894874573e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 242 1650942474 1345050478 886355088 1168828219 + 1112207884 1231876612 1550108076 1951460330</internalNodes> + <leafValues> + -3.2898175716400146e-01 1.8093948066234589e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 124 369300756 891056133 -1916674564 -1374783349 + 996154237 -1145226207 -5607004 744775764</internalNodes> + <leafValues> + -3.2225444912910461e-01 1.9573701918125153e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 211 205577554 1071184148 -1263948549 1998946051 + 1260935961 -304903199 -606843718 1677159567</internalNodes> + <leafValues> + -3.0931469798088074e-01 2.0338173210620880e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 143 -591418226 177508317 -1342069760 -4867939 + 1927953629 272683209 -1222155904 1886512754</internalNodes> + <leafValues> + -3.4405741095542908e-01 1.8495233356952667e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 265 -21049686 -28131359 1073478397 -34340880 + -2075882246 -1093124168 -64807690 -1243108611</internalNodes> + <leafValues> + -2.2883313894271851e-01 2.9109308123588562e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 307 781346419 639805680 -546181353 -1174989674 + -1555551183 -893031525 -2006836811 -1392561707</internalNodes> + <leafValues> + -3.0891278386116028e-01 2.1537151932716370e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 134 -26563529 576995058 1442217478 -1427177744 + -68769859 -1241776649 -424779152 -285805586</internalNodes> + <leafValues> + -2.3855516314506531e-01 2.6137462258338928e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 178 -491860352 1783772447 -1761018371 989361018 + 2037451840 -988741302 2128769468 1865931536</internalNodes> + <leafValues> + -3.3605426549911499e-01 1.9781108200550079e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 59 -1359078146 -21349537 1081098827 794979703 147593312 + -343136234 308281456 -1121813221</internalNodes> + <leafValues> + -3.4179690480232239e-01 1.8693566322326660e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 131 -467418588 607934740 -510185553 -839002645 + 2000942967 1793376114 710533396 209909344</internalNodes> + <leafValues> + -3.9927759766578674e-01 1.5319514274597168e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 6 -251463749 -1087866735 954640701 -1345720208 + 257393053 -1721918337 71237772 -522239782</internalNodes> + <leafValues> + -2.0329028367996216e-01 3.1257370114326477e-01</leafValues></_></weakClassifiers></_> + <!-- stage 14 --> + <_> + <maxWeakCount>36</maxWeakCount> + <stageThreshold>-1.3515049219131470e+00</stageThreshold> + <weakClassifiers> + <_> + <internalNodes> + 0 -1 121 -583157832 -41345124 -1667477754 -47472728 + -1631649861 -192782657 -1106464817 1060048799</internalNodes> + <leafValues> + -6.6292375326156616e-02 5.3096652030944824e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 250 -961874181 -589629441 -202714187 -33687726 + 246352127 -1125919869 1090470655 1441756927</internalNodes> + <leafValues> + -2.9468271136283875e-01 2.9257065057754517e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 284 -219161456 1561959797 -1157910723 -644253203 + -37769852 -1175163461 -358812998 -1146105857</internalNodes> + <leafValues> + -2.2191908955574036e-01 3.5355353355407715e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 133 -1395773932 -19337 -872592385 -1082417169 + -268501000 -203988001 -1360523750 -1430265925</internalNodes> + <leafValues> + -2.6642906665802002e-01 2.7330449223518372e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 89 937245647 533051903 22876957 837784063 -113164289 + 1593942211 1207413693 1942338443</internalNodes> + <leafValues> + -3.1208184361457825e-01 2.1690289676189423e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 245 -135270614 1911454511 -694266878 1894711038 + 1380414469 1078062543 -200939772 1616172018</internalNodes> + <leafValues> + -3.7340530753135681e-01 2.0424529910087585e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 79 -673199910 2132653981 -1105191310 -1622098985 + -279161640 1874615084 2066898922 -82575381</internalNodes> + <leafValues> + -2.7581161260604858e-01 2.3679631948471069e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 108 -88609193 -764250339 -21964208 1785903000 + -705804525 -281542881 -17301633 -29425689</internalNodes> + <leafValues> + -2.1142585575580597e-01 3.1125506758689880e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 287 2117156824 2142892795 -1715733421 -1118208017 + 1981329130 -1132205343 -531030662 218086646</internalNodes> + <leafValues> + -3.1311789155006409e-01 2.2028388082981110e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 217 657173799 1208409516 -304231172 -319884740 + 829234695 822389124 -1816709292 -725510</internalNodes> + <leafValues> + -3.4273931384086609e-01 1.9222305715084076e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 270 -102301694 -135807070 -1880672401 -253298777 + -354657254 -100668629 2011055773 -155453461</internalNodes> + <leafValues> + -2.4226696789264679e-01 2.7088710665702820e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 191 215240020 -1148310119 -707214049 -1112003969 + 1860719544 -6638593 -4317491 -1414921888</internalNodes> + <leafValues> + -3.1184914708137512e-01 2.1366736292839050e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 18 -873476105 -30229647 -168296643 -1682912767 + 161284371 -1972125919 -12305 -268965957</internalNodes> + <leafValues> + -2.1284404397010803e-01 3.0867013335227966e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 289 -449986302 74968807 2145360303 20911369 1088274818 + 1642837031 -1026366551 53429507</internalNodes> + <leafValues> + -3.2960832118988037e-01 1.9843052327632904e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 187 -294473945 1289282231 -69736418 -366809570 + 585961254 -894112782 -352349116 -857431116</internalNodes> + <leafValues> + -3.4131202101707458e-01 1.7822383344173431e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 117 -260075316 810653599 -307212416 -1090154618 + 1085218542 2014454434 -889360754 840974879</internalNodes> + <leafValues> + -2.8617727756500244e-01 2.1801990270614624e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 32 683352887 144804986 883838777 -470815534 749351701 + -1376201987 -1007927147 -269498899</internalNodes> + <leafValues> + -2.7213591337203979e-01 2.3029308021068573e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 160 -221779237 2055449126 1356126973 1739434813 + -69756111 -78052576 -724205593 927834891</internalNodes> + <leafValues> + -2.1148133277893066e-01 2.9901567101478577e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 139 -327161337 -541288917 -1074706625 2069214763 + 1422886542 -570618263 1425911455 1965665867</internalNodes> + <leafValues> + -1.9790525734424591e-01 3.1719624996185303e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 12 -286652406 -1499051796 -2055991469 557910223 + 226286607 -307013288 537285247 1340571263</internalNodes> + <leafValues> + -3.5588899254798889e-01 1.8802331387996674e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 204 -440754172 -1969605175 -11230786 -1369112738 + -164163787 -439228643 -440438364 -855854880</internalNodes> + <leafValues> + -3.3756810426712036e-01 1.8606249988079071e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 54 -2103276886 1587028506 -1920974049 -382890163 + -50953603 -625169247 -75998374 847320848</internalNodes> + <leafValues> + -3.3854812383651733e-01 1.8501213192939758e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 149 -912264192 1437763456 -117442116 1477965101 + -109596288 -782313675 -520249859 -174065925</internalNodes> + <leafValues> + -2.3874689638614655e-01 2.6692461967468262e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 179 -74761 -1193108610 -1627717634 -1141359812 + -1350594860 -1212204947 -1900803076 -68466436</internalNodes> + <leafValues> + -1.8434369564056396e-01 3.4898519515991211e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 320 -806388957 -1603323478 -319904391 -1889144109 + -1427144299 -1074553867 -1577597820 -822218338</internalNodes> + <leafValues> + -1.9554206728935242e-01 3.1441712379455566e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 13 -495530405 -1200567737 -562818530 1426283955 + -646798267 -42047966 -283939201 -15210669</internalNodes> + <leafValues> + -2.0684894919395447e-01 3.0842429399490356e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 227 -204473205 -111677525 1659697788 -168822810 + 164468462 95878471 1418323918 -705698930</internalNodes> + <leafValues> + -1.5447042882442474e-01 4.0702703595161438e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 122 332207055 1368372207 1371537916 1541155303 + 1377022927 1195687084 -845629564 -191055413</internalNodes> + <leafValues> + -2.6908680796623230e-01 2.3349469900131226e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 183 2095555624 -1858958714 880848550 -1086881165 + 1278302507 2017585116 -2100032289 83174071</internalNodes> + <leafValues> + -3.3865603804588318e-01 1.8762224912643433e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 257 -1599264894 1347540775 548331985 566406079 + -490018643 2125284619 -542620802 -507647254</internalNodes> + <leafValues> + -2.4964332580566406e-01 2.5305619835853577e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 153 -928904029 -73474081 -431055591 -1365072480 + 1082731439 -1163105435 -401696790 -331068832</internalNodes> + <leafValues> + -2.4641251564025879e-01 2.5806349515914917e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 261 -1551324256 227211248 -139191220 -1912739520 + 1712117749 1864425601 1605297047 232734801</internalNodes> + <leafValues> + -3.6956688761711121e-01 1.7259617149829865e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 37 2062683840 -1171226870 -634334626 -42988729 + -177907627 1819501884 -1554858149 -81568901</internalNodes> + <leafValues> + -2.8280401229858398e-01 2.3210345208644867e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 101 -52896752 2073648803 -104703392 -1877326687 + 1482183868 486564792 -1181155586 -1078764545</internalNodes> + <leafValues> + -2.6182973384857178e-01 2.4921841919422150e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 119 931876834 -1689820513 -175925869 295803187 + 1288847768 -1394638455 -527053926 -1609099725</internalNodes> + <leafValues> + -2.8890526294708252e-01 2.2244787216186523e-01</leafValues></_> + <_> + <internalNodes> + 0 -1 23 674383927 640705886 -1980567045 775069703 941246324 + -1074502869 821615185 -1439559299</internalNodes> + <leafValues> + -3.7707689404487610e-01 1.7476500570774078e-01</leafValues></_></weakClassifiers></_></stages> + <features> + <_> + <rect> + 0 0 2 2</rect></_> + <_> + <rect> + 0 0 3 4</rect></_> + <_> + <rect> + 0 0 5 3</rect></_> + <_> + <rect> + 0 0 5 4</rect></_> + <_> + <rect> + 0 0 6 3</rect></_> + <_> + <rect> + 0 0 8 1</rect></_> + <_> + <rect> + 0 1 2 3</rect></_> + <_> + <rect> + 0 1 6 4</rect></_> + <_> + <rect> + 0 2 8 5</rect></_> + <_> + <rect> + 0 7 6 2</rect></_> + <_> + <rect> + 0 9 2 2</rect></_> + <_> + <rect> + 0 9 3 1</rect></_> + <_> + <rect> + 0 9 5 3</rect></_> + <_> + <rect> + 0 14 3 3</rect></_> + <_> + <rect> + 0 15 2 2</rect></_> + <_> + <rect> + 0 16 1 2</rect></_> + <_> + <rect> + 0 17 2 2</rect></_> + <_> + <rect> + 1 0 2 4</rect></_> + <_> + <rect> + 1 0 5 3</rect></_> + <_> + <rect> + 1 0 7 1</rect></_> + <_> + <rect> + 1 6 2 1</rect></_> + <_> + <rect> + 1 9 1 1</rect></_> + <_> + <rect> + 1 9 2 2</rect></_> + <_> + <rect> + 1 15 1 2</rect></_> + <_> + <rect> + 1 16 2 2</rect></_> + <_> + <rect> + 2 0 2 2</rect></_> + <_> + <rect> + 2 0 2 3</rect></_> + <_> + <rect> + 2 0 3 4</rect></_> + <_> + <rect> + 2 1 3 7</rect></_> + <_> + <rect> + 2 4 1 1</rect></_> + <_> + <rect> + 2 4 5 2</rect></_> + <_> + <rect> + 2 4 5 4</rect></_> + <_> + <rect> + 2 5 1 1</rect></_> + <_> + <rect> + 2 6 2 2</rect></_> + <_> + <rect> + 2 7 1 1</rect></_> + <_> + <rect> + 2 7 2 1</rect></_> + <_> + <rect> + 2 8 3 3</rect></_> + <_> + <rect> + 2 9 1 1</rect></_> + <_> + <rect> + 2 10 1 1</rect></_> + <_> + <rect> + 2 10 2 1</rect></_> + <_> + <rect> + 2 15 1 2</rect></_> + <_> + <rect> + 2 19 2 1</rect></_> + <_> + <rect> + 3 0 4 3</rect></_> + <_> + <rect> + 3 0 6 6</rect></_> + <_> + <rect> + 3 0 7 1</rect></_> + <_> + <rect> + 3 2 4 2</rect></_> + <_> + <rect> + 3 2 6 1</rect></_> + <_> + <rect> + 3 4 2 2</rect></_> + <_> + <rect> + 3 5 1 1</rect></_> + <_> + <rect> + 3 6 1 1</rect></_> + <_> + <rect> + 3 6 2 1</rect></_> + <_> + <rect> + 3 6 2 3</rect></_> + <_> + <rect> + 3 6 3 3</rect></_> + <_> + <rect> + 3 6 6 1</rect></_> + <_> + <rect> + 3 7 2 1</rect></_> + <_> + <rect> + 3 7 2 2</rect></_> + <_> + <rect> + 3 8 1 1</rect></_> + <_> + <rect> + 3 8 6 1</rect></_> + <_> + <rect> + 3 9 1 1</rect></_> + <_> + <rect> + 3 9 1 2</rect></_> + <_> + <rect> + 3 10 1 1</rect></_> + <_> + <rect> + 3 10 1 2</rect></_> + <_> + <rect> + 3 10 2 1</rect></_> + <_> + <rect> + 3 11 1 1</rect></_> + <_> + <rect> + 3 11 1 3</rect></_> + <_> + <rect> + 3 13 3 3</rect></_> + <_> + <rect> + 4 0 4 2</rect></_> + <_> + <rect> + 4 0 6 7</rect></_> + <_> + <rect> + 4 1 1 1</rect></_> + <_> + <rect> + 4 1 4 3</rect></_> + <_> + <rect> + 4 4 2 1</rect></_> + <_> + <rect> + 4 6 1 1</rect></_> + <_> + <rect> + 4 6 5 1</rect></_> + <_> + <rect> + 4 6 5 4</rect></_> + <_> + <rect> + 4 7 1 1</rect></_> + <_> + <rect> + 4 7 2 2</rect></_> + <_> + <rect> + 4 7 5 1</rect></_> + <_> + <rect> + 4 8 1 1</rect></_> + <_> + <rect> + 4 8 5 4</rect></_> + <_> + <rect> + 4 9 1 1</rect></_> + <_> + <rect> + 4 9 2 1</rect></_> + <_> + <rect> + 4 9 3 5</rect></_> + <_> + <rect> + 4 9 5 1</rect></_> + <_> + <rect> + 4 10 1 1</rect></_> + <_> + <rect> + 4 10 5 1</rect></_> + <_> + <rect> + 4 12 1 2</rect></_> + <_> + <rect> + 4 14 3 3</rect></_> + <_> + <rect> + 4 15 1 1</rect></_> + <_> + <rect> + 4 18 2 1</rect></_> + <_> + <rect> + 5 2 5 3</rect></_> + <_> + <rect> + 5 3 4 4</rect></_> + <_> + <rect> + 5 6 1 1</rect></_> + <_> + <rect> + 5 6 4 1</rect></_> + <_> + <rect> + 5 7 1 1</rect></_> + <_> + <rect> + 5 7 1 2</rect></_> + <_> + <rect> + 5 7 2 2</rect></_> + <_> + <rect> + 5 8 1 1</rect></_> + <_> + <rect> + 5 8 3 4</rect></_> + <_> + <rect> + 5 8 3 5</rect></_> + <_> + <rect> + 5 8 5 1</rect></_> + <_> + <rect> + 5 8 5 3</rect></_> + <_> + <rect> + 5 9 1 1</rect></_> + <_> + <rect> + 5 9 5 1</rect></_> + <_> + <rect> + 5 10 1 1</rect></_> + <_> + <rect> + 5 10 2 1</rect></_> + <_> + <rect> + 5 10 5 1</rect></_> + <_> + <rect> + 5 11 1 1</rect></_> + <_> + <rect> + 5 13 1 3</rect></_> + <_> + <rect> + 5 16 1 1</rect></_> + <_> + <rect> + 5 18 1 2</rect></_> + <_> + <rect> + 6 0 6 5</rect></_> + <_> + <rect> + 6 1 1 2</rect></_> + <_> + <rect> + 6 3 1 1</rect></_> + <_> + <rect> + 6 3 6 3</rect></_> + <_> + <rect> + 6 4 1 1</rect></_> + <_> + <rect> + 6 5 1 1</rect></_> + <_> + <rect> + 6 5 1 2</rect></_> + <_> + <rect> + 6 6 2 2</rect></_> + <_> + <rect> + 6 6 2 4</rect></_> + <_> + <rect> + 6 6 3 3</rect></_> + <_> + <rect> + 6 7 1 1</rect></_> + <_> + <rect> + 6 7 1 2</rect></_> + <_> + <rect> + 6 7 4 1</rect></_> + <_> + <rect> + 6 7 4 4</rect></_> + <_> + <rect> + 6 7 6 1</rect></_> + <_> + <rect> + 6 8 1 4</rect></_> + <_> + <rect> + 6 8 2 2</rect></_> + <_> + <rect> + 6 9 1 1</rect></_> + <_> + <rect> + 6 9 2 1</rect></_> + <_> + <rect> + 6 9 2 2</rect></_> + <_> + <rect> + 6 9 5 1</rect></_> + <_> + <rect> + 6 9 6 2</rect></_> + <_> + <rect> + 6 10 1 1</rect></_> + <_> + <rect> + 6 11 1 1</rect></_> + <_> + <rect> + 6 13 1 1</rect></_> + <_> + <rect> + 6 13 2 1</rect></_> + <_> + <rect> + 6 18 1 2</rect></_> + <_> + <rect> + 6 20 1 1</rect></_> + <_> + <rect> + 7 0 3 1</rect></_> + <_> + <rect> + 7 2 1 3</rect></_> + <_> + <rect> + 7 3 4 5</rect></_> + <_> + <rect> + 7 5 1 1</rect></_> + <_> + <rect> + 7 5 2 1</rect></_> + <_> + <rect> + 7 6 1 1</rect></_> + <_> + <rect> + 7 7 1 2</rect></_> + <_> + <rect> + 7 7 2 4</rect></_> + <_> + <rect> + 7 8 1 1</rect></_> + <_> + <rect> + 7 8 1 2</rect></_> + <_> + <rect> + 7 8 2 4</rect></_> + <_> + <rect> + 7 9 1 1</rect></_> + <_> + <rect> + 7 16 1 1</rect></_> + <_> + <rect> + 7 21 1 1</rect></_> + <_> + <rect> + 7 21 2 1</rect></_> + <_> + <rect> + 8 0 2 1</rect></_> + <_> + <rect> + 8 0 3 1</rect></_> + <_> + <rect> + 8 0 5 3</rect></_> + <_> + <rect> + 8 3 1 1</rect></_> + <_> + <rect> + 8 4 1 1</rect></_> + <_> + <rect> + 8 5 1 1</rect></_> + <_> + <rect> + 8 5 1 2</rect></_> + <_> + <rect> + 8 7 3 4</rect></_> + <_> + <rect> + 8 8 1 1</rect></_> + <_> + <rect> + 8 9 1 1</rect></_> + <_> + <rect> + 8 9 3 5</rect></_> + <_> + <rect> + 8 11 1 1</rect></_> + <_> + <rect> + 8 14 1 1</rect></_> + <_> + <rect> + 8 19 1 1</rect></_> + <_> + <rect> + 8 20 1 1</rect></_> + <_> + <rect> + 8 21 1 1</rect></_> + <_> + <rect> + 9 0 2 3</rect></_> + <_> + <rect> + 9 1 2 1</rect></_> + <_> + <rect> + 9 5 2 1</rect></_> + <_> + <rect> + 9 5 2 3</rect></_> + <_> + <rect> + 9 5 4 1</rect></_> + <_> + <rect> + 9 7 1 1</rect></_> + <_> + <rect> + 9 9 1 1</rect></_> + <_> + <rect> + 9 11 1 1</rect></_> + <_> + <rect> + 9 12 2 4</rect></_> + <_> + <rect> + 9 15 1 1</rect></_> + <_> + <rect> + 9 17 1 1</rect></_> + <_> + <rect> + 9 18 1 2</rect></_> + <_> + <rect> + 9 20 1 1</rect></_> + <_> + <rect> + 10 0 2 2</rect></_> + <_> + <rect> + 10 6 3 3</rect></_> + <_> + <rect> + 10 7 1 1</rect></_> + <_> + <rect> + 10 8 1 1</rect></_> + <_> + <rect> + 10 9 1 1</rect></_> + <_> + <rect> + 10 11 1 1</rect></_> + <_> + <rect> + 10 12 1 1</rect></_> + <_> + <rect> + 10 15 1 1</rect></_> + <_> + <rect> + 10 20 1 1</rect></_> + <_> + <rect> + 10 21 1 1</rect></_> + <_> + <rect> + 11 0 4 4</rect></_> + <_> + <rect> + 11 1 4 4</rect></_> + <_> + <rect> + 11 3 1 1</rect></_> + <_> + <rect> + 11 4 3 1</rect></_> + <_> + <rect> + 11 6 3 6</rect></_> + <_> + <rect> + 11 7 1 1</rect></_> + <_> + <rect> + 11 8 1 1</rect></_> + <_> + <rect> + 11 9 1 1</rect></_> + <_> + <rect> + 11 9 2 3</rect></_> + <_> + <rect> + 11 10 1 1</rect></_> + <_> + <rect> + 11 11 1 1</rect></_> + <_> + <rect> + 11 12 2 3</rect></_> + <_> + <rect> + 11 13 1 1</rect></_> + <_> + <rect> + 11 15 3 1</rect></_> + <_> + <rect> + 11 20 1 1</rect></_> + <_> + <rect> + 11 21 1 1</rect></_> + <_> + <rect> + 12 3 1 1</rect></_> + <_> + <rect> + 12 5 1 1</rect></_> + <_> + <rect> + 12 5 2 3</rect></_> + <_> + <rect> + 12 5 3 3</rect></_> + <_> + <rect> + 12 6 1 1</rect></_> + <_> + <rect> + 12 7 2 4</rect></_> + <_> + <rect> + 12 8 1 1</rect></_> + <_> + <rect> + 12 8 2 2</rect></_> + <_> + <rect> + 12 8 2 3</rect></_> + <_> + <rect> + 12 9 1 1</rect></_> + <_> + <rect> + 12 10 2 3</rect></_> + <_> + <rect> + 12 11 1 1</rect></_> + <_> + <rect> + 12 16 1 1</rect></_> + <_> + <rect> + 12 17 1 1</rect></_> + <_> + <rect> + 12 18 1 1</rect></_> + <_> + <rect> + 12 18 1 2</rect></_> + <_> + <rect> + 12 20 1 1</rect></_> + <_> + <rect> + 12 21 1 1</rect></_> + <_> + <rect> + 13 0 3 3</rect></_> + <_> + <rect> + 13 3 1 1</rect></_> + <_> + <rect> + 13 4 1 1</rect></_> + <_> + <rect> + 13 5 1 1</rect></_> + <_> + <rect> + 13 6 2 3</rect></_> + <_> + <rect> + 13 7 3 2</rect></_> + <_> + <rect> + 13 8 1 1</rect></_> + <_> + <rect> + 13 9 1 1</rect></_> + <_> + <rect> + 13 10 1 1</rect></_> + <_> + <rect> + 13 10 2 1</rect></_> + <_> + <rect> + 13 11 1 1</rect></_> + <_> + <rect> + 13 13 1 1</rect></_> + <_> + <rect> + 13 19 1 1</rect></_> + <_> + <rect> + 13 20 1 1</rect></_> + <_> + <rect> + 13 21 1 1</rect></_> + <_> + <rect> + 14 0 3 4</rect></_> + <_> + <rect> + 14 3 1 2</rect></_> + <_> + <rect> + 14 3 2 1</rect></_> + <_> + <rect> + 14 4 1 1</rect></_> + <_> + <rect> + 14 5 1 1</rect></_> + <_> + <rect> + 14 6 2 3</rect></_> + <_> + <rect> + 14 7 1 3</rect></_> + <_> + <rect> + 14 7 2 2</rect></_> + <_> + <rect> + 14 8 1 1</rect></_> + <_> + <rect> + 14 8 1 2</rect></_> + <_> + <rect> + 14 9 1 1</rect></_> + <_> + <rect> + 14 9 2 1</rect></_> + <_> + <rect> + 14 13 1 1</rect></_> + <_> + <rect> + 14 21 1 1</rect></_> + <_> + <rect> + 15 0 3 4</rect></_> + <_> + <rect> + 15 1 1 2</rect></_> + <_> + <rect> + 15 2 1 2</rect></_> + <_> + <rect> + 15 5 1 1</rect></_> + <_> + <rect> + 15 6 2 1</rect></_> + <_> + <rect> + 15 6 2 2</rect></_> + <_> + <rect> + 15 7 1 1</rect></_> + <_> + <rect> + 15 7 1 2</rect></_> + <_> + <rect> + 15 7 2 2</rect></_> + <_> + <rect> + 15 8 1 1</rect></_> + <_> + <rect> + 15 9 1 1</rect></_> + <_> + <rect> + 15 9 1 5</rect></_> + <_> + <rect> + 15 9 2 1</rect></_> + <_> + <rect> + 15 15 3 3</rect></_> + <_> + <rect> + 15 17 1 1</rect></_> + <_> + <rect> + 16 4 1 1</rect></_> + <_> + <rect> + 16 6 1 1</rect></_> + <_> + <rect> + 16 7 1 1</rect></_> + <_> + <rect> + 16 7 1 2</rect></_> + <_> + <rect> + 16 7 2 1</rect></_> + <_> + <rect> + 16 7 2 2</rect></_> + <_> + <rect> + 16 8 1 1</rect></_> + <_> + <rect> + 16 10 1 1</rect></_> + <_> + <rect> + 16 10 2 1</rect></_> + <_> + <rect> + 16 17 1 1</rect></_> + <_> + <rect> + 16 17 1 2</rect></_> + <_> + <rect> + 16 18 1 1</rect></_> + <_> + <rect> + 17 4 2 2</rect></_> + <_> + <rect> + 17 5 1 1</rect></_> + <_> + <rect> + 17 6 2 1</rect></_> + <_> + <rect> + 17 6 2 2</rect></_> + <_> + <rect> + 17 7 1 1</rect></_> + <_> + <rect> + 17 7 1 2</rect></_> + <_> + <rect> + 17 8 1 1</rect></_> + <_> + <rect> + 17 8 2 1</rect></_> + <_> + <rect> + 17 9 1 1</rect></_> + <_> + <rect> + 17 10 1 1</rect></_> + <_> + <rect> + 17 10 2 2</rect></_> + <_> + <rect> + 17 11 1 1</rect></_> + <_> + <rect> + 17 12 1 2</rect></_> + <_> + <rect> + 17 13 1 2</rect></_> + <_> + <rect> + 17 13 1 3</rect></_> + <_> + <rect> + 17 21 2 1</rect></_> + <_> + <rect> + 18 0 2 2</rect></_> + <_> + <rect> + 18 4 1 2</rect></_> + <_> + <rect> + 18 6 1 1</rect></_> + <_> + <rect> + 18 8 2 2</rect></_> + <_> + <rect> + 18 9 1 1</rect></_> + <_> + <rect> + 18 9 1 2</rect></_> + <_> + <rect> + 18 9 1 3</rect></_> + <_> + <rect> + 18 10 1 1</rect></_> + <_> + <rect> + 18 11 1 2</rect></_> + <_> + <rect> + 18 12 1 1</rect></_> + <_> + <rect> + 18 13 1 2</rect></_> + <_> + <rect> + 18 17 2 2</rect></_> + <_> + <rect> + 18 18 1 2</rect></_> + <_> + <rect> + 18 19 2 1</rect></_> + <_> + <rect> + 19 0 1 1</rect></_> + <_> + <rect> + 19 6 1 1</rect></_> + <_> + <rect> + 19 9 1 1</rect></_> + <_> + <rect> + 19 9 1 2</rect></_> + <_> + <rect> + 19 10 1 4</rect></_> + <_> + <rect> + 19 12 1 2</rect></_> + <_> + <rect> + 20 10 1 1</rect></_> + <_> + <rect> + 20 15 1 2</rect></_> + <_> + <rect> + 21 10 1 1</rect></_> + <_> + <rect> + 21 14 1 2</rect></_> + <_> + <rect> + 21 15 1 3</rect></_></features></cascade> +</opencv_storage> diff --git a/examples/objectdetector/CMakeLists.txt b/examples/objectdetector/CMakeLists.txt index f4d9897c..261d98cf 100644 --- a/examples/objectdetector/CMakeLists.txt +++ b/examples/objectdetector/CMakeLists.txt @@ -34,6 +34,20 @@ if(image_hog_files_example) LINK_WITH od_global_image_detector) endif() +option(face_detection_files_example "Build the face detection example" ON) +if(face_detection_files_example) + OD_ADD_EXAMPLE(od_face_detection_files + FILES od_face_detector_files.cpp + LINK_WITH od_global_image_detector) +endif() + +option(face_detection_cam_example "Build the face detection example" ON) +if(face_detection_cam_example) + OD_ADD_EXAMPLE(od_face_detection_cam + FILES od_face_detector_cam.cpp + LINK_WITH od_global_image_detector) +endif() + option(image_customhog_example "Build the custom hog example" ON) if(image_customhog_example) OD_ADD_EXAMPLE(od_image_customhog diff --git a/examples/objectdetector/od_face_detector_cam.cpp b/examples/objectdetector/od_face_detector_cam.cpp new file mode 100644 index 00000000..ade87870 --- /dev/null +++ b/examples/objectdetector/od_face_detector_cam.cpp @@ -0,0 +1,47 @@ +#include "od/detectors/global2D/detection/ODFaceDetector.h" +#include "od/common/utils/ODFrameGenerator.h" +#include "od/common/utils/ODShared_pointers.h" + +#include <iostream> +#include <string> + +int main(int argc, char * argv[]) +{ + + if(argc < 2){ + std::cout << "Use with parameters: detector_file_path gpu_usage (0 disable / 1 enable)" << std::endl; + return -1; + } + + unsigned int gpu = 0; + shared_ptr<od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_DEVICE> > frame_generator; + + if(argc > 2){ + gpu = atoi(argv[2]); + } + + frame_generator = make_shared<od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_DEVICE> >(0); + + +#ifdef WITH_GPU + od::g2d::ODFaceDetector2D detector(gpu ? od::g2d::ODFaceDetector2D::GPU : od::g2d::ODFaceDetector2D::CPU, argv[1]); +#else + od::g2d::ODFaceDetector2D detector(od::g2d::ODFaceDetector2D::CPU, argv[1]); +#endif + //GUI + cv::namedWindow("Faces"); + while(frame_generator->isValid() && cv::waitKey(10) != 27) + { + boost::shared_ptr<od::ODSceneImage> scene = frame_generator->getNextFrame(); + + //Detect + boost::shared_ptr<od::ODDetections2D> detections = detector.detectOmni(scene); + + if(detections->size() > 0) + cv::imshow("Faces", detections->renderMetainfo(scene).getCVImage()); + else + cv::imshow("Faces", scene->getCVImage()); + } + + return 0; +} \ No newline at end of file diff --git a/examples/objectdetector/od_face_detector_files.cpp b/examples/objectdetector/od_face_detector_files.cpp new file mode 100644 index 00000000..88e81c6f --- /dev/null +++ b/examples/objectdetector/od_face_detector_files.cpp @@ -0,0 +1,42 @@ +#include "od/detectors/global2D/detection/ODFaceDetector.h" +#include "od/common/utils/ODFrameGenerator.h" +#include <iostream> + +int main(int argc, char * argv[]) +{ + + if(argc < 3){ + std::cout << "Wrong number of parameters: image_dir_path detector_file gpu_usage (0 disable / 1 enable)" << std::endl; + return -1; + } + + std::string image_path(argv[1]); + od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_FILE_LIST> frame_generator(image_path); + + unsigned int gpu = 0; + if(argc > 2) + gpu = atoi(argv[3]); + +#ifdef WITH_GPU + od::g2d::ODFaceDetector2D detector(gpu ? od::g2d::ODFaceDetector2D::GPU : od::g2d::ODFaceDetector2D::CPU, argv[2]); +#else + od::g2d::ODFaceDetector2D detector(od::g2d::ODFaceDetector2D::CPU, argv[2]); +#endif + //GUI + cv::namedWindow("Faces", cv::WINDOW_NORMAL); + while(frame_generator.isValid() && cv::waitKey(10) != 27) + { + boost::shared_ptr<od::ODSceneImage> scene = frame_generator.getNextFrame(); + //Detect + boost::shared_ptr<od::ODDetections2D> detections = detector.detectOmni(scene); + + if(detections->size() > 0) + cv::imshow("Faces", detections->renderMetainfo(scene).getCVImage()); + else + cv::imshow("Faces", scene->getCVImage()); + + cv::waitKey(0); + } + + return 0; +} \ No newline at end of file From b7c3d11d854d1d177d6d224f2eedcdf61378a618 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Tue, 5 Jul 2016 12:09:51 +0200 Subject: [PATCH 39/79] adds data folder for cascade classification, fixes viewer for different image type and scene types and adds viewer in majority of examples. Also adds last documentation about gsoc --- common/impl/od/common/utils/ODViewer.hpp | 30 ++++- common/include/od/common/pipeline/ODScene.h | 2 +- common/include/od/common/utils/ODViewer.h | 42 +++++- common/src/pipeline/ODScene.cpp | 2 +- common/src/utils/ODViewer.cpp | 122 ++++++++++++++++-- .../haarcascade_frontalface_alt.xml | 0 .../haarcascade_frontalface_alt_gpu.xml | 0 .../lbpcascade_frontalcatface.xml | 0 .../gsoc2016_blog_giacomo.md | 14 +- examples/objectdetector/CMakeLists.txt | 1 + examples/objectdetector/od_cascade_cam.cpp | 14 +- examples/objectdetector/od_cascade_files.cpp | 16 ++- .../od_example_framegenerator.cpp | 14 +- .../objectdetector/od_face_detector_cam.cpp | 20 +-- .../objectdetector/od_face_detector_files.cpp | 26 ++-- examples/objectdetector/od_hog_train.cpp | 12 +- .../od_image_cadrecog_camera.cpp | 12 +- .../od_image_cadrecog_files.cpp | 11 +- .../objectdetector/od_image_customhog.cpp | 15 ++- .../objectdetector/od_image_facerecog.cpp | 11 +- .../objectdetector/od_image_hog_files.cpp | 15 ++- .../objectdetector/od_multialgo_files.cpp | 14 +- examples/objectdetector/od_multialgo_pc.cpp | 1 + examples/objectdetector/od_pc_global.cpp | 6 +- .../objectdetector/od_pc_global_files.cpp | 9 +- .../objectdetector/od_pc_global_real_time.cpp | 3 +- 26 files changed, 324 insertions(+), 88 deletions(-) rename {examples/data => data}/haarcascade_frontalface_alt.xml (100%) rename {examples/data => data}/haarcascade_frontalface_alt_gpu.xml (100%) rename {examples/data => data}/lbpcascade_frontalcatface.xml (100%) diff --git a/common/impl/od/common/utils/ODViewer.hpp b/common/impl/od/common/utils/ODViewer.hpp index 63d3e84f..6ffa4e59 100644 --- a/common/impl/od/common/utils/ODViewer.hpp +++ b/common/impl/od/common/utils/ODViewer.hpp @@ -4,7 +4,8 @@ namespace od { template<typename PointT> - void ODViewer::render(shared_ptr<pcl::PointCloud<PointT> > to_display, const std::string & cloud_name, bool colored){ + void ODViewer::render(shared_ptr<pcl::PointCloud<PointT> > to_display, const std::string & cloud_name, bool colored) + { if(status_ != POINTCLOUD){ std::cout << "Switching viewer to PointCloud mode" << std::endl; if(status_ == CVMAT){ @@ -31,7 +32,20 @@ namespace od { } template<typename PointT> - void ODViewer::update(shared_ptr<pcl::PointCloud<PointT> > to_display, const std::string & cloud_name, bool colored){ + void ODViewer::render(shared_ptr<ODScenePointCloud<PointT> > to_display, const std::string & cloud_name, bool colored) + { + render(to_display->getPointCloud(), cloud_name, colored); + } + + template<typename PointT> + void ODViewer::render(const ODScenePointCloud<PointT> & to_display, const std::string & cloud_name, bool colored) + { + render(to_display.getPointCloud(), cloud_name, colored); + } + + template<typename PointT> + void ODViewer::update(shared_ptr<pcl::PointCloud<PointT> > to_display, const std::string & cloud_name, bool colored) + { if(status_ != POINTCLOUD){ std::cout << "No PointCloud to render! use render(shared_ptr<pcl::PointCloud<PointT>>) first!" << std::endl; return; @@ -53,4 +67,16 @@ namespace od { } } + template<typename PointT> + void ODViewer::update(shared_ptr<ODScenePointCloud<PointT> > to_display, const std::string & cloud_name, bool colored) + { + update(to_display->getPointCloud(), cloud_name, colored); + } + + template<typename PointT> + void ODViewer::update(const ODScenePointCloud<PointT> & to_display, const std::string & cloud_name, bool colored) + { + update(to_display.getPointCloud(), cloud_name, colored); + } + } \ No newline at end of file diff --git a/common/include/od/common/pipeline/ODScene.h b/common/include/od/common/pipeline/ODScene.h index b7b5552a..93315de0 100644 --- a/common/include/od/common/pipeline/ODScene.h +++ b/common/include/od/common/pipeline/ODScene.h @@ -74,7 +74,7 @@ namespace od const cv::Mat & getDescriptors() const; void setDescriptors(const cv::Mat & descriptors_); - cv::Mat getCVImage(); + cv::Mat getCVImage() const; void * getData(); diff --git a/common/include/od/common/utils/ODViewer.h b/common/include/od/common/utils/ODViewer.h index 3a20617e..a65eea1e 100644 --- a/common/include/od/common/utils/ODViewer.h +++ b/common/include/od/common/utils/ODViewer.h @@ -9,10 +9,10 @@ #include <iostream> #include <string> - #include <vtkRenderWindow.h> #include "od/common/utils/ODShared_pointers.h" +#include "od/common/pipeline/ODScene.h" namespace od { @@ -39,12 +39,31 @@ namespace od { template<typename PointT> void render(shared_ptr<pcl::PointCloud<PointT> > to_display, const std::string & cloud_name, bool colored = true); + template<typename PointT> + void render(shared_ptr<ODScenePointCloud<PointT> > to_display, const std::string & cloud_name, bool colored = true); + + template<typename PointT> + void render(const ODScenePointCloud<PointT> & to_display, const std::string & cloud_name, bool colored = true); + void render(const cv::Mat & to_display, const std::string & window_name); + void render(shared_ptr<ODSceneImage> to_display, const std::string & window_name); + void render(const ODSceneImage & to_display, const std::string & window_name); + + void initCVWindow(const std::string & window_name); + void initPCLWindow(const std::string & window_name); template<typename PointT> void update(shared_ptr<pcl::PointCloud<PointT> > to_display, const std::string & cloud_name, bool colored = true); + template<typename PointT> + void update(shared_ptr<ODScenePointCloud<PointT> > to_display, const std::string & cloud_name, bool colored = true); + + template<typename PointT> + void update(const ODScenePointCloud<PointT> & to_display, const std::string & cloud_name, bool colored = true); + void update(const cv::Mat & to_display, const std::string & window_name); + void update(const ODSceneImage & to_display, const std::string & window_name); + void update(shared_ptr<ODSceneImage> to_display, const std::string & window_name); void setBackGround(const cv::Scalar & color); void setBackGround(unsigned int r, unsigned int g, unsigned int b); @@ -55,6 +74,10 @@ namespace od { void spin(); bool toStop(); + void remove(const std::string & name); + + unsigned int wait(unsigned int time) const; + private: odViewType status_; @@ -68,10 +91,27 @@ namespace od { extern template void ODViewer::render<pcl::PointXYZRGB>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> >, const std::string &, bool); extern template void ODViewer::render<pcl::PointXYZRGBA>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA> >, const std::string &, bool); + extern template void ODViewer::render<pcl::PointXYZ>(shared_ptr<ODScenePointCloud<pcl::PointXYZ> >, const std::string &, bool); + extern template void ODViewer::render<pcl::PointXYZRGB>(shared_ptr<ODScenePointCloud<pcl::PointXYZRGB> >, const std::string &, bool); + extern template void ODViewer::render<pcl::PointXYZRGBA>(shared_ptr<ODScenePointCloud<pcl::PointXYZRGBA> >, const std::string &, bool); + + extern template void ODViewer::render<pcl::PointXYZ>(const ODScenePointCloud<pcl::PointXYZ> &, const std::string &, bool); + extern template void ODViewer::render<pcl::PointXYZRGB>(const ODScenePointCloud<pcl::PointXYZRGB> &, const std::string &, bool); + extern template void ODViewer::render<pcl::PointXYZRGBA>(const ODScenePointCloud<pcl::PointXYZRGBA> &, const std::string &, bool); + extern template void ODViewer::update<pcl::PointXYZ>(shared_ptr<pcl::PointCloud<pcl::PointXYZ> >, const std::string &, bool); extern template void ODViewer::update<pcl::PointXYZRGB>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> >, const std::string &, bool); extern template void ODViewer::update<pcl::PointXYZRGBA>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA> >, const std::string &, bool); + extern template void ODViewer::update<pcl::PointXYZ>(shared_ptr<ODScenePointCloud<pcl::PointXYZ> >, const std::string &, bool); + extern template void ODViewer::update<pcl::PointXYZRGB>(shared_ptr<ODScenePointCloud<pcl::PointXYZRGB> >, const std::string &, bool); + extern template void ODViewer::update<pcl::PointXYZRGBA>(shared_ptr<ODScenePointCloud<pcl::PointXYZRGBA> >, const std::string &, bool); + + extern template void ODViewer::update<pcl::PointXYZ>(const ODScenePointCloud<pcl::PointXYZ> &, const std::string &, bool); + extern template void ODViewer::update<pcl::PointXYZRGB>(const ODScenePointCloud<pcl::PointXYZRGB> &, const std::string &, bool); + extern template void ODViewer::update<pcl::PointXYZRGBA>(const ODScenePointCloud<pcl::PointXYZRGBA> &, const std::string &, bool); + + } #include "od/common/utils/ODViewer.hpp" diff --git a/common/src/pipeline/ODScene.cpp b/common/src/pipeline/ODScene.cpp index 80341b04..39f42c4f 100644 --- a/common/src/pipeline/ODScene.cpp +++ b/common/src/pipeline/ODScene.cpp @@ -70,7 +70,7 @@ namespace od { is_trained_ = true; } - cv::Mat ODSceneImage::getCVImage() + cv::Mat ODSceneImage::getCVImage() const { return cvimage_; } diff --git a/common/src/utils/ODViewer.cpp b/common/src/utils/ODViewer.cpp index 94a5f93c..5d1686be 100644 --- a/common/src/utils/ODViewer.cpp +++ b/common/src/utils/ODViewer.cpp @@ -7,7 +7,8 @@ namespace od { ODViewer::ODViewer() : status_(UNDEFINED) {} - void ODViewer::render(const cv::Mat & to_display, const std::string & window_name){ + void ODViewer::render(const cv::Mat & to_display, const std::string & window_name) + { if(status_ != CVMAT){ std::cout << "Switching viewer to cv::Mat mode" << std::endl; if(status_ == POINTCLOUD){ @@ -32,13 +33,72 @@ namespace od { cvNamedWindow(mat_window_name_.c_str(), CV_WINDOW_AUTOSIZE); } - void ODViewer::update(const cv::Mat & to_display, const std::string & window_name){ + void ODViewer::render(const ODSceneImage & to_display, const std::string & window_name) + { + render(to_display.getCVImage(), window_name); + } + + void ODViewer::render(shared_ptr<ODSceneImage> to_display, const std::string & window_name) + { + render(to_display->getCVImage(), window_name); + } + + void ODViewer::initCVWindow(const std::string & window_name) + { if(status_ != CVMAT){ + std::cout << "Switching viewer to cv::Mat mode" << std::endl; + if(status_ == POINTCLOUD){ + //BUGGED IN PCL, its not closing https://github.com/PointCloudLibrary/pcl/issues/172 + //viewer_->close(); + //Alternative solution +#if(WIN32) + HWND hWnd = (HWND)viewer3d->getRenderWindow()->GetGenericWindowId(); + viewer_.reset(); + DestroyWindow(hWnd); +#else + auto p = viewer_->getRenderWindow(); + viewer_.reset(); + p->Finalize(); +#endif + } + } + status_ = CVMAT; + mat_window_name_ = window_name; + mat_ = make_shared<cv::Mat>(); + cvStartWindowThread(); + cvNamedWindow(mat_window_name_.c_str(), CV_WINDOW_AUTOSIZE); + } + + void ODViewer::initPCLWindow(const std::string & cloud_name) + { + if(status_ != POINTCLOUD){ + std::cout << "Switching viewer to PointCloud mode" << std::endl; + if(status_ == CVMAT){ + cv::destroyWindow(mat_window_name_.c_str()); + } + } + + status_ = POINTCLOUD; + + if(!viewer_ || pcl_window_name_ != cloud_name) + { + viewer_ = make_shared<pcl::visualization::PCLVisualizer>(cloud_name); + viewer_->setBackgroundColor(0, 0, 0); + } + + pcl_window_name_ = cloud_name; + } + + void ODViewer::update(const cv::Mat & to_display, const std::string & window_name) + { + if(status_ != CVMAT) + { std::cout << "No cv::Mat to render! use render(const cv::Mat &) first!" << std::endl; return; } - if(window_name != mat_window_name_){ - std::cout << "No window " << window_name << " present. Please first use render(const cv::Mat &, const std::string &) to create the new window" << std::endl; + if(window_name != mat_window_name_) + { + render(to_display, window_name); return; } @@ -46,7 +106,18 @@ namespace od { } - void ODViewer::setBackGround(const cv::Scalar & color){ + void ODViewer::update(const ODSceneImage & to_display, const std::string & window_name) + { + update(to_display.getCVImage(), window_name); + } + + void ODViewer::update(shared_ptr<ODSceneImage> to_display, const std::string & window_name) + { + update(to_display->getCVImage(), window_name); + } + + void ODViewer::setBackGround(const cv::Scalar & color) + { switch(status_){ case POINTCLOUD : viewer_->setBackgroundColor(color[2], color[1], color[0]); @@ -61,7 +132,8 @@ namespace od { } - bool ODViewer::toStop(){ + bool ODViewer::toStop() + { switch(status_){ case POINTCLOUD : @@ -77,11 +149,13 @@ namespace od { } - void ODViewer::setBackGround(unsigned int r, unsigned int g, unsigned int b){ + void ODViewer::setBackGround(unsigned int r, unsigned int g, unsigned int b) + { setBackGround(cv::Scalar(b, g, r)); } - void ODViewer::spin(){ + void ODViewer::spin() + { switch(status_){ case POINTCLOUD : if(viewer_) @@ -97,12 +171,44 @@ namespace od { } } + unsigned int ODViewer::wait(unsigned int time) const + { + return cv::waitKey(time); + } + + void ODViewer::remove(const std::string & name) + { + if(status_ != POINTCLOUD){ + std::cout << "Viewer not in pointcloud mode! use render(shared_ptr<pcl::PointCloud<PointT>>) first!" << std::endl; + return; + } + + viewer_->removePointCloud(name); + } + // Explicit template function instantiation template void ODViewer::render<pcl::PointXYZ>(shared_ptr<pcl::PointCloud<pcl::PointXYZ> >, const std::string &, bool); template void ODViewer::render<pcl::PointXYZRGB>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> >, const std::string &, bool); template void ODViewer::render<pcl::PointXYZRGBA>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA> >, const std::string &, bool); + template void ODViewer::render<pcl::PointXYZ>(shared_ptr<ODScenePointCloud<pcl::PointXYZ> >, const std::string &, bool); + template void ODViewer::render<pcl::PointXYZRGB>(shared_ptr<ODScenePointCloud<pcl::PointXYZRGB> >, const std::string &, bool); + template void ODViewer::render<pcl::PointXYZRGBA>(shared_ptr<ODScenePointCloud<pcl::PointXYZRGBA> >, const std::string &, bool); + + template void ODViewer::render<pcl::PointXYZ>(const ODScenePointCloud<pcl::PointXYZ> &, const std::string &, bool); + template void ODViewer::render<pcl::PointXYZRGB>(const ODScenePointCloud<pcl::PointXYZRGB> &, const std::string &, bool); + template void ODViewer::render<pcl::PointXYZRGBA>(const ODScenePointCloud<pcl::PointXYZRGBA> &, const std::string &, bool); + template void ODViewer::update<pcl::PointXYZ>(shared_ptr<pcl::PointCloud<pcl::PointXYZ> >, const std::string &, bool); template void ODViewer::update<pcl::PointXYZRGB>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> >, const std::string &, bool); template void ODViewer::update<pcl::PointXYZRGBA>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA> >, const std::string &, bool); + + template void ODViewer::update<pcl::PointXYZ>(shared_ptr<ODScenePointCloud<pcl::PointXYZ> >, const std::string &, bool); + template void ODViewer::update<pcl::PointXYZRGB>(shared_ptr<ODScenePointCloud<pcl::PointXYZRGB> >, const std::string &, bool); + template void ODViewer::update<pcl::PointXYZRGBA>(shared_ptr<ODScenePointCloud<pcl::PointXYZRGBA> >, const std::string &, bool); + + template void ODViewer::update<pcl::PointXYZ>(const ODScenePointCloud<pcl::PointXYZ> &, const std::string &, bool); + template void ODViewer::update<pcl::PointXYZRGB>(const ODScenePointCloud<pcl::PointXYZRGB> &, const std::string &, bool); + template void ODViewer::update<pcl::PointXYZRGBA>(const ODScenePointCloud<pcl::PointXYZRGBA> &, const std::string &, bool); + } \ No newline at end of file diff --git a/examples/data/haarcascade_frontalface_alt.xml b/data/haarcascade_frontalface_alt.xml similarity index 100% rename from examples/data/haarcascade_frontalface_alt.xml rename to data/haarcascade_frontalface_alt.xml diff --git a/examples/data/haarcascade_frontalface_alt_gpu.xml b/data/haarcascade_frontalface_alt_gpu.xml similarity index 100% rename from examples/data/haarcascade_frontalface_alt_gpu.xml rename to data/haarcascade_frontalface_alt_gpu.xml diff --git a/examples/data/lbpcascade_frontalcatface.xml b/data/lbpcascade_frontalcatface.xml similarity index 100% rename from examples/data/lbpcascade_frontalcatface.xml rename to data/lbpcascade_frontalcatface.xml diff --git a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md index 75964c36..f9405a4b 100644 --- a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md +++ b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md @@ -203,7 +203,7 @@ Another important missing thing was the creation of a .deb package which can be All files in the library have been renamed according to a common scheme which consists in a Prefix "OD" followed by the first charachter of the file name uppercase. -##Templates and Windows compilation## +##Templates and Windows compilation 27/06/15## To improve compilation time of projects using the **OpenDetection** library I decided to precompile all the templates for the most common types. In this case almost all templates depend on *pcl* point types. The most commonly used point type for which I precompiled the library are: @@ -218,3 +218,15 @@ I then fixed all the linking in the whole library since there where some useless After that I tested compilation of the library under Windows, but without success. There have been several issues which are ut of the Open Deteciton library. I tested compilation using **Visual Studio 2015** and **MinGW**. Pcl has been installed by using the prebuilt version while opencv has to be built manually since *opencv_contrib* is necessary while not present in the prebuilt binaries. Building opencv3 with visual studio and cuda is not possible at the moment since cuda 7.5 is not compatible with visual studio 2015, so version 8 is necessary which will be available soon. MinGW is also not usable since compilation of opencv3 with cuda is disabled as flagged as incompatible by opencv. +##Face detection 27/06/15## + +To test the new cmake and code structure I **implemented face detection** (not recognition which is already available). Face detection is implemented in *OpenCV* both in **CPU** mode and in **GPU** mode (using **CUDA**). + +I created an interface which is using the *ODDetector2D* interface to use both **CPU** and **GPU** mode based on a parameter passed in the constructor. **GPU** mode is only available if the library was built with **GPU** support (using the *WITH_GPU* flag in the cmake file). + +To test the new detector I created two examples; the first one is *od_face_detecor_cam.cpp* which uses the first found webcam to detect faces; the second example is called *od_face_detector_files.cpp* which uses a path to a file folder to execute the face detection algorithm over all files present in that folder. + +I noticed that it is necessary to pass to the *ODFrameGenerator* a file expression like /home/test/images/\*.jpg. I don't really like this method since it is quite restrictive and not intuitive to call. I will add also a function using **boost filesystem** to iterate over all files which are OpenCV supported images, using as input a path name. +I added in the cmake the two new examples with a custom option and tested them successfully. + +The next step will be to add the functionality previously described to the frame generator; I will also add the possibility to use standard containers as input sources in roder to be able to use arrays, vectors and lists for example. \ No newline at end of file diff --git a/examples/objectdetector/CMakeLists.txt b/examples/objectdetector/CMakeLists.txt index 261d98cf..7d66eb5b 100644 --- a/examples/objectdetector/CMakeLists.txt +++ b/examples/objectdetector/CMakeLists.txt @@ -1,6 +1,7 @@ include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) +include_directories(${COMMON_IMPL_DIR}) include_directories(${SIMPLE_RANSAC_INCLUDE_DIR}) include_directories(${DETECTORS_IMPL_DIR}) include_directories(${CMAKE_3RDPARTY_DIR}/pugixml/src/) diff --git a/examples/objectdetector/od_cascade_cam.cpp b/examples/objectdetector/od_cascade_cam.cpp index 6184e285..74addd6f 100644 --- a/examples/objectdetector/od_cascade_cam.cpp +++ b/examples/objectdetector/od_cascade_cam.cpp @@ -31,9 +31,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/global2D/detection/ODCascadeDetector.h" #include "od/common/utils/ODFrameGenerator.h" +#include "od/common/utils/ODViewer.h" #include <boost/shared_ptr.hpp> -int main(int argc, char *argv[]) +int main(int argc, char * argv[]) { if(argc < 2){ @@ -48,11 +49,14 @@ int main(int argc, char *argv[]) detector.setTrainedDataLocation(trained_cascade); detector.init(); + od::ODViewer viewer; + viewer.initCVWindow(std::string("Overlay")); + //get scenes od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_DEVICE> frameGenerator(0); //GUI cv::namedWindow("Overlay", cv::WINDOW_NORMAL); - while(frameGenerator.isValid() && cv::waitKey(10) != 27) + while(frameGenerator.isValid() && viewer.wait(10) != 27) { boost::shared_ptr<od::ODSceneImage> scene = frameGenerator.getNextFrame(); @@ -60,9 +64,11 @@ int main(int argc, char *argv[]) boost::shared_ptr<od::ODDetections2D> detections = detector.detectOmni(scene); if(detections->size() > 0) - cv::imshow("Overlay", detections->getMetainfoImage()); + viewer.render(detections->getMetainfoImage(), "Overlay"); else - cv::imshow("Overlay", scene->getCVImage()); + viewer.render(scene, "Overlay"); + + viewer.spin(); } diff --git a/examples/objectdetector/od_cascade_files.cpp b/examples/objectdetector/od_cascade_files.cpp index 2b0abec7..fbfda158 100644 --- a/examples/objectdetector/od_cascade_files.cpp +++ b/examples/objectdetector/od_cascade_files.cpp @@ -31,6 +31,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/global2D/detection/ODCascadeDetector.h" #include "od/common/utils/ODFrameGenerator.h" +#include "od/common/utils/ODViewer.h" #include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) @@ -51,9 +52,11 @@ int main(int argc, char *argv[]) //get scenes od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(images); - //GUI - cv::namedWindow("Overlay", cv::WINDOW_NORMAL); - while(frameGenerator.isValid() && cv::waitKey(2000) != 27) + + od::ODViewer viewer; + viewer.initCVWindow(std::string("Overlay")); + + while(frameGenerator.isValid() && viewer.wait(10) != 27) { boost::shared_ptr<od::ODSceneImage> scene = frameGenerator.getNextFrame(); @@ -62,11 +65,14 @@ int main(int argc, char *argv[]) if(detections->size() > 0) { - cv::imshow("Overlay", detections->renderMetainfo(*scene).getCVImage()); + + viewer.render(detections->renderMetainfo(*scene), "Overlay"); cv::imwrite(frameGenerator.currentFile() + ".detected.jpg", detections->renderMetainfo(*scene).getCVImage()); } else - cv::imshow("Overlay", scene->getCVImage()); + viewer.render(scene, "Overlay"); + + viewer.spin(); } diff --git a/examples/objectdetector/od_example_framegenerator.cpp b/examples/objectdetector/od_example_framegenerator.cpp index 5efd421d..3ef236db 100644 --- a/examples/objectdetector/od_example_framegenerator.cpp +++ b/examples/objectdetector/od_example_framegenerator.cpp @@ -34,23 +34,25 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "od/common/utils/ODFrameGenerator.h" +#include "od/common/utils/ODViewer.h" + int main(int argc, char *argv[]) { //GUI and feedback - pcl::visualization::PCLVisualizer vis ("kinect"); + od::ODViewer viewer; + viewer.initPCLWindow(std::string("frame")); od::ODFrameGenerator<od::ODScenePointCloud<pcl::PointXYZRGBA>, od::GENERATOR_TYPE_DEVICE> frameGenerator(""); while(frameGenerator.isValid()) { - //get frame + boost::shared_ptr<od::ODScenePointCloud<pcl::PointXYZRGBA>> frame = frameGenerator.getNextFrame(); - vis.removePointCloud ("frame"); - vis.addPointCloud<pcl::PointXYZRGBA> (frame->getPointCloud(), "frame"); - vis.spinOnce (); + viewer.remove("frame"); + viewer.update(frame, "frame"); + viewer.spin(); - //free frame } return 0; } diff --git a/examples/objectdetector/od_face_detector_cam.cpp b/examples/objectdetector/od_face_detector_cam.cpp index ade87870..f5331bd5 100644 --- a/examples/objectdetector/od_face_detector_cam.cpp +++ b/examples/objectdetector/od_face_detector_cam.cpp @@ -1,9 +1,11 @@ #include "od/detectors/global2D/detection/ODFaceDetector.h" #include "od/common/utils/ODFrameGenerator.h" -#include "od/common/utils/ODShared_pointers.h" +#include "od/common/utils/ODViewer.h" #include <iostream> #include <string> +#include <boost/shared_ptr.hpp> + int main(int argc, char * argv[]) { @@ -22,25 +24,27 @@ int main(int argc, char * argv[]) frame_generator = make_shared<od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_DEVICE> >(0); - + od::ODViewer viewer; + viewer.initCVWindow(std::string("Faces")); + #ifdef WITH_GPU od::g2d::ODFaceDetector2D detector(gpu ? od::g2d::ODFaceDetector2D::GPU : od::g2d::ODFaceDetector2D::CPU, argv[1]); #else od::g2d::ODFaceDetector2D detector(od::g2d::ODFaceDetector2D::CPU, argv[1]); #endif - //GUI - cv::namedWindow("Faces"); - while(frame_generator->isValid() && cv::waitKey(10) != 27) + + while(frame_generator->isValid() && viewer.wait(50) != 27) { boost::shared_ptr<od::ODSceneImage> scene = frame_generator->getNextFrame(); - //Detect boost::shared_ptr<od::ODDetections2D> detections = detector.detectOmni(scene); if(detections->size() > 0) - cv::imshow("Faces", detections->renderMetainfo(scene).getCVImage()); + viewer.update(detections->renderMetainfo(scene), std::string("Faces")); else - cv::imshow("Faces", scene->getCVImage()); + viewer.update(scene, std::string("Faces")); + + viewer.spin(); } return 0; diff --git a/examples/objectdetector/od_face_detector_files.cpp b/examples/objectdetector/od_face_detector_files.cpp index 88e81c6f..9afe567e 100644 --- a/examples/objectdetector/od_face_detector_files.cpp +++ b/examples/objectdetector/od_face_detector_files.cpp @@ -1,6 +1,10 @@ #include "od/detectors/global2D/detection/ODFaceDetector.h" #include "od/common/utils/ODFrameGenerator.h" +#include "od/common/utils/ODViewer.h" + #include <iostream> +#include <boost/shared_ptr.hpp> + int main(int argc, char * argv[]) { @@ -14,7 +18,7 @@ int main(int argc, char * argv[]) od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_FILE_LIST> frame_generator(image_path); unsigned int gpu = 0; - if(argc > 2) + if(argc > 3) gpu = atoi(argv[3]); #ifdef WITH_GPU @@ -22,20 +26,24 @@ int main(int argc, char * argv[]) #else od::g2d::ODFaceDetector2D detector(od::g2d::ODFaceDetector2D::CPU, argv[2]); #endif - //GUI - cv::namedWindow("Faces", cv::WINDOW_NORMAL); - while(frame_generator.isValid() && cv::waitKey(10) != 27) + + od::ODViewer viewer; + viewer.initCVWindow(std::string("Faces")); + + while(frame_generator.isValid() && viewer.wait(10) != 27) { boost::shared_ptr<od::ODSceneImage> scene = frame_generator.getNextFrame(); - //Detect + boost::shared_ptr<od::ODDetections2D> detections = detector.detectOmni(scene); if(detections->size() > 0) - cv::imshow("Faces", detections->renderMetainfo(scene).getCVImage()); - else - cv::imshow("Faces", scene->getCVImage()); + viewer.update(detections->renderMetainfo(scene), std::string("Faces")); + else + viewer.update(scene, std::string("Faces")); + + viewer.spin(); + viewer.wait(0); - cv::waitKey(0); } return 0; diff --git a/examples/objectdetector/od_hog_train.cpp b/examples/objectdetector/od_hog_train.cpp index a0c82378..b7eacfbd 100644 --- a/examples/objectdetector/od_hog_train.cpp +++ b/examples/objectdetector/od_hog_train.cpp @@ -34,6 +34,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/global2D/training/ODHOGTrainer.h" #include "od/detectors/global2D/detection/ODHOGDetector.h" #include "od/common/utils/ODFrameGenerator.h" +#include "od/common/utils/ODViewer.h" #include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) @@ -64,8 +65,10 @@ int main(int argc, char *argv[]) //get scenes od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(test_images); //GUI - cv::namedWindow("Overlay", cv::WINDOW_NORMAL); - while(frameGenerator.isValid() && cv::waitKey(10) != 27) + od::ODViewer viewer; + viewer.initCVWindow(std::string("Overlay")); + + while(frameGenerator.isValid() && viewer.wait(10) != 27) { boost::shared_ptr<od::ODSceneImage> scene = frameGenerator.getNextFrame(); @@ -73,10 +76,11 @@ int main(int argc, char *argv[]) boost::shared_ptr<od::ODDetections2D> detections = detector.detectOmni(scene); if(detections->size() > 0) - cv::imshow("Overlay", detections->renderMetainfo(*scene).getCVImage()); + viewer.render(detections->renderMetainfo(*scene), "Overlay"); else - cv::imshow("Overlay", scene->getCVImage()); + viewer.render(scene, "Overlay"); + viewer.spin(); } return 0; diff --git a/examples/objectdetector/od_image_cadrecog_camera.cpp b/examples/objectdetector/od_image_cadrecog_camera.cpp index fc1cc442..b6165bb1 100644 --- a/examples/objectdetector/od_image_cadrecog_camera.cpp +++ b/examples/objectdetector/od_image_cadrecog_camera.cpp @@ -33,6 +33,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h" #include "od/detectors/local2D/detection/ODCADRecognizer2DLocal.h" #include "od/common/utils/ODFrameGenerator.h" +#include "od/common/utils/ODViewer.h" #include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) @@ -61,8 +62,10 @@ int main(int argc, char *argv[]) //get scenes od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_DEVICE> frameGenerator(0); //GUI - cv::namedWindow("Overlay", cv::WINDOW_NORMAL); - while(frameGenerator.isValid() && cv::waitKey(10) != 27) + od::ODViewer viewer; + viewer.initCVWindow(std::string("Overlay")); + + while(frameGenerator.isValid() && viewer.wait(10) != 27) { boost::shared_ptr<od::ODSceneImage> scene = frameGenerator.getNextFrame(); @@ -70,10 +73,11 @@ int main(int argc, char *argv[]) boost::shared_ptr<ODDetections3D> detections = detector.detectOmni(scene); if(detections->size() > 0) - cv::imshow("Overlay", detections->getMetainfoImage()); + viewer.update(detections->getMetainfoImage(), "Overlay"); else - cv::imshow("Overlay", scene->getCVImage()); + viewer.update(scene, "Overlay"); + viewer.spin(); } return 0; diff --git a/examples/objectdetector/od_image_cadrecog_files.cpp b/examples/objectdetector/od_image_cadrecog_files.cpp index 65ce2285..075bacee 100644 --- a/examples/objectdetector/od_image_cadrecog_files.cpp +++ b/examples/objectdetector/od_image_cadrecog_files.cpp @@ -28,6 +28,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h" #include "od/detectors/local2D/detection/ODCADRecognizer2DLocal.h" #include "od/common/utils/ODFrameGenerator.h" +#include "od/common/utils/ODViewer.h" #include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) @@ -56,8 +57,10 @@ int main(int argc, char *argv[]) //get scenes od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(query_images); //GUI - cv::namedWindow("Overlay", cv::WINDOW_NORMAL); - while(frameGenerator.isValid() && cv::waitKey(10) != 27) + od::ODViewer viewer; + viewer.initCVWindow(std::string("Overlay")); + + while(frameGenerator.isValid() && viewer.wait(10) != 27) { boost::shared_ptr<od::ODSceneImage> scene = frameGenerator.getNextFrame(); cv::imshow("Overlay", scene->getCVImage()); @@ -66,9 +69,9 @@ int main(int argc, char *argv[]) boost::shared_ptr<od::ODDetections3D> detections = detector.detectOmni(scene); if(detections->size() > 0) - cv::imshow("Overlay", detections->getMetainfoImage()); + viewer.update(detections->getMetainfoImage(), "Overlay"); else - cv::imshow("Overlay", scene->getCVImage()); + viewer.update(scene, "Overlay"); } diff --git a/examples/objectdetector/od_image_customhog.cpp b/examples/objectdetector/od_image_customhog.cpp index e7f0257a..9cd73724 100644 --- a/examples/objectdetector/od_image_customhog.cpp +++ b/examples/objectdetector/od_image_customhog.cpp @@ -33,6 +33,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/global2D/detection/ODHOGDetector.h" #include "od/common/utils/ODFrameGenerator.h" +#include "od/common/utils/ODViewer.h" #include <boost/shared_ptr.hpp> int main(int argc, char * argv[]) @@ -51,9 +52,11 @@ int main(int argc, char * argv[]) //get scenes od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_DEVICE> frameGenerator(0); - //GUI - cv::namedWindow("Overlay", cv::WINDOW_NORMAL); - while(frameGenerator.isValid() && cv::waitKey(10) != 27) + + od::ODViewer viewer; + viewer.initCVWindow(std::string("Overlay")); + + while(frameGenerator.isValid() && viewer.wait(10) != 27) { boost::shared_ptr<od::ODSceneImage> scene = frameGenerator.getNextFrame(); @@ -61,9 +64,11 @@ int main(int argc, char * argv[]) boost::shared_ptr<od::ODDetections2D> detections = detector.detectOmni(scene); if(detections->size() > 0) - cv::imshow("Overlay", detections->renderMetainfo(*scene).getCVImage()); + viewer.update(detections->renderMetainfo(*scene), "Overlay"); else - cv::imshow("Overlay", scene->getCVImage()); + viewer.update(scene, "Overlay"); + + viewer.spin(); } return 0; diff --git a/examples/objectdetector/od_image_facerecog.cpp b/examples/objectdetector/od_image_facerecog.cpp index 08ba4af1..33aada69 100644 --- a/examples/objectdetector/od_image_facerecog.cpp +++ b/examples/objectdetector/od_image_facerecog.cpp @@ -32,6 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/global2D/ODFaceRecognizer.h" #include "od/common/utils/ODFrameGenerator.h" +#include "od/common/utils/ODViewer.h" #include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) @@ -56,9 +57,11 @@ int main(int argc, char *argv[]) //get scenes od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(query_images); - //GUI - cv::namedWindow("Overlay", cv::WINDOW_NORMAL); - while(frameGenerator.isValid() && cv::waitKey(10) != 27) + + od::ODViewer viewer; + viewer.initCVWindow(std::string("Overlay")); + + while(frameGenerator.isValid() && viewer.wait(10) != 27) { boost::shared_ptr<od::ODSceneImage> scene = frameGenerator.getNextFrame(); @@ -66,7 +69,7 @@ int main(int argc, char *argv[]) boost::shared_ptr<od::ODDetections> detections = objdetector.detect(scene); (*detections)[0]->printSelf(); - cv::imshow("Overlay", scene->getCVImage()); + viewer.update(scene, "Overlay"); } return 0; diff --git a/examples/objectdetector/od_image_hog_files.cpp b/examples/objectdetector/od_image_hog_files.cpp index 4feac2f4..acf8b393 100644 --- a/examples/objectdetector/od_image_hog_files.cpp +++ b/examples/objectdetector/od_image_hog_files.cpp @@ -32,6 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/global2D/detection/ODCascadeDetector.h" #include "od/detectors/global2D/detection/ODHOGDetector.h" #include "od/common/utils/ODFrameGenerator.h" +#include "od/common/utils/ODViewer.h" #include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) @@ -53,9 +54,11 @@ int main(int argc, char *argv[]) //get scenes od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(images); - //GUI - cv::namedWindow("Overlay", cv::WINDOW_NORMAL); - while(frameGenerator.isValid() && cv::waitKey(10) != 27) + + od::ODViewer viewer; + viewer.initCVWindow(std::string("Overlay")); + + while(frameGenerator.isValid() && viewer.wait(10) != 27) { boost::shared_ptr<od::ODSceneImage> scene = frameGenerator.getNextFrame(); @@ -63,9 +66,11 @@ int main(int argc, char *argv[]) boost::shared_ptr<od::ODDetections2D> detections = detector.detectOmni(scene); if(detections->size() > 0) - cv::imshow("Overlay", detections->renderMetainfo(*scene).getCVImage()); + viewer.update(detections->renderMetainfo(*scene), "Overlay"); else - cv::imshow("Overlay", scene->getCVImage()); + viewer.update(scene, "Overlay"); + + viewer.spin(); } return 0; diff --git a/examples/objectdetector/od_multialgo_files.cpp b/examples/objectdetector/od_multialgo_files.cpp index d46ddfc8..56d0e2db 100644 --- a/examples/objectdetector/od_multialgo_files.cpp +++ b/examples/objectdetector/od_multialgo_files.cpp @@ -27,7 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/misc/detection/ODDetectorMultiAlgo.hpp" #include "od/common/utils/ODFrameGenerator.h" -#include <opencv2/highgui.hpp> +#include "od/common/utils/ODViewer.h" #include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) @@ -47,9 +47,11 @@ int main(int argc, char *argv[]) //get scenes od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(query_images); - //GUI - cv::namedWindow("Overlay", cv::WINDOW_NORMAL); - while(frameGenerator.isValid() && cv::waitKey(10) != 27) + + od::ODViewer viewer; + viewer.initCVWindow(std::string("Overlay")); + + while(frameGenerator.isValid() && viewer.wait(10) != 27) { boost::shared_ptr<od::ODSceneImage> scene = frameGenerator.getNextFrame(); @@ -57,9 +59,9 @@ int main(int argc, char *argv[]) boost::shared_ptr<od::ODDetections2D> detections = detector.detectOmni(scene); if(detections->size() > 0) - cv::imshow("Overlay", detections->renderMetainfo(*scene).getCVImage()); + viewer.render(detections->renderMetainfo(*scene), "Overlay"); else - cv::imshow("Overlay", scene->getCVImage()); + viewer.render(scene, "Overlay"); } diff --git a/examples/objectdetector/od_multialgo_pc.cpp b/examples/objectdetector/od_multialgo_pc.cpp index adad16aa..8fb32b5a 100644 --- a/examples/objectdetector/od_multialgo_pc.cpp +++ b/examples/objectdetector/od_multialgo_pc.cpp @@ -34,6 +34,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/misc/detection/ODDetectorMultiAlgo.hpp" #include "od/detectors/global3D/ODPointCloudGlobalMatching.h" #include "od/common/utils/ODFrameGenerator.h" +#include "od/common/utils/ODViewer.h" #include <string> #include <boost/shared_ptr.hpp> diff --git a/examples/objectdetector/od_pc_global.cpp b/examples/objectdetector/od_pc_global.cpp index b6a0209f..59087d6c 100644 --- a/examples/objectdetector/od_pc_global.cpp +++ b/examples/objectdetector/od_pc_global.cpp @@ -33,9 +33,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -#include <iostream> #include "od/detectors/global3D/ODPointCloudGlobalMatching.h" +#include "od/common/utils/ODViewer.h" #include <boost/shared_ptr.hpp> +#include <iostream> int main(int argc, char *argv[]) { @@ -61,8 +62,7 @@ int main(int argc, char *argv[]) //Get a scene boost::shared_ptr<od::ODScenePointCloud<pcl::PointXYZRGBA> > scene = boost::make_shared<od::ODScenePointCloud<pcl::PointXYZRGBA>>(pointcloud_file); - - + boost::shared_ptr<od::ODDetections3D> detections = detector.detectOmni(scene); //feedback diff --git a/examples/objectdetector/od_pc_global_files.cpp b/examples/objectdetector/od_pc_global_files.cpp index 64e8445d..4023f92b 100644 --- a/examples/objectdetector/od_pc_global_files.cpp +++ b/examples/objectdetector/od_pc_global_files.cpp @@ -33,10 +33,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -#include <vector> #include "od/common/utils/ODFrameGenerator.h" #include "od/detectors/global3D/ODPointCloudGlobalMatching.h" +#include "od/common/utils/ODViewer.h" #include <boost/shared_ptr.hpp> +#include <vector> + int main(int argc, char *argv[]) { @@ -75,11 +77,6 @@ int main(int argc, char *argv[]) detections->at(i)->printSelf(); } - //vis.removePointCloud ("frame"); - //vis.addPointCloud<pcl::PointXYZRGBA> (frame->getPointCloud(), "frame"); - - //vis.spinOnce (5); - } return 0; diff --git a/examples/objectdetector/od_pc_global_real_time.cpp b/examples/objectdetector/od_pc_global_real_time.cpp index 9b0bad45..89bbdd5e 100644 --- a/examples/objectdetector/od_pc_global_real_time.cpp +++ b/examples/objectdetector/od_pc_global_real_time.cpp @@ -36,6 +36,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/global3D/ODPointCloudGlobalMatching.h" #include "od/common/utils/ODFrameGenerator.h" +#include "od/common/utils/ODViewer.h" #include <string> #include <boost/shared_ptr.hpp> @@ -94,7 +95,7 @@ int main(int argc, char *argv[]) ss.str(std::string()); } - vis.spinOnce (); + vis.spinOnce(); } return 0; From 7b80afbd5518fea2e2f54fcd197bb30d003c7a72 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Tue, 5 Jul 2016 16:23:23 +0200 Subject: [PATCH 40/79] removed face detection and added it as cascade classifier in the new gpu module --- CMakeLists.txt | 2 +- .../global2D/detection/ODCascadeDetector.h | 14 +- .../global2D/detection/ODFaceDetector.h | 69 ------- detectors/src/global2D/CMakeLists.txt | 1 - .../global2D/detection/ODCascadeDetector.cpp | 84 ++++++-- .../src/global2D/detection/ODFaceDetector.cpp | 121 ------------ examples/objectdetector/CMakeLists.txt | 7 +- .../objectdetector/od_face_detector_cam.cpp | 22 ++- .../objectdetector/od_face_detector_files.cpp | 23 ++- gpu/detectors/CMakeLists.txt | 11 ++ .../global2D/detection/ODCascadeDetector.h | 107 ++++++++++ gpu/detectors/src/global2D/CMakeLists.txt | 20 ++ .../global2D/detection/ODCascadeDetector.cpp | 184 ++++++++++++++++++ 13 files changed, 446 insertions(+), 219 deletions(-) delete mode 100644 detectors/include/od/detectors/global2D/detection/ODFaceDetector.h delete mode 100644 detectors/src/global2D/detection/ODFaceDetector.cpp create mode 100644 gpu/detectors/CMakeLists.txt create mode 100644 gpu/detectors/include/od/gpu/detectors/global2D/detection/ODCascadeDetector.h create mode 100644 gpu/detectors/src/global2D/CMakeLists.txt create mode 100644 gpu/detectors/src/global2D/detection/ODCascadeDetector.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f2e19202..222b1974 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,7 +50,7 @@ if(WITH_GPU) endif() # Set modules -set(OD_MODULES_NAMES 3rdparty common detectors doc examples) +set(OD_MODULES_NAMES 3rdparty common detectors "gpu/detectors" doc examples ) set(OD_MODULES_DIRS ${OD_MODULES_NAMES}) # Add modules diff --git a/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h b/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h index ba7631e8..81a8b126 100644 --- a/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h +++ b/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h @@ -51,7 +51,7 @@ namespace od { public: - ODCascadeDetector(const std::string & trained_data_location = std::string(""), double scale_factor = 1.1, int min_neighbors = 3, + ODCascadeDetector(const std::string & trainer = std::string("cascade.xml"), const std::string & trained_data_location = std::string(""), double scale_factor = 1.1, int min_neighbors = 3, int flags = 0, const cv::Size & min_size = cv::Size(), const cv::Size & max_size = cv::Size()); void init(); @@ -61,6 +61,18 @@ namespace od shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene); + void setScale(const float scale); + float getScale() const; + + void setMinNeighbors(const unsigned int min_neighbors); + unsigned int getMinNeighbors() const; + + void setMinSize(const cv::Size & size); + cv::Size getMinSize() const; + + void setMaxSize(const cv::Size & size); + cv::Size getMaxSize() const; + private: shared_ptr<cv::CascadeClassifier> haar_cascade_; diff --git a/detectors/include/od/detectors/global2D/detection/ODFaceDetector.h b/detectors/include/od/detectors/global2D/detection/ODFaceDetector.h deleted file mode 100644 index af8e4d08..00000000 --- a/detectors/include/od/detectors/global2D/detection/ODFaceDetector.h +++ /dev/null @@ -1,69 +0,0 @@ - -#pragma once -#include "opencv2/highgui/highgui.hpp" -#if WITH_GPU -#include "opencv2/cudaobjdetect.hpp" -#include "opencv2/cudaimgproc.hpp" -#include "opencv2/cudawarping.hpp" -#endif -#include "od/common/utils/ODShared_pointers.h" -#include "od/common/pipeline/ODDetector.h" -#include "opencv2/objdetect/objdetect.hpp" -#include "opencv2/imgproc/imgproc.hpp" - -namespace od -{ - namespace g2d - { - - class ODFaceDetector2D: public ODDetector2D { - - public: - -#ifndef DOXYGEN_SHOULD_SKIP_THIS - - OD_DEFINE_ENUM_WITH_STRING_CONVERSIONS(FaceRecogType, (GPU)(CPU)) -#endif - - ODFaceDetector2D(ODFaceDetector2D::FaceRecogType type, const std::string & path = std::string()); - - shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene); - shared_ptr<ODDetections2D> detectOmni(shared_ptr<ODSceneImage> scene); - shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene); - - void setScale(const float scale); - float getScale() const; - - void setMinNeighbors(const unsigned int mn); - unsigned int getMinNeighbors() const; - - void setMinSize(const cv::Size & size); - cv::Size getMinSize() const; - - void setMaxSize(const cv::Size & size); - cv::Size getMaxSize() const; - - void setFindLargest(bool largest); - bool getFindLargest() const; - - void init(); - - private: - - FaceRecogType type_; - std::vector<cv::Rect> faces; - float scale_; - unsigned int mn_; - bool largest_; - cv::Size min_size_, max_size_; - cv::Mat frame_gray_; - shared_ptr<cv::CascadeClassifier> face_cascade_; - -#ifdef WITH_GPU - cv::Ptr<cv::cuda::CascadeClassifier> face_cascade_gpu_; - cv::cuda::GpuMat gray_gpu_, faces_buf_gpu_, color_gpu_; -#endif - - }; - } -} \ No newline at end of file diff --git a/detectors/src/global2D/CMakeLists.txt b/detectors/src/global2D/CMakeLists.txt index 6cf0069c..4a4f09f8 100644 --- a/detectors/src/global2D/CMakeLists.txt +++ b/detectors/src/global2D/CMakeLists.txt @@ -12,7 +12,6 @@ if(build) "ODFaceRecognizer.cpp" "detection/ODCascadeDetector.cpp" "detection/ODHOGDetector.cpp" - "detection/ODFaceDetector.cpp" "training/ODHOGTrainer.cpp" ) diff --git a/detectors/src/global2D/detection/ODCascadeDetector.cpp b/detectors/src/global2D/detection/ODCascadeDetector.cpp index 3e75e951..96f6a20f 100644 --- a/detectors/src/global2D/detection/ODCascadeDetector.cpp +++ b/detectors/src/global2D/detection/ODCascadeDetector.cpp @@ -35,13 +35,13 @@ namespace od namespace g2d { - ODCascadeDetector::ODCascadeDetector(const std::string & trained_data_location, double scale_factor, int min_neighbors, int flags, + ODCascadeDetector::ODCascadeDetector(const std::string & trainer, const std::string & trained_data_location, double scale_factor, int min_neighbors, int flags, const cv::Size & min_size, const cv::Size & max_size): ODDetector2D(trained_data_location), scale_factor_(scale_factor), min_neighbors_(min_neighbors), min_size_(min_size), max_size_(max_size) { trained_location_identifier_ = std::string("CASCADE"); - trained_data_id_ = std::string("cascade.xml"); + trained_data_id_ = trainer; meta_info_ = true; } @@ -58,25 +58,31 @@ namespace od shared_ptr<ODDetections2D> ODCascadeDetector::detectOmni(shared_ptr<ODSceneImage> scene) { + + if(!haar_cascade_){ + std::cout << "Call init() first!" << std::endl; + return nullptr; + } + cv::Mat gray; cv::cvtColor(scene->getCVImage(), gray, CV_BGR2GRAY); - // Find the faces in the frame: - std::vector<cv::Rect_<int> > faces; - haar_cascade_->detectMultiScale(gray, faces, scale_factor_, min_neighbors_, 0, min_size_, max_size_); + + std::vector<cv::Rect_<int> > objects; + haar_cascade_->detectMultiScale(gray, objects, scale_factor_, min_neighbors_, 0, min_size_, max_size_); //always create detections shared_ptr<ODDetections2D> detections = make_shared<ODDetections2D>(); - cv::Mat viz = scene->getCVImage().clone(); - for(auto & face : faces) + cv::Mat viz = scene->getCVImage(); + for(auto & o : objects) { - // Process face by face: - shared_ptr<ODDetection2D> detection2D = make_shared<ODDetection2D>(ODDetection::OD_CLASSIFICATION, "FACE", 1); - detection2D->setBoundingBox(face); + // Process object by object: + shared_ptr<ODDetection2D> detection2D = make_shared<ODDetection2D>(ODDetection::OD_CLASSIFICATION, "OBJ", 1); + detection2D->setBoundingBox(o); detections->push_back(detection2D); if(meta_info_) { - cv::rectangle(viz, face, CV_RGB(0, 255, 0), 1); + cv::rectangle(viz, o, CV_RGB(0, 255, 0), 1); } } detections->setMetainfoImage(viz); @@ -86,24 +92,70 @@ namespace od shared_ptr<ODDetections> ODCascadeDetector::detect(shared_ptr<ODSceneImage> scene) { + + if(!haar_cascade_){ + std::cout << "Call init() first!" << std::endl; + return nullptr; + } + //always create detections shared_ptr<ODDetections> detections = make_shared<ODDetections>(); cv::Mat gray; cv::cvtColor(scene->getCVImage(), gray, CV_BGR2GRAY); - // Find the faces in the frame: - std::vector<cv::Rect_<int> > faces; + // Find the objects in the frame: + std::vector<cv::Rect_<int> > objects; //hack for single detection, //note: maxsize = minsize = size of input image for single window detection //todo: implement in some other way of fast single detection; currently this will work, but maynot be fast - haar_cascade_->detectMultiScale(gray, faces, 5, min_neighbors_, 0, gray.size(), gray.size()); - if(faces.size() > 0) + haar_cascade_->detectMultiScale(gray, objects, 5, min_neighbors_, 0, gray.size(), gray.size()); + if(objects.size() > 0) { - detections->push_back(make_shared<ODDetection>(ODDetection::OD_CLASSIFICATION, "FACE", 1)); + detections->push_back(make_shared<ODDetection>(ODDetection::OD_CLASSIFICATION, "OBJ", 1)); } return detections; } + void ODCascadeDetector::setScale(const float scale) + { + scale_factor_ = scale; + } + + float ODCascadeDetector::getScale() const + { + return scale_factor_; + } + + void ODCascadeDetector::setMinNeighbors(const unsigned int min_neighbors) + { + min_neighbors_ = min_neighbors; + } + + unsigned int ODCascadeDetector::getMinNeighbors() const + { + return min_neighbors_; + } + + void ODCascadeDetector::setMinSize(const cv::Size & size) + { + min_size_ = size; + } + + cv::Size ODCascadeDetector::getMinSize() const + { + return min_size_; + } + + void ODCascadeDetector::setMaxSize(const cv::Size & size) + { + max_size_ = size; + } + + cv::Size ODCascadeDetector::getMaxSize() const + { + return max_size_; + } + } } \ No newline at end of file diff --git a/detectors/src/global2D/detection/ODFaceDetector.cpp b/detectors/src/global2D/detection/ODFaceDetector.cpp deleted file mode 100644 index 4292f42c..00000000 --- a/detectors/src/global2D/detection/ODFaceDetector.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include "od/detectors/global2D/detection/ODFaceDetector.h" - -namespace od -{ - namespace g2d - { - - ODFaceDetector2D::ODFaceDetector2D(ODFaceDetector2D::FaceRecogType type, const std::string & path) : - type_(type), scale_(1.1), mn_(2), largest_(true) - { - - std::string face_cascade = path.empty() ? "haarcascade_frontalface_alt.xml" : path; - if(type_ == GPU){ - face_cascade_gpu_ = cv::cuda::CascadeClassifier::create(face_cascade); - face_cascade_gpu_->setFindLargestObject(largest_); - }else{ - face_cascade_ = make_shared<cv::CascadeClassifier>(face_cascade); - } - } - - shared_ptr<ODDetections2D> ODFaceDetector2D::detectOmni(shared_ptr<ODSceneImage> scene) - { - - cv::Mat input = scene->getCVImage(); - faces.clear(); - - if(type_ == GPU){ - - color_gpu_.upload(input); - cv::cuda::cvtColor(color_gpu_, gray_gpu_, CV_BGR2GRAY); - - face_cascade_gpu_->setFindLargestObject(largest_); - - face_cascade_gpu_->setScaleFactor(scale_); - face_cascade_gpu_->detectMultiScale(gray_gpu_, faces_buf_gpu_); - face_cascade_gpu_->convert(faces_buf_gpu_, faces); - - }else{ - cv::cvtColor(input, frame_gray_, CV_BGR2GRAY); - //cv::equalizeHist(frame_gray_, frame_gray_); - face_cascade_->detectMultiScale(frame_gray_, faces, scale_, mn_, 0|CV_HAAR_SCALE_IMAGE, min_size_, max_size_); - } - - shared_ptr<ODDetections2D> detections = make_shared<ODDetections2D>(); - for(auto & face : faces){ - shared_ptr<ODDetection2D> detection = make_shared<ODDetection2D>(ODDetection::OD_DETECTION, std::string("face")); - detection->bounding_box_2d_ = face; - detections->push_back(detection); - } - - return detections; - } - - shared_ptr<ODDetections> ODFaceDetector2D::detect(shared_ptr<ODSceneImage> scene) - { - std::cout << "Not implemented. Use detectOmni(shared_ptr<ODSceneImage>) instead." << std::endl; - return nullptr; - } - - shared_ptr<ODDetections> ODFaceDetector2D::detectOmni(shared_ptr<ODScene> scene) - { - std::cout << "Not implemented. Use detectOmni(shared_ptr<ODSceneImage>) instead." << std::endl; - return nullptr; - } - - void ODFaceDetector2D::setScale(const float scale) - { - scale_ = scale; - } - - float ODFaceDetector2D::getScale() const - { - return scale_; - } - - void ODFaceDetector2D::setMinNeighbors(const unsigned int mn) - { - mn_ = mn; - } - - unsigned int ODFaceDetector2D::getMinNeighbors() const - { - return mn_; - } - - void ODFaceDetector2D::setMinSize(const cv::Size & size) - { - min_size_ = size; - } - - cv::Size ODFaceDetector2D::getMinSize() const - { - return min_size_; - } - - void ODFaceDetector2D::setMaxSize(const cv::Size & size) - { - max_size_ = size; - } - - cv::Size ODFaceDetector2D::getMaxSize() const - { - return max_size_; - } - - void ODFaceDetector2D::init(){ - std::cout << "Unused method" << std::endl; - } - - void ODFaceDetector2D::setFindLargest(bool largest) - { - largest_ = largest; - } - - bool ODFaceDetector2D::getFindLargest() const - { - return largest_; - } - - } -} \ No newline at end of file diff --git a/examples/objectdetector/CMakeLists.txt b/examples/objectdetector/CMakeLists.txt index 7d66eb5b..2ee5f6ba 100644 --- a/examples/objectdetector/CMakeLists.txt +++ b/examples/objectdetector/CMakeLists.txt @@ -1,5 +1,8 @@ include_directories(${DETECTORS_INCLUDE_DIR}) +if(WITH_GPU) + include_directories(${GPU_DETECTORS_INCLUDE_DIR}) +endif() include_directories(${COMMON_INCLUDE_DIR}) include_directories(${COMMON_IMPL_DIR}) include_directories(${SIMPLE_RANSAC_INCLUDE_DIR}) @@ -39,14 +42,14 @@ option(face_detection_files_example "Build the face detection example" ON) if(face_detection_files_example) OD_ADD_EXAMPLE(od_face_detection_files FILES od_face_detector_files.cpp - LINK_WITH od_global_image_detector) + LINK_WITH od_global_image_detector od_gpu_global_image_detector) endif() option(face_detection_cam_example "Build the face detection example" ON) if(face_detection_cam_example) OD_ADD_EXAMPLE(od_face_detection_cam FILES od_face_detector_cam.cpp - LINK_WITH od_global_image_detector) + LINK_WITH od_global_image_detector od_gpu_global_image_detector) endif() option(image_customhog_example "Build the custom hog example" ON) diff --git a/examples/objectdetector/od_face_detector_cam.cpp b/examples/objectdetector/od_face_detector_cam.cpp index f5331bd5..974a42a3 100644 --- a/examples/objectdetector/od_face_detector_cam.cpp +++ b/examples/objectdetector/od_face_detector_cam.cpp @@ -1,4 +1,8 @@ -#include "od/detectors/global2D/detection/ODFaceDetector.h" +#include "od/detectors/global2D/detection/ODCascadeDetector.h" +#ifdef WITH_GPU + #include "od/gpu/detectors/global2D/detection/ODCascadeDetector.h" +#endif + #include "od/common/utils/ODFrameGenerator.h" #include "od/common/utils/ODViewer.h" @@ -27,17 +31,27 @@ int main(int argc, char * argv[]) od::ODViewer viewer; viewer.initCVWindow(std::string("Faces")); + boost::shared_ptr<od::ODDetector2D> detector; + + if(gpu) + { #ifdef WITH_GPU - od::g2d::ODFaceDetector2D detector(gpu ? od::g2d::ODFaceDetector2D::GPU : od::g2d::ODFaceDetector2D::CPU, argv[1]); + detector = boost::make_shared<od::gpu::g2d::ODCascadeDetector>(argv[1]); #else - od::g2d::ODFaceDetector2D detector(od::g2d::ODFaceDetector2D::CPU, argv[1]); + std::cout << "Compiled without gpu support. Recompile to use gpu support" << std::endl; + return -1; #endif + }else{ + detector = boost::make_shared<od::g2d::ODCascadeDetector>(argv[1]); + } + + detector->init(); while(frame_generator->isValid() && viewer.wait(50) != 27) { boost::shared_ptr<od::ODSceneImage> scene = frame_generator->getNextFrame(); - boost::shared_ptr<od::ODDetections2D> detections = detector.detectOmni(scene); + boost::shared_ptr<od::ODDetections2D> detections = detector->detectOmni(scene); if(detections->size() > 0) viewer.update(detections->renderMetainfo(scene), std::string("Faces")); diff --git a/examples/objectdetector/od_face_detector_files.cpp b/examples/objectdetector/od_face_detector_files.cpp index 9afe567e..6fef557a 100644 --- a/examples/objectdetector/od_face_detector_files.cpp +++ b/examples/objectdetector/od_face_detector_files.cpp @@ -1,4 +1,7 @@ -#include "od/detectors/global2D/detection/ODFaceDetector.h" +#include "od/detectors/global2D/detection/ODCascadeDetector.h" +#ifdef WITH_GPU + #include "od/gpu/detectors/global2D/detection/ODCascadeDetector.h" +#endif #include "od/common/utils/ODFrameGenerator.h" #include "od/common/utils/ODViewer.h" @@ -21,11 +24,23 @@ int main(int argc, char * argv[]) if(argc > 3) gpu = atoi(argv[3]); + boost::shared_ptr<od::ODDetector2D> detector; + + + + if(gpu) + { #ifdef WITH_GPU - od::g2d::ODFaceDetector2D detector(gpu ? od::g2d::ODFaceDetector2D::GPU : od::g2d::ODFaceDetector2D::CPU, argv[2]); + detector = boost::make_shared<od::gpu::g2d::ODCascadeDetector>(argv[1]); #else - od::g2d::ODFaceDetector2D detector(od::g2d::ODFaceDetector2D::CPU, argv[2]); + std::cout << "Compiled without gpu support. Recompile to use gpu support" << std::endl; + return -1; #endif + }else{ + detector = boost::make_shared<od::g2d::ODCascadeDetector>(argv[1]); + } + + detector->init(); od::ODViewer viewer; viewer.initCVWindow(std::string("Faces")); @@ -34,7 +49,7 @@ int main(int argc, char * argv[]) { boost::shared_ptr<od::ODSceneImage> scene = frame_generator.getNextFrame(); - boost::shared_ptr<od::ODDetections2D> detections = detector.detectOmni(scene); + boost::shared_ptr<od::ODDetections2D> detections = detector->detectOmni(scene); if(detections->size() > 0) viewer.update(detections->renderMetainfo(scene), std::string("Faces")); diff --git a/gpu/detectors/CMakeLists.txt b/gpu/detectors/CMakeLists.txt new file mode 100644 index 00000000..45661e6d --- /dev/null +++ b/gpu/detectors/CMakeLists.txt @@ -0,0 +1,11 @@ +if(WITH_GPU) + set(GPU_DETECTORS_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + set(GPU_DETECTORS_INCLUDE_DIR ${GPU_DETECTORS_DIR}/include) + + set(GPU_DETECTORS_INCLUDE_DIR ${GPU_DETECTORS_DIR}/include PARENT_SCOPE) + + add_subdirectory("src/global2D") + + install(DIRECTORY ${GPU_DETECTORS_INCLUDE_DIR}/od DESTINATION ${OD_INSTALL_INCLUDE_DIR}) + set(${OD_INSTALLED_LIBRARIES} PARENT_SCOPE) +endif() diff --git a/gpu/detectors/include/od/gpu/detectors/global2D/detection/ODCascadeDetector.h b/gpu/detectors/include/od/gpu/detectors/global2D/detection/ODCascadeDetector.h new file mode 100644 index 00000000..6296a0e8 --- /dev/null +++ b/gpu/detectors/include/od/gpu/detectors/global2D/detection/ODCascadeDetector.h @@ -0,0 +1,107 @@ +/* +Copyright (c) 2015, Kripasindhu Sarkar +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions 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. + * Neither the name of the copyright holder(s) nor the + names of its contributors may 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 COPYRIGHT HOLDER 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. +*/// +// Created by sarkar on 17.07.15. +// +#pragma once +#include "od/common/pipeline/ODDetector.h" +#include "od/common/pipeline/ODScene.h" +#include "od/common/utils/ODUtils.h" +#include "od/common/utils/ODFeatureDetector2D.h" + +#include <opencv2/cudaobjdetect.hpp> +#include <opencv2/cudaimgproc.hpp> +#include <opencv2/cudawarping.hpp> + +#include <opencv2/opencv.hpp> + +namespace od +{ + + namespace gpu + { + namespace g2d + { + /** \brief A class for detection using Cascade classifiers. + * Given a scene and a cascade classifier, this class performs a classification and returns detections. The training is not supported in OD currently but is compatible to the cascade training of OpenCV. + * Train your cascade classifiers using OpenCV's *opencv_traincascade* utility (http://docs.opencv.org/master/dc/d88/tutorial_traincascade.html#gsc.tab=0). It is a great tool for traning your cascade. + * Paste the generated xml in trained_data_location_/TD_CASCADE/\*.cascade.xml to use your trained cascade. + * + * \author Kripasindhu Sarkar + * + */ + + class ODCascadeDetector : public ODDetector2D + { + public: + + ODCascadeDetector(const std::string & trainer = std::string("cascade.xml"), const std::string & trained_data_location = std::string(""), double scale_factor = 1.1, int min_neighbors = 3, + int flags = 0, const cv::Size & min_size = cv::Size(), const cv::Size & max_size = cv::Size()); + + void init(); + + shared_ptr<ODDetections2D> detectOmni(shared_ptr<ODSceneImage> scene); + shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene); + + shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene); + + void setScale(const float scale); + float getScale() const; + + void setMinNeighbors(const unsigned int mn); + unsigned int getMinNeighbors() const; + + void setMinSize(const cv::Size & size); + cv::Size getMinSize() const; + + void setMaxSize(const cv::Size & size); + cv::Size getMaxSize() const; + + void setFindLargest(bool largest); + bool getFindLargest() const; + + private: + + cv::Ptr<cv::cuda::CascadeClassifier> haar_cascade_; + cv::cuda::GpuMat gray_, buf_, color_; + + std::vector<cv::Rect_<int> > objects_; + + double scale_factor_; + int min_neighbors_; + cv::Size min_size_, max_size_; + std::string trained_data_id_; + bool meta_info_, largest_; + + }; + /** \examples objectdetector/od_image_cascade.cpp + * \examples objectdetector/od_image_cascade_files.cpp + */ + } + + } + +} diff --git a/gpu/detectors/src/global2D/CMakeLists.txt b/gpu/detectors/src/global2D/CMakeLists.txt new file mode 100644 index 00000000..cd70727b --- /dev/null +++ b/gpu/detectors/src/global2D/CMakeLists.txt @@ -0,0 +1,20 @@ +set(SUBSYS_NAME gpu_global_image_detector) +set(LIB_NAME od_${SUBSYS_NAME}) +set(SUBSYS_DESC "global gpu feature based detection in 2D images") + +set(SUBSYS_DEPS od_common) + +set(build TRUE) + +if(build) + + set(SOURCES + "detection/ODCascadeDetector.cpp" + ) + + include_directories(${GPU_DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + + OD_ADD_LIBRARY("${SUBSYS_NAME}" SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) + +endif(build) \ No newline at end of file diff --git a/gpu/detectors/src/global2D/detection/ODCascadeDetector.cpp b/gpu/detectors/src/global2D/detection/ODCascadeDetector.cpp new file mode 100644 index 00000000..3acedf90 --- /dev/null +++ b/gpu/detectors/src/global2D/detection/ODCascadeDetector.cpp @@ -0,0 +1,184 @@ +/* +Copyright (c) 2015, Kripasindhu Sarkar +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions 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. + * Neither the name of the copyright holder(s) nor the + names of its contributors may 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 COPYRIGHT HOLDER 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. +*/// +// Created by sarkar on 17.07.15. +// + +#include "od/gpu/detectors/global2D/detection/ODCascadeDetector.h" + +namespace od +{ + + namespace gpu + { + + namespace g2d + { + + ODCascadeDetector::ODCascadeDetector(const std::string & trainer, const std::string & trained_data_location, double scale_factor, int min_neighbors, int flags, + const cv::Size & min_size, const cv::Size & max_size): + ODDetector2D(trained_data_location), scale_factor_(scale_factor), min_neighbors_(min_neighbors), + min_size_(min_size), max_size_(max_size), trained_data_id_(trainer), meta_info_(true), largest_(true) + {} + + shared_ptr<ODDetections> ODCascadeDetector::detectOmni(shared_ptr<ODScene> scene) + { + std::cout << "not implemented, use detect()" <<std::endl; + return nullptr; + } + + void ODCascadeDetector::init() + { + haar_cascade_ = cv::cuda::CascadeClassifier::create(fileutils::getFirstFile(getSpecificTrainingDataLocation(), trained_data_id_)); + } + + shared_ptr<ODDetections2D> ODCascadeDetector::detectOmni(shared_ptr<ODSceneImage> scene) + { + if(!haar_cascade_){ + std::cout << "Call init() first!" << std::endl; + return nullptr; + } + + cv::Mat gray; + + cv::Mat input = scene->getCVImage(); + objects_.clear(); + + color_.upload(input); + cv::cuda::cvtColor(color_, gray_, CV_BGR2GRAY); + + haar_cascade_->setFindLargestObject(largest_); + haar_cascade_->setMaxObjectSize(max_size_); + haar_cascade_->setMinNeighbors(min_neighbors_); + haar_cascade_->setMinObjectSize(min_size_); + + haar_cascade_->setScaleFactor(scale_factor_); + haar_cascade_->detectMultiScale(gray_, buf_); + haar_cascade_->convert(buf_, objects_); + + //always create detections + shared_ptr<ODDetections2D> detections = make_shared<ODDetections2D>(); + cv::Mat viz = scene->getCVImage().clone(); + for(auto & o : objects_) + { + // Process face by face: + shared_ptr<ODDetection2D> detection2D = make_shared<ODDetection2D>(ODDetection::OD_CLASSIFICATION, "OBJ", 1); + detection2D->setBoundingBox(o); + detections->push_back(detection2D); + + if(meta_info_) + { + cv::rectangle(viz, o, CV_RGB(0, 255, 0), 1); + } + } + detections->setMetainfoImage(viz); + + return detections; + } + + shared_ptr<ODDetections> ODCascadeDetector::detect(shared_ptr<ODSceneImage> scene) + { + + if(!haar_cascade_){ + std::cout << "Call init() first!" << std::endl; + return nullptr; + } + + cv::Mat gray; + + cv::Mat input = scene->getCVImage(); + objects_.clear(); + + color_.upload(input); + cv::cuda::cvtColor(color_, gray_, CV_BGR2GRAY); + + haar_cascade_->setFindLargestObject(largest_); + haar_cascade_->setMaxObjectSize(gray.size()); + haar_cascade_->setMinNeighbors(min_neighbors_); + haar_cascade_->setMinObjectSize(gray.size()); + + haar_cascade_->setScaleFactor(scale_factor_); + haar_cascade_->detectMultiScale(gray_, buf_); + haar_cascade_->convert(buf_, objects_); + + //always create detections + shared_ptr<ODDetections> detections = make_shared<ODDetections>(); + + //hack for single detection, + //note: maxsize = minsize = size of input image for single window detection + //todo: implement in some other way of fast single detection; currently this will work, but maynot be fast + if(objects_.size() > 0) + { + detections->push_back(make_shared<ODDetection>(ODDetection::OD_CLASSIFICATION, "OBJ", 1)); + } + return detections; + } + + void ODCascadeDetector::setScale(const float scale) + { + scale_factor_ = scale; + } + + float ODCascadeDetector::getScale() const + { + return scale_factor_; + } + + void ODCascadeDetector::setMinNeighbors(const unsigned int min_neighbors) + { + min_neighbors_ = min_neighbors; + } + + unsigned int ODCascadeDetector::getMinNeighbors() const + { + return min_neighbors_; + } + + void ODCascadeDetector::setMinSize(const cv::Size & size) + { + min_size_ = size; + } + + cv::Size ODCascadeDetector::getMinSize() const + { + return min_size_; + } + + void ODCascadeDetector::setMaxSize(const cv::Size & size) + { + max_size_ = size; + } + + cv::Size ODCascadeDetector::getMaxSize() const + { + return max_size_; + } + + } + + } + +} \ No newline at end of file From 9e48641df03a916cfa95ea459b5c53ff2e70db94 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Wed, 6 Jul 2016 09:55:00 +0200 Subject: [PATCH 41/79] fixes examble build cmake --- examples/apps/CMakeLists.txt | 57 +++-- examples/objectdetector/CMakeLists.txt | 221 +++++++++++++----- ...amegenerator.cpp => od_framegenerator.cpp} | 0 examples/objectdetector/od_pc_global.cpp | 1 - .../objectdetector/od_pc_global_files.cpp | 1 - .../objectdetector/od_pc_global_real_time.cpp | 1 - 6 files changed, 196 insertions(+), 85 deletions(-) rename examples/objectdetector/{od_example_framegenerator.cpp => od_framegenerator.cpp} (100%) diff --git a/examples/apps/CMakeLists.txt b/examples/apps/CMakeLists.txt index 08262193..2e64c974 100644 --- a/examples/apps/CMakeLists.txt +++ b/examples/apps/CMakeLists.txt @@ -1,26 +1,43 @@ -include_directories(${DETECTORS_INCLUDE_DIR}) -include_directories(${COMMON_INCLUDE_DIR}) -include_directories(${COMMON_IMPL_DIR}) -include_directories(${CMAKE_3RDPARTY_DIR}/pugixml/src/) -include_directories(${OD_BINARY_DIR}/generated) -include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) +option(cad_recog_test_single_model "Build the cad recognition test" ON) +if(cad_recog_test_single_model) -OD_ADD_EXAMPLE(odcadrecog_test_single_model - FILES cadrecog2D/od_test_single_db_single_model.cpp - LINK_WITH od_local_image_detector -) + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${CMAKE_3RDPARTY_DIR}/pugixml/src/) + OD_ADD_EXAMPLE(odcadrecog_test_single_model + FILES cadrecog2D/od_test_single_db_single_model.cpp + LINK_WITH od_local_image_detector + ) +endif() -set(SOURCE_FILES version/opendetection.cpp) -add_executable(opendetection ${SOURCE_FILES}) +################################################################################## +option(multihog_app "Build the multihog app" ON) +if(od_multihog_app) + + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + + OD_ADD_EXAMPLE(od_multihog_app + FILES global2D/od_multihog_app.cpp + LINK_WITH od_global_image_detector + ) +endif() + +################################################################################## +option(viewer_test "Build the viewer test" ON) +if(viewer_test) -OD_ADD_EXAMPLE(od_multihog_app - FILES global2D/od_multihog_app.cpp - LINK_WITH od_global_image_detector -) + include_directories(${COMMON_IMPL_DIR}) -OD_ADD_EXAMPLE(viewer_test - FILES visualization/ODVisualizationTest.cpp - LINK_WITH od_common -) + OD_ADD_EXAMPLE(viewer_test + FILES visualization/ODVisualizationTest.cpp + LINK_WITH od_common + ) +endif() + +################################################################################## +set(SOURCE_FILES version/opendetection.cpp) +include_directories(${OD_BINARY_DIR}/generated) +add_executable(opendetection ${SOURCE_FILES}) diff --git a/examples/objectdetector/CMakeLists.txt b/examples/objectdetector/CMakeLists.txt index 2ee5f6ba..1500a0ee 100644 --- a/examples/objectdetector/CMakeLists.txt +++ b/examples/objectdetector/CMakeLists.txt @@ -1,128 +1,225 @@ -include_directories(${DETECTORS_INCLUDE_DIR}) -if(WITH_GPU) - include_directories(${GPU_DETECTORS_INCLUDE_DIR}) -endif() -include_directories(${COMMON_INCLUDE_DIR}) -include_directories(${COMMON_IMPL_DIR}) -include_directories(${SIMPLE_RANSAC_INCLUDE_DIR}) -include_directories(${DETECTORS_IMPL_DIR}) -include_directories(${CMAKE_3RDPARTY_DIR}/pugixml/src/) -include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) - option(camera_recognition_example "Build the camera recognition example" ON) if(od_image_camera_example) - OD_ADD_EXAMPLE(od_image_camera - FILES od_image_cadrecog_camera.cpp - LINK_WITH od_local_image_detector) + + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) + + OD_ADD_EXAMPLE(od_image_camera + FILES od_image_cadrecog_camera.cpp + LINK_WITH od_local_image_detector) endif() +################################################################################## option(image_recognition_example "Build the image recognition example" ON) if(image_recognition_example) - OD_ADD_EXAMPLE(od_example_files - FILES od_image_cadrecog_files.cpp - LINK_WITH od_local_image_detector) + + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) + include_directories(${CMAKE_3RDPARTY_DIR}/pugixml/src/) + + OD_ADD_EXAMPLE(od_example_files + FILES od_image_cadrecog_files.cpp + LINK_WITH od_local_image_detector) endif() +################################################################################## option(hog_train_example "Build the hog train example" ON) if(hog_train_example) - OD_ADD_EXAMPLE(od_hog_train - FILES od_hog_train.cpp - LINK_WITH od_global_image_detector) + + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) + + OD_ADD_EXAMPLE(od_hog_train + FILES od_hog_train.cpp + LINK_WITH od_global_image_detector) endif() +################################################################################## option(image_hog_files_example "Build the hog image example" ON) if(image_hog_files_example) - OD_ADD_EXAMPLE(od_image_hog_files - FILES od_image_hog_files.cpp - LINK_WITH od_global_image_detector) + + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) + + OD_ADD_EXAMPLE(od_image_hog_files + FILES od_image_hog_files.cpp + LINK_WITH od_global_image_detector) endif() -option(face_detection_files_example "Build the face detection example" ON) +################################################################################## +option(face_detection_files_example "Build the face detection example with file input" ON) if(face_detection_files_example) - OD_ADD_EXAMPLE(od_face_detection_files - FILES od_face_detector_files.cpp - LINK_WITH od_global_image_detector od_gpu_global_image_detector) + + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) + + if(WITH_GPU) + include_directories(${GPU_DETECTORS_INCLUDE_DIR}) + endif() + + OD_ADD_EXAMPLE(od_face_detection_files + FILES od_face_detector_files.cpp + LINK_WITH od_global_image_detector + od_gpu_global_image_detector) endif() -option(face_detection_cam_example "Build the face detection example" ON) +################################################################################## +option(face_detection_cam_example "Build the face detection example with camera input" ON) if(face_detection_cam_example) - OD_ADD_EXAMPLE(od_face_detection_cam - FILES od_face_detector_cam.cpp - LINK_WITH od_global_image_detector od_gpu_global_image_detector) + + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) + + if(WITH_GPU) + include_directories(${GPU_DETECTORS_INCLUDE_DIR}) + endif() + + OD_ADD_EXAMPLE(od_face_detection_cam + FILES od_face_detector_cam.cpp + LINK_WITH od_global_image_detector + od_gpu_global_image_detector) endif() +################################################################################## option(image_customhog_example "Build the custom hog example" ON) if(image_customhog_example) - OD_ADD_EXAMPLE(od_image_customhog - FILES od_image_customhog.cpp - LINK_WITH od_global_image_detector) + + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) + + OD_ADD_EXAMPLE(od_image_customhog + FILES od_image_customhog.cpp + LINK_WITH od_global_image_detector) endif() +################################################################################## option(multialgo_files_example "Build the multialgo_files example" ON) if(multialgo_files_example) - OD_ADD_EXAMPLE(od_multialgo_files - FILES od_multialgo_files.cpp - LINK_WITH od_global_image_detector od_misc_detector) + + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${DETECTORS_IMPL_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) + + OD_ADD_EXAMPLE(od_multialgo_files + FILES od_multialgo_files.cpp + LINK_WITH od_global_image_detector + od_misc_detector) endif() +################################################################################## option(multialgo_pc_example "Build the multialgo_pc example" ON) if(multialgo_pc_example) - OD_ADD_EXAMPLE(od_multialgo_pc - FILES od_multialgo_pc.cpp - LINK_WITH od_global_image_detector od_misc_detector od_pointcloud_global_detector) + + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) + + OD_ADD_EXAMPLE(od_multialgo_pc + FILES od_multialgo_pc.cpp + LINK_WITH od_global_image_detector + od_misc_detector + od_pointcloud_global_detector) endif() +################################################################################## option(cascade_cam_example "Build the cascade camera example" ON) if(cascade_cam_example) - OD_ADD_EXAMPLE(od_cascade_cam - FILES od_cascade_cam.cpp - LINK_WITH od_global_image_detector) + + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) + + OD_ADD_EXAMPLE(od_cascade_cam + FILES od_cascade_cam.cpp + LINK_WITH od_global_image_detector) endif() +################################################################################## option(cascade_files_example "Build the cascade file example" ON) if(cascade_files_example) - OD_ADD_EXAMPLE(od_cascade_files - FILES od_cascade_files.cpp - LINK_WITH od_global_image_detector) + + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) + + OD_ADD_EXAMPLE(od_cascade_files + FILES od_cascade_files.cpp + LINK_WITH od_global_image_detector) endif() +################################################################################## option(image_facerecog_example "Build the face recognition example" ON) if(image_facerecog_example) - OD_ADD_EXAMPLE(od_image_facerecog - FILES od_image_facerecog.cpp - LINK_WITH od_global_image_detector) + + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) + + OD_ADD_EXAMPLE(od_image_facerecog + FILES od_image_facerecog.cpp + LINK_WITH od_global_image_detector) endif() +################################################################################## option(pc_global_example "Build the global detector example" ON) if(pc_global_example) - OD_ADD_EXAMPLE(od_example_pc_global - FILES od_pc_global.cpp - LINK_WITH od_pointcloud_global_detector) + + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${DETECTORS_IMPL_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + + OD_ADD_EXAMPLE(od_example_pc_global + FILES od_pc_global.cpp + LINK_WITH od_pointcloud_global_detector) endif() +################################################################################## option(pc_global_files_example "Build the global detector file example" ON) if(pc_global_files_example) - OD_ADD_EXAMPLE(od_example_pc_global_files - FILES od_pc_global_files.cpp - LINK_WITH od_pointcloud_global_detector) + + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${DETECTORS_IMPL_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + + OD_ADD_EXAMPLE(od_example_pc_global_files + FILES od_pc_global_files.cpp + LINK_WITH od_pointcloud_global_detector) endif() +################################################################################## option(pc_global_real_time_example "Build the global detector real time example" ON) if(pc_global_real_time_example) - OD_ADD_EXAMPLE(od_example_pc_global_real_time - FILES od_pc_global_real_time.cpp - LINK_WITH od_pointcloud_global_detector) + + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${DETECTORS_IMPL_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + + OD_ADD_EXAMPLE(od_example_pc_global_real_time + FILES od_pc_global_real_time.cpp + LINK_WITH od_pointcloud_global_detector) endif() +################################################################################## option(framegenerator_example "Build the frame generator example" ON) if(framegenerator_example) - OD_ADD_EXAMPLE(od_example_framegenerator - FILES od_example_framegenerator.cpp - LINK_WITH od_common) -endif() + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) + include_directories(${COMMON_IMPL_DIR}) + OD_ADD_EXAMPLE(od_framegenerator + FILES od_framegenerator.cpp + LINK_WITH od_common) +endif() diff --git a/examples/objectdetector/od_example_framegenerator.cpp b/examples/objectdetector/od_framegenerator.cpp similarity index 100% rename from examples/objectdetector/od_example_framegenerator.cpp rename to examples/objectdetector/od_framegenerator.cpp diff --git a/examples/objectdetector/od_pc_global.cpp b/examples/objectdetector/od_pc_global.cpp index 59087d6c..d07c1f3d 100644 --- a/examples/objectdetector/od_pc_global.cpp +++ b/examples/objectdetector/od_pc_global.cpp @@ -34,7 +34,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "od/detectors/global3D/ODPointCloudGlobalMatching.h" -#include "od/common/utils/ODViewer.h" #include <boost/shared_ptr.hpp> #include <iostream> diff --git a/examples/objectdetector/od_pc_global_files.cpp b/examples/objectdetector/od_pc_global_files.cpp index 4023f92b..55d29ab1 100644 --- a/examples/objectdetector/od_pc_global_files.cpp +++ b/examples/objectdetector/od_pc_global_files.cpp @@ -35,7 +35,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/common/utils/ODFrameGenerator.h" #include "od/detectors/global3D/ODPointCloudGlobalMatching.h" -#include "od/common/utils/ODViewer.h" #include <boost/shared_ptr.hpp> #include <vector> diff --git a/examples/objectdetector/od_pc_global_real_time.cpp b/examples/objectdetector/od_pc_global_real_time.cpp index 89bbdd5e..72093705 100644 --- a/examples/objectdetector/od_pc_global_real_time.cpp +++ b/examples/objectdetector/od_pc_global_real_time.cpp @@ -36,7 +36,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/global3D/ODPointCloudGlobalMatching.h" #include "od/common/utils/ODFrameGenerator.h" -#include "od/common/utils/ODViewer.h" #include <string> #include <boost/shared_ptr.hpp> From b796a7a7f866875f1a9b6d2819ebfcc2d793ec8c Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Mon, 11 Jul 2016 16:45:20 +0200 Subject: [PATCH 42/79] fixes the viewer with all functions to cover the examples, adds blog entry --- common/impl/od/common/utils/ODViewer.hpp | 35 +++++- common/include/od/common/utils/ODViewer.h | 22 ++++ common/src/pipeline/ODDetection.cpp | 2 +- common/src/utils/ODViewer.cpp | 117 ++++++++++++++++-- .../global2D/detection/ODCascadeDetector.cpp | 2 +- .../gsoc2016_blog_giacomo.md | 55 +++++++- .../objectdetector/od_face_detector_cam.cpp | 2 +- examples/objectdetector/od_multialgo_pc.cpp | 21 ++-- .../objectdetector/od_pc_global_real_time.cpp | 19 ++- .../global2D/detection/ODCascadeDetector.cpp | 2 +- 10 files changed, 237 insertions(+), 40 deletions(-) diff --git a/common/impl/od/common/utils/ODViewer.hpp b/common/impl/od/common/utils/ODViewer.hpp index 6ffa4e59..5840520b 100644 --- a/common/impl/od/common/utils/ODViewer.hpp +++ b/common/impl/od/common/utils/ODViewer.hpp @@ -15,12 +15,12 @@ namespace od { status_ = POINTCLOUD; - if(!viewer_ || pcl_window_name_ != cloud_name){ + if(!viewer_ ){ viewer_ = make_shared<pcl::visualization::PCLVisualizer>(cloud_name); viewer_->setBackgroundColor(0, 0, 0); } - pcl_window_name_ = cloud_name; + clouds_.push_back(cloud_name); if(colored) { @@ -31,6 +31,30 @@ namespace od { } } + template<typename PointT> + void ODViewer::render(shared_ptr<pcl::PointCloud<PointT> > to_display, + pcl::visualization::PointCloudColorHandlerRandom<PointT> & random_handler, const std::string & cloud_name) + { + if(status_ != POINTCLOUD){ + std::cout << "Switching viewer to PointCloud mode" << std::endl; + if(status_ == CVMAT){ + cv::destroyWindow(mat_window_name_.c_str()); + } + } + + status_ = POINTCLOUD; + + if(!viewer_ ){ + viewer_ = make_shared<pcl::visualization::PCLVisualizer>(cloud_name); + viewer_->setBackgroundColor(0, 0, 0); + } + + clouds_.push_back(cloud_name); + + viewer_->addPointCloud<PointT>(to_display, random_handler, cloud_name); + + } + template<typename PointT> void ODViewer::render(shared_ptr<ODScenePointCloud<PointT> > to_display, const std::string & cloud_name, bool colored) { @@ -51,9 +75,10 @@ namespace od { return; } - if(cloud_name != pcl_window_name_){ - std::cout << "No window " << cloud_name - << " present. Please first use render(shared_ptr<pcl::PointCloud<PointT> >, const std::string & ) to create the new window" + + if(!viewer_ || std::find(clouds_.begin(), clouds_.end(), cloud_name) != clouds_.end()){ + std::cout << "No cloud " << cloud_name + << " present. Please first use render(shared_ptr<pcl::PointCloud<PointT> >, const std::string & ) to add the cloud" << std::endl; return; } diff --git a/common/include/od/common/utils/ODViewer.h b/common/include/od/common/utils/ODViewer.h index a65eea1e..73ab7e9a 100644 --- a/common/include/od/common/utils/ODViewer.h +++ b/common/include/od/common/utils/ODViewer.h @@ -1,6 +1,7 @@ #pragma once #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> +#include <opencv2/imgproc/imgproc.hpp> #include <pcl/visualization/cloud_viewer.h> #include <pcl/point_cloud.h> @@ -45,6 +46,10 @@ namespace od { template<typename PointT> void render(const ODScenePointCloud<PointT> & to_display, const std::string & cloud_name, bool colored = true); + template<typename PointT> + void render(shared_ptr<pcl::PointCloud<PointT> > to_display, + pcl::visualization::PointCloudColorHandlerRandom<PointT> & random_handler, const std::string & cloud_name); + void render(const cv::Mat & to_display, const std::string & window_name); void render(shared_ptr<ODSceneImage> to_display, const std::string & window_name); void render(const ODSceneImage & to_display, const std::string & window_name); @@ -74,7 +79,16 @@ namespace od { void spin(); bool toStop(); + void addText(const std::string & text, pcl::PointXYZ pos, double textScale, cv::Scalar color); + void addText(const std::string & text, cv::Point3f pos, double textScale, cv::Scalar color); + + void removeText(const std::string & text); + void remove(const std::string & name); + void removeAll(); + + void removeShape(const std::string & text); + void removeAllShapes(); unsigned int wait(unsigned int time) const; @@ -83,6 +97,7 @@ namespace od { odViewType status_; shared_ptr<pcl::visualization::PCLVisualizer> viewer_; std::string mat_window_name_, pcl_window_name_; + std::vector<std::string> clouds_; shared_ptr<cv::Mat> mat_; }; @@ -91,6 +106,13 @@ namespace od { extern template void ODViewer::render<pcl::PointXYZRGB>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> >, const std::string &, bool); extern template void ODViewer::render<pcl::PointXYZRGBA>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA> >, const std::string &, bool); + extern template void ODViewer::render<pcl::PointXYZ>(shared_ptr<pcl::PointCloud<pcl::PointXYZ> > to_display, + pcl::visualization::PointCloudColorHandlerRandom<pcl::PointXYZ> & random_handler, const std::string & cloud_name); + extern template void ODViewer::render<pcl::PointXYZRGB>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> > to_display, + pcl::visualization::PointCloudColorHandlerRandom<pcl::PointXYZRGB> & random_handler, const std::string & cloud_name); + extern template void ODViewer::render<pcl::PointXYZRGBA>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA> > to_display, + pcl::visualization::PointCloudColorHandlerRandom<pcl::PointXYZRGBA> & random_handler, const std::string & cloud_name); + extern template void ODViewer::render<pcl::PointXYZ>(shared_ptr<ODScenePointCloud<pcl::PointXYZ> >, const std::string &, bool); extern template void ODViewer::render<pcl::PointXYZRGB>(shared_ptr<ODScenePointCloud<pcl::PointXYZRGB> >, const std::string &, bool); extern template void ODViewer::render<pcl::PointXYZRGBA>(shared_ptr<ODScenePointCloud<pcl::PointXYZRGBA> >, const std::string &, bool); diff --git a/common/src/pipeline/ODDetection.cpp b/common/src/pipeline/ODDetection.cpp index 496f16cc..95358c1e 100644 --- a/common/src/pipeline/ODDetection.cpp +++ b/common/src/pipeline/ODDetection.cpp @@ -224,7 +224,7 @@ namespace od color_map[detections_[i]->getId()] = CV_RGB(rand()%255, rand()%255, rand()%255); }*/ - cv::Mat image = input.getCVImage().clone(); + cv::Mat image = input.getCVImage(); for(size_t i = 0; i < detections_.size(); ++i) { shared_ptr<ODDetection2D> detection = dynamic_pointer_cast<ODDetection2D>(detections_[i]); diff --git a/common/src/utils/ODViewer.cpp b/common/src/utils/ODViewer.cpp index 5d1686be..977551bf 100644 --- a/common/src/utils/ODViewer.cpp +++ b/common/src/utils/ODViewer.cpp @@ -69,7 +69,7 @@ namespace od { cvNamedWindow(mat_window_name_.c_str(), CV_WINDOW_AUTOSIZE); } - void ODViewer::initPCLWindow(const std::string & cloud_name) + void ODViewer::initPCLWindow(const std::string & window_name) { if(status_ != POINTCLOUD){ std::cout << "Switching viewer to PointCloud mode" << std::endl; @@ -80,13 +80,13 @@ namespace od { status_ = POINTCLOUD; - if(!viewer_ || pcl_window_name_ != cloud_name) + if(!viewer_ || pcl_window_name_ != window_name) { - viewer_ = make_shared<pcl::visualization::PCLVisualizer>(cloud_name); + viewer_ = make_shared<pcl::visualization::PCLVisualizer>(window_name); viewer_->setBackgroundColor(0, 0, 0); } - pcl_window_name_ = cloud_name; + pcl_window_name_ = window_name; } void ODViewer::update(const cv::Mat & to_display, const std::string & window_name) @@ -176,14 +176,106 @@ namespace od { return cv::waitKey(time); } - void ODViewer::remove(const std::string & name) + void ODViewer::addText(const std::string & text, pcl::PointXYZ pos, double textScale, cv::Scalar color) { - if(status_ != POINTCLOUD){ - std::cout << "Viewer not in pointcloud mode! use render(shared_ptr<pcl::PointCloud<PointT>>) first!" << std::endl; - return; + if(status_ == CVMAT) + { + cv::putText(*mat_, text, cv::Point(pos.x, pos.y), cv::FONT_HERSHEY_SIMPLEX, textScale, color); + } + else if(status_ == POINTCLOUD) + { + viewer_->addText3D(text, pos, textScale, color[2], color[1], color[0]); + + } + } + + void ODViewer::addText(const std::string & text, cv::Point3f pos, double textScale, cv::Scalar color) + { + if(status_ == CVMAT) + { + cv::putText(*mat_, text, cv::Point(pos.x, pos.y), cv::FONT_HERSHEY_SIMPLEX, textScale, color); + } + else if(status_ == POINTCLOUD) + { + viewer_->addText3D(text, pcl::PointXYZ(pos.x, pos.y, pos.z), textScale, color[2], color[1], color[0]); + + } + } + + void ODViewer::remove(const std::string & text) + { + if(status_ == CVMAT) + { + if(text != mat_window_name_) + { + std::cout << "no window " << text << " to remove" << std::endl; + return; + } + cv::destroyWindow(mat_window_name_.c_str()); + mat_window_name_ = std::string(); + } + else if(status_ == POINTCLOUD) + { + if(std::remove(clouds_.begin(), clouds_.end(), text) == clouds_.end()) + { + std::cout << "no cloud " << text << " to remove" << std::endl; + return; + } + viewer_->removePointCloud(text); + } + } + + void ODViewer::removeAll() + { + if(status_ == CVMAT) + { + remove(mat_window_name_); + } + else if(status_ == POINTCLOUD) + { + viewer_->removeAllPointClouds(); + clouds_.clear(); + } + } + + void ODViewer::removeAllShapes() + { + if(status_ == CVMAT) + { + std::cout << "Not in pcl mode!" << std::endl; + } + else if(status_ == POINTCLOUD) + { + viewer_->removeAllShapes(); + } + + } + + + void ODViewer::removeText(const std::string & text) + { + if(status_ == CVMAT) + { + std::cout << "Not in pcl mode!" << std::endl; + } + else if(status_ == POINTCLOUD) + { + viewer_->removeText3D(text); + } + + } + + void ODViewer::removeShape(const std::string & text) + { + if(status_ == CVMAT) + { + std::cout << "Not in pcl mode!" << std::endl; + } + else if(status_ == POINTCLOUD) + { + viewer_->removeShape(text); } - viewer_->removePointCloud(name); } // Explicit template function instantiation @@ -191,6 +283,13 @@ namespace od { template void ODViewer::render<pcl::PointXYZRGB>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> >, const std::string &, bool); template void ODViewer::render<pcl::PointXYZRGBA>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA> >, const std::string &, bool); + template void ODViewer::render<pcl::PointXYZ>(shared_ptr<pcl::PointCloud<pcl::PointXYZ> > to_display, + pcl::visualization::PointCloudColorHandlerRandom<pcl::PointXYZ> & random_handler, const std::string & cloud_name); + template void ODViewer::render<pcl::PointXYZRGB>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> > to_display, + pcl::visualization::PointCloudColorHandlerRandom<pcl::PointXYZRGB> & random_handler, const std::string & cloud_name); + template void ODViewer::render<pcl::PointXYZRGBA>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA> > to_display, + pcl::visualization::PointCloudColorHandlerRandom<pcl::PointXYZRGBA> & random_handler, const std::string & cloud_name); + template void ODViewer::render<pcl::PointXYZ>(shared_ptr<ODScenePointCloud<pcl::PointXYZ> >, const std::string &, bool); template void ODViewer::render<pcl::PointXYZRGB>(shared_ptr<ODScenePointCloud<pcl::PointXYZRGB> >, const std::string &, bool); template void ODViewer::render<pcl::PointXYZRGBA>(shared_ptr<ODScenePointCloud<pcl::PointXYZRGBA> >, const std::string &, bool); diff --git a/detectors/src/global2D/detection/ODCascadeDetector.cpp b/detectors/src/global2D/detection/ODCascadeDetector.cpp index 96f6a20f..e5143cbe 100644 --- a/detectors/src/global2D/detection/ODCascadeDetector.cpp +++ b/detectors/src/global2D/detection/ODCascadeDetector.cpp @@ -53,7 +53,7 @@ namespace od void ODCascadeDetector::init() { - haar_cascade_ = make_shared<cv::CascadeClassifier>(fileutils::getFirstFile(getSpecificTrainingDataLocation(), trained_data_id_)); + haar_cascade_ = make_shared<cv::CascadeClassifier>(trained_data_id_); } shared_ptr<ODDetections2D> ODCascadeDetector::detectOmni(shared_ptr<ODSceneImage> scene) diff --git a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md index f9405a4b..3d381643 100644 --- a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md +++ b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md @@ -218,7 +218,7 @@ I then fixed all the linking in the whole library since there where some useless After that I tested compilation of the library under Windows, but without success. There have been several issues which are ut of the Open Deteciton library. I tested compilation using **Visual Studio 2015** and **MinGW**. Pcl has been installed by using the prebuilt version while opencv has to be built manually since *opencv_contrib* is necessary while not present in the prebuilt binaries. Building opencv3 with visual studio and cuda is not possible at the moment since cuda 7.5 is not compatible with visual studio 2015, so version 8 is necessary which will be available soon. MinGW is also not usable since compilation of opencv3 with cuda is disabled as flagged as incompatible by opencv. -##Face detection 27/06/15## +##Face detection 31/06/15## To test the new cmake and code structure I **implemented face detection** (not recognition which is already available). Face detection is implemented in *OpenCV* both in **CPU** mode and in **GPU** mode (using **CUDA**). @@ -229,4 +229,55 @@ To test the new detector I created two examples; the first one is *od_face_detec I noticed that it is necessary to pass to the *ODFrameGenerator* a file expression like /home/test/images/\*.jpg. I don't really like this method since it is quite restrictive and not intuitive to call. I will add also a function using **boost filesystem** to iterate over all files which are OpenCV supported images, using as input a path name. I added in the cmake the two new examples with a custom option and tested them successfully. -The next step will be to add the functionality previously described to the frame generator; I will also add the possibility to use standard containers as input sources in roder to be able to use arrays, vectors and lists for example. \ No newline at end of file +The next step will be to add the functionality previously described to the frame generator; I will also add the possibility to use standard containers as input sources in roder to be able to use arrays, vectors and lists for example. + +##Face detection 2 06/06/15## + +After talking with Kripa I found out that the CPU cascade detector (the detector used to detect faces) was already rpesent n the library; so only the gpu version had to be added. This implies to create in the library a new gpu folder containing all gpu modules which should be built only if support is present. + +To do this I created a separate gpu folder which contains for now a detector folder with the same structure as the external one. Everything defined here should be in the **od::gpu::** namespace and each class should have the same name as the cpu version class. It is also a good habit to first create a common interface for the **CPU** and **GPU** version in order to be able to dinamically allocate objects of both types. + +To grasp better the meaning I implemented the **cascade detector** as follows: + +Both detectors derive already from the same class so there is no need for an additional interface + +@code +class ODCascadeDetector : public ODDetector2D +@endcode + +Each class has a different detector member: + +GPU +@code +cv::Ptr<cv::cuda::CascadeClassifier> haar_cascade_; +@endcode + +CPU +@code +shared_ptr<cv::CascadeClassifier> haar_cascade_; +@endcode + +This way we can do the following when using the detectors: + +@code + + boost::shared_ptr<od::ODDetector2D> detector; + + if(gpu) + { + detector = boost::make_shared<od::gpu::g2d::ODCascadeDetector>(argv[1]); + }else{ + detector = boost::make_shared<od::g2d::ODCascadeDetector>(argv[1]); + } + +@endcode + +There were three possible solutions to how to impement gpu in the library: + +- Add in the constructor a flag to decide the **CPU** / **GPU** mode. This mixes up gpu and cpu code in the same class and changing anything becomes more and more difficult + +- Create two separate classes with dfferent names, one for each type. This fixes the previous issue but moving from one version to the other can be hard + +- Create a common interface and have the same class imlemented in different namespaces. Clean solution which has been adopted. + +I removed the previous facedetector classes which I had added and used the new ones in the examples which I had previoulsy created. I also used the new viewer in all examples and fixed the build of the different examples to be independent and clean in the *CMake*. \ No newline at end of file diff --git a/examples/objectdetector/od_face_detector_cam.cpp b/examples/objectdetector/od_face_detector_cam.cpp index 974a42a3..248a6652 100644 --- a/examples/objectdetector/od_face_detector_cam.cpp +++ b/examples/objectdetector/od_face_detector_cam.cpp @@ -47,7 +47,7 @@ int main(int argc, char * argv[]) detector->init(); - while(frame_generator->isValid() && viewer.wait(50) != 27) + while(frame_generator->isValid() && viewer.wait(100) != 27) { boost::shared_ptr<od::ODSceneImage> scene = frame_generator->getNextFrame(); diff --git a/examples/objectdetector/od_multialgo_pc.cpp b/examples/objectdetector/od_multialgo_pc.cpp index 8fb32b5a..59a62f2f 100644 --- a/examples/objectdetector/od_multialgo_pc.cpp +++ b/examples/objectdetector/od_multialgo_pc.cpp @@ -56,7 +56,8 @@ int main(int argc, char *argv[]) //GUI and feedback - pcl::visualization::PCLVisualizer vis ("kinect"); + od::ODViewer viewer; + viewer.initPCLWindow(std::string("kinect")); size_t previous_cluster_size = 0; od::ODFrameGenerator<od::ODScenePointCloud<pcl::PointXYZRGBA>, od::GENERATOR_TYPE_DEVICE> frameGenerator; @@ -68,17 +69,17 @@ int main(int argc, char *argv[]) boost::shared_ptr<od::ODScenePointCloud<pcl::PointXYZRGBA>> frame = frameGenerator.getNextFrame(); //remove previous point clouds and text and add new ones in the visualizer - vis.removePointCloud ("frame"); - vis.addPointCloud<pcl::PointXYZRGBA> (frame->getPointCloud(), "frame"); + viewer.remove("frame"); + viewer.render(frame->getPointCloud(), "frame"); for(size_t i = 0; i < previous_cluster_size; ++i) { cluster_name << "cluster_" << i; - vis.removePointCloud (cluster_name.str ()); - vis.removeText3D (cluster_name.str() + "_txt"); + viewer.remove(cluster_name.str ()); + viewer.removeText(cluster_name.str() + "_txt"); cluster_name.str(std::string()); cluster_name << "_ply_model_"; - vis.removeShape (cluster_name.str ()); + viewer.removeShape(cluster_name.str()); cluster_name.str(std::string()); } @@ -91,19 +92,19 @@ int main(int argc, char *argv[]) { cluster_name << "cluster_" << i; pcl::visualization::PointCloudColorHandlerRandom<pcl::PointXYZ> random_handler (detections->at(i)->getMetainfoCluster()); - vis.addPointCloud<pcl::PointXYZ> (detections->at(i)->getMetainfoCluster(), random_handler, cluster_name.str ()); + viewer.render(detections->at(i)->getMetainfoCluster(), random_handler, cluster_name.str()); pos.x = detections->at(i)->getLocation()[0]; pos.y = detections->at(i)->getLocation()[1]; pos.z = detections->at(i)->getLocation()[2]; - vis.addText3D (detections->at(i)->getId(), pos, 0.015f, 1, 0, 1, cluster_name.str() + "_txt", 0); + viewer.addText(detections->at(i)->getId(), pos, 0.015f, cv::Scalar(1, 0, 1)); cluster_name.str(std::string()); } - previous_cluster_size = detections->size (); + previous_cluster_size = detections->size(); - vis.spinOnce (); + viewer.spin(); } return 0; diff --git a/examples/objectdetector/od_pc_global_real_time.cpp b/examples/objectdetector/od_pc_global_real_time.cpp index 72093705..5e3f8806 100644 --- a/examples/objectdetector/od_pc_global_real_time.cpp +++ b/examples/objectdetector/od_pc_global_real_time.cpp @@ -36,6 +36,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/detectors/global3D/ODPointCloudGlobalMatching.h" #include "od/common/utils/ODFrameGenerator.h" +#include "od/common/utils/ODViewer.h" #include <string> #include <boost/shared_ptr.hpp> @@ -62,10 +63,10 @@ int main(int argc, char *argv[]) //GUI and feedback - pcl::visualization::PCLVisualizer vis ("kinect"); + od::ODViewer viewer; + viewer.initPCLWindow(std::string("kinect")); od::ODFrameGenerator<od::ODScenePointCloud<pcl::PointXYZRGBA>, od::GENERATOR_TYPE_DEVICE> frameGenerator; pcl::PointXYZ pos; - std::stringstream ss; while(frameGenerator.isValid()) { @@ -73,9 +74,9 @@ int main(int argc, char *argv[]) boost::shared_ptr<od::ODScenePointCloud<pcl::PointXYZRGBA>> frame = frameGenerator.getNextFrame(); //remove previous point clouds and text and add new ones in the visualizer - vis.removeAllPointClouds(); - vis.removeAllShapes(); - vis.addPointCloud<pcl::PointXYZRGBA> (frame->getPointCloud(), "frame"); + viewer.removeAll(); + viewer.removeAllShapes(); + viewer.render(frame->getPointCloud(), std::string("frame")); //Detect boost::shared_ptr<od::ODDetections3D> detections = detector.detectOmni(frame); @@ -84,17 +85,15 @@ int main(int argc, char *argv[]) for(size_t i = 0; i < detections->size(); ++i) { pcl::visualization::PointCloudColorHandlerRandom<pcl::PointXYZ> random_handler (detections->at(i)->getMetainfoCluster()); - vis.addPointCloud<pcl::PointXYZ> (detections->at(i)->getMetainfoCluster(), random_handler, "cluster_" + i); + viewer.render(detections->at(i)->getMetainfoCluster(), random_handler, std::string("cluster_") + std::to_string(i)); pos.x = detections->at(i)->getLocation()[0]; pos.y = detections->at(i)->getLocation()[1]; pos.z = detections->at(i)->getLocation()[2]; - ss << "cluster_" << i << "_txt"; - vis.addText3D(detections->at(i)->getId(), pos, 0.015f, 1, 0, 1, ss.str(), 0); - ss.str(std::string()); + viewer.addText(detections->at(i)->getId(), pos, 0.015f, cv::Scalar(1, 0, 1)); } - vis.spinOnce(); + viewer.spin(); } return 0; diff --git a/gpu/detectors/src/global2D/detection/ODCascadeDetector.cpp b/gpu/detectors/src/global2D/detection/ODCascadeDetector.cpp index 3acedf90..0f07c7da 100644 --- a/gpu/detectors/src/global2D/detection/ODCascadeDetector.cpp +++ b/gpu/detectors/src/global2D/detection/ODCascadeDetector.cpp @@ -52,7 +52,7 @@ namespace od void ODCascadeDetector::init() { - haar_cascade_ = cv::cuda::CascadeClassifier::create(fileutils::getFirstFile(getSpecificTrainingDataLocation(), trained_data_id_)); + haar_cascade_ = cv::cuda::CascadeClassifier::create(trained_data_id_); } shared_ptr<ODDetections2D> ODCascadeDetector::detectOmni(shared_ptr<ODSceneImage> scene) From effc982932de2f10405b91894a8fd13e5af46277 Mon Sep 17 00:00:00 2001 From: giacomo <giacomo.dabisias@gmail.com> Date: Tue, 12 Jul 2016 09:40:27 +0200 Subject: [PATCH 43/79] fixes build with gcc 5.3.1 avoiding adl errors --- cmake/ODDependency.cmake | 1 + common/CMakeLists.txt | 4 ++-- common/impl/od/common/utils/ODViewer.hpp | 8 ++++++++ .../include/od/common/utils/ODFrameGenerator.h | 4 ++++ common/src/utils/ODViewer.cpp | 4 ++++ .../detection/ODCADDetector3DGlobal.hpp | 3 ++- .../misc/detection/ODDetectorMultiAlgo.hpp | 17 ++++++++++++++--- .../training/ODCADRecogTrainerSnapshotBased.h | 1 + detectors/src/global2D/CMakeLists.txt | 2 +- detectors/src/global2D/ODFaceRecognizer.cpp | 2 +- .../global2D/detection/ODCascadeDetector.cpp | 4 ++++ .../detection/ODCADRecognizer2DLocal.cpp | 10 ++++++++++ detectors/src/misc/CMakeLists.txt | 1 + examples/objectdetector/CMakeLists.txt | 1 + 14 files changed, 54 insertions(+), 8 deletions(-) diff --git a/cmake/ODDependency.cmake b/cmake/ODDependency.cmake index e426e461..abccb5f5 100644 --- a/cmake/ODDependency.cmake +++ b/cmake/ODDependency.cmake @@ -3,5 +3,6 @@ find_package(OpenCV 3 REQUIRED) find_package(VTK REQUIRED) find_package(Eigen REQUIRED) find_package(Boost 1.40 COMPONENTS program_options REQUIRED) +find_package(Glew REQUIRED) include_directories("${OD_SOURCE_DIR}" ${EIGEN_INCLUDE_DIRS} ${OpenCV_INCLUDE_DIRS} ${PCL_INCLUDE_DIRS} ${OD_SOURCE_DIR}/3rdparty/SiftGPU/src/SiftGPU) \ No newline at end of file diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index cf2c0d1d..d2060366 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -1,7 +1,7 @@ set(SUBSYS_NAME common) set(LIB_NAME od_${SUBSYS_NAME}) set(SUBSYS_DESC "The core module of OpenDetection having the pipeline logic") -set(SUBSYS_DEPS ${OpenCV_LIBS} ${PCL_LIBRARIES}) +set(SUBSYS_DEPS ${OpenCV_LIBS} ${PCL_LIBRARIES} GLEW) set(COMMON_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) set(COMMON_IMPL_DIR ${CMAKE_CURRENT_SOURCE_DIR}/impl) @@ -14,7 +14,7 @@ if(WITH_GPU) set(SUBSYS_DEPS ${SUBSYS_DEPS} siftgpu) endif() -set(SUBSYS_DEPS ${SUBSYS_DEPS} svmlight) +set(SUBSYS_DEPS ${SUBSYS_DEPS} svm) set(build TRUE) diff --git a/common/impl/od/common/utils/ODViewer.hpp b/common/impl/od/common/utils/ODViewer.hpp index 5840520b..f8bf1bd3 100644 --- a/common/impl/od/common/utils/ODViewer.hpp +++ b/common/impl/od/common/utils/ODViewer.hpp @@ -16,7 +16,11 @@ namespace od { status_ = POINTCLOUD; if(!viewer_ ){ +#ifdef WITH_BOOST_SHARED_PTR + viewer_ = shared_ptr<pcl::visualization::PCLVisualizer>(new pcl::visualization::PCLVisualizer(cloud_name)); +#else viewer_ = make_shared<pcl::visualization::PCLVisualizer>(cloud_name); +#endif viewer_->setBackgroundColor(0, 0, 0); } @@ -45,7 +49,11 @@ namespace od { status_ = POINTCLOUD; if(!viewer_ ){ +#ifdef WITH_BOOST_SHARED_PTR + viewer_ = shared_ptr<pcl::visualization::PCLVisualizer>(new pcl::visualization::PCLVisualizer(cloud_name)); +#else viewer_ = make_shared<pcl::visualization::PCLVisualizer>(cloud_name); +#endif viewer_->setBackgroundColor(0, 0, 0); } diff --git a/common/include/od/common/utils/ODFrameGenerator.h b/common/include/od/common/utils/ODFrameGenerator.h index f5caa86e..9d0be708 100644 --- a/common/include/od/common/utils/ODFrameGenerator.h +++ b/common/include/od/common/utils/ODFrameGenerator.h @@ -71,7 +71,11 @@ namespace od exhausted_ = true; cout << "Frame: " << file_list_[curr_image_] << endl; +#ifdef WITH_BOOST_SHARED_PTR + return shared_ptr<SceneT>(new SceneT(file_list_[curr_image_])); +#else return make_shared<SceneT>(file_list_[curr_image_]); +#endif } bool isValid() diff --git a/common/src/utils/ODViewer.cpp b/common/src/utils/ODViewer.cpp index 977551bf..22dd10c1 100644 --- a/common/src/utils/ODViewer.cpp +++ b/common/src/utils/ODViewer.cpp @@ -82,7 +82,11 @@ namespace od { if(!viewer_ || pcl_window_name_ != window_name) { +#ifdef WITH_BOOST_SHARED_PTR + viewer_ = shared_ptr<pcl::visualization::PCLVisualizer>(new pcl::visualization::PCLVisualizer(window_name)); +#else viewer_ = make_shared<pcl::visualization::PCLVisualizer>(window_name); +#endif viewer_->setBackgroundColor(0, 0, 0); } diff --git a/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp b/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp index 29eddd8e..27d2bf95 100644 --- a/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp +++ b/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp @@ -240,7 +240,8 @@ namespace od //detection done! //now fill up the detection: - shared_ptr<ODDetection> detection = make_shared<ODDetection3D>(ODDetection::OD_CLASSIFICATION, categories[0], conf[0]); + shared_ptr<ODDetection> detection(new ODDetection(ODDetection::OD_CLASSIFICATION, categories[0], conf[0])); + detections->push_back(detection); return detections; diff --git a/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp b/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp index 2c32292f..b46445f5 100644 --- a/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp +++ b/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp @@ -102,8 +102,15 @@ namespace od { //make a list of different algorithms //vector<ODDetector *> detectors = {new ODCascadeDetector(trained_data_location_), new ODHOGDetector(trained_data_location_), new ODCADRecognizer2DLocal(trained_data_location_)}; - detectors_2d_.push_back(make_shared<g2d::ODCascadeDetector>(trained_data_location_)); - detectors_2d_.push_back(make_shared<g2d::ODHOGDetector>(trained_data_location_)); + +#ifdef WITH_BOOST_SHARED_PTR + detectors_2d_.push_back(shared_ptr<g2d::ODCascadeDetector>(new g2d::ODCascadeDetector(trained_data_location_))); + detectors_2d_.push_back(shared_ptr<g2d::ODHOGDetector>(new g2d::ODHOGDetector(trained_data_location_))); +#else + detectors_2d_.push_back(make_shared<g2d::ODCascadeDetector>(trained_data_location_)); + detectors_2d_.push_back(make_shared<g2d::ODHOGDetector>(trained_data_location_)); +#endif + // detectors.push_back(new ODCADRecognizer2DLocal(trained_data_location_)); for(auto & d : detectors_2d_) @@ -212,7 +219,11 @@ namespace od void ODDetectorMultiAlgo<PointT>::init() { //3D - detectors_3d_.push_back( make_shared<g3d::ODCADDetector3DGlobal<PointT> >(trained_data_location_, training_input_location_)); +#ifdef WITH_BOOST_SHARED_PTR + detectors_3d_.push_back(shared_ptr<g3d::ODCADDetector3DGlobal<PointT> >(new g3d::ODCADDetector3DGlobal<PointT>(trained_data_location_, training_input_location_))); +#else + detectors_3d_.push_back(make_shared<g3d::ODCADDetector3DGlobal<PointT> >(trained_data_location_, training_input_location_)); +#endif for(auto & d : detectors_3d_) { d->init(); diff --git a/detectors/include/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h b/detectors/include/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h index 377a0a41..9fbb2e18 100644 --- a/detectors/include/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h +++ b/detectors/include/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h @@ -47,6 +47,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <vtkImageReader2Factory.h> #include <vtkImageReader.h> #include <vtkWindowToImageFilter.h> +#include <vtkSmartPointer.h> #include <opencv2/xfeatures2d.hpp> diff --git a/detectors/src/global2D/CMakeLists.txt b/detectors/src/global2D/CMakeLists.txt index 4a4f09f8..3dc1e47f 100644 --- a/detectors/src/global2D/CMakeLists.txt +++ b/detectors/src/global2D/CMakeLists.txt @@ -2,7 +2,7 @@ set(SUBSYS_NAME global_image_detector) set(LIB_NAME od_${SUBSYS_NAME}) set(SUBSYS_DESC "global feature based detection in 2D images") -set(SUBSYS_DEPS od_common svmlight) +set(SUBSYS_DEPS od_common svm) set(build TRUE) diff --git a/detectors/src/global2D/ODFaceRecognizer.cpp b/detectors/src/global2D/ODFaceRecognizer.cpp index 4da16336..b9f3c60f 100644 --- a/detectors/src/global2D/ODFaceRecognizer.cpp +++ b/detectors/src/global2D/ODFaceRecognizer.cpp @@ -148,7 +148,7 @@ namespace od cv_recognizer_->predict(face_edited, label, confidence); //fill in the detection - shared_ptr<ODDetection2D> detection = make_shared<ODDetection2D>(ODDetection::OD_CLASSIFICATION, std::to_string(label), confidence); + shared_ptr<ODDetection2D> detection(new ODDetection2D(ODDetection::OD_CLASSIFICATION, std::to_string(label), confidence)); shared_ptr<ODDetections2D> detections = make_shared<ODDetections2D>(); detections->push_back(detection); diff --git a/detectors/src/global2D/detection/ODCascadeDetector.cpp b/detectors/src/global2D/detection/ODCascadeDetector.cpp index e5143cbe..b51e5336 100644 --- a/detectors/src/global2D/detection/ODCascadeDetector.cpp +++ b/detectors/src/global2D/detection/ODCascadeDetector.cpp @@ -53,7 +53,11 @@ namespace od void ODCascadeDetector::init() { +#ifdef WITH_BOOST_SHARED_PTR + haar_cascade_ = shared_ptr<cv::CascadeClassifier>(new cv::CascadeClassifier(trained_data_id_)); +#else haar_cascade_ = make_shared<cv::CascadeClassifier>(trained_data_id_); +#endif } shared_ptr<ODDetections2D> ODCascadeDetector::detectOmni(shared_ptr<ODSceneImage> scene) diff --git a/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp b/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp index 2a7103b6..fa6cb602 100644 --- a/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp +++ b/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp @@ -61,7 +61,13 @@ namespace od pnp_method_ = cv::SOLVEPNP_EPNP; f_type_default_ = std::string("SIFT"); + +#ifdef WITH_BOOST_SHARED_PTR + feature_detector_ = shared_ptr<ODFeatureDetector2D>(new ODFeatureDetector2D(f_type_default_, use_gpu_)); +#else feature_detector_ = make_shared<ODFeatureDetector2D>(f_type_default_, use_gpu_); +#endif + } const std::string & ODCADRecognizer2DLocal::getCameraIntrinsicFile() const @@ -252,7 +258,11 @@ namespace od if(models_.size() > 0) f_type_default_ = models_[0].f_type_; +#ifdef WITH_BOOST_SHARED_PTR + feature_detector_ = shared_ptr<ODFeatureDetector2D>(new ODFeatureDetector2D(f_type_default_, use_gpu_)); +#else feature_detector_ = make_shared<ODFeatureDetector2D>(f_type_default_, use_gpu_); +#endif } diff --git a/detectors/src/misc/CMakeLists.txt b/detectors/src/misc/CMakeLists.txt index 3939a36b..d5985f44 100644 --- a/detectors/src/misc/CMakeLists.txt +++ b/detectors/src/misc/CMakeLists.txt @@ -14,6 +14,7 @@ if(build) include_directories(${DETECTORS_IMPL_DIR}) include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) OD_ADD_LIBRARY("${SUBSYS_NAME}" SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) diff --git a/examples/objectdetector/CMakeLists.txt b/examples/objectdetector/CMakeLists.txt index 1500a0ee..2eb1be68 100644 --- a/examples/objectdetector/CMakeLists.txt +++ b/examples/objectdetector/CMakeLists.txt @@ -45,6 +45,7 @@ if(image_hog_files_example) include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) include_directories(${COMMON_IMPL_DIR}) + include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) OD_ADD_EXAMPLE(od_image_hog_files FILES od_image_hog_files.cpp From 5d19740b021c232576f1505d30e2c6869689075a Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Tue, 12 Jul 2016 13:56:53 +0200 Subject: [PATCH 44/79] adds some fixes --- 3rdparty/svmlightlib | 2 +- doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/3rdparty/svmlightlib b/3rdparty/svmlightlib index b1ff32b8..c8297570 160000 --- a/3rdparty/svmlightlib +++ b/3rdparty/svmlightlib @@ -1 +1 @@ -Subproject commit b1ff32b8cd68570c33ceee63e464a8773adb6413 +Subproject commit c82975709dc5237718e94d0859cbf737a13e4889 diff --git a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md index 3d381643..e1a5ca32 100644 --- a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md +++ b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md @@ -280,4 +280,12 @@ There were three possible solutions to how to impement gpu in the library: - Create a common interface and have the same class imlemented in different namespaces. Clean solution which has been adopted. -I removed the previous facedetector classes which I had added and used the new ones in the examples which I had previoulsy created. I also used the new viewer in all examples and fixed the build of the different examples to be independent and clean in the *CMake*. \ No newline at end of file +I removed the previous facedetector classes which I had added and used the new ones in the examples which I had previoulsy created. I also used the new viewer in all examples and fixed the build of the different examples to be independent and clean in the *CMake*. + + +##Visualizer update 11/06/15## + +To cover all examples and to have a more versatile viewer I added today an interface so add and remove text, clouds and added some improvments to be able to have multiple clouds in the same viewer at the same time. There is still some testing needed. I also added as usual all the template precompilation for the common PCL types. + +The next step consists in adding as stated before the std::containers to the frame generator class and then have a look if there are some modules to move to the gpu folder or maybe add some new methods there. + From a6732027a66197f89ceb7c120b2c24c931718ed3 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Tue, 12 Jul 2016 16:17:21 +0200 Subject: [PATCH 45/79] creates separate buildable modules and fixes cmake accordingly --- CMakeLists.txt | 6 +- cmake/ODDependency.cmake | 4 +- common/CMakeLists.txt | 39 ++- detectors/CMakeLists.txt | 1 - detectors/src/global2D/CMakeLists.txt | 7 +- detectors/src/global3D/CMakeLists.txt | 13 +- detectors/src/local2D/CMakeLists.txt | 7 +- detectors/src/misc/CMakeLists.txt | 27 +- examples/apps/CMakeLists.txt | 49 ++-- examples/objectdetector/CMakeLists.txt | 323 +++++++++++++--------- gpu/detectors/src/global2D/CMakeLists.txt | 1 + 11 files changed, 274 insertions(+), 203 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 222b1974..0e36a3b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,9 +37,13 @@ configure_file(${OD_CMAKE_DIR}/ODConfig.cmake.in ${OD_BINARY_DIR}/FindOD.cmake) # Optional parameters option(WITH_DOCUMENTATION "Build the OD documentation" ON) -option(WITH_GPU "Build GPU components of the project" ON) +option(WITH_GPU "Build GPU modules" ON) option(WITH_EXAMPLES "Build examples" OFF) option(WITH_BOOST_SHARED_PTR "use boost::shared_ptr instead of std::shared_ptr" ON) +option(BUILD_GLOBAL_2D_DETECTION "build global 2D detection" ON) +option(BUILD_GLOBAL_3D_DETECTION "build global 3D detection" ON) +option(BUILD_LOCAL_2D_DETECTION "build local 2D detection" ON) +option(BUILD_MISC_DETECTION "build miscellaneous detection" ON) if(WITH_BOOST_SHARED_PTR) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWITH_BOOST_SHARED_PTR") diff --git a/cmake/ODDependency.cmake b/cmake/ODDependency.cmake index abccb5f5..4ced772c 100644 --- a/cmake/ODDependency.cmake +++ b/cmake/ODDependency.cmake @@ -3,6 +3,4 @@ find_package(OpenCV 3 REQUIRED) find_package(VTK REQUIRED) find_package(Eigen REQUIRED) find_package(Boost 1.40 COMPONENTS program_options REQUIRED) -find_package(Glew REQUIRED) - -include_directories("${OD_SOURCE_DIR}" ${EIGEN_INCLUDE_DIRS} ${OpenCV_INCLUDE_DIRS} ${PCL_INCLUDE_DIRS} ${OD_SOURCE_DIR}/3rdparty/SiftGPU/src/SiftGPU) \ No newline at end of file +find_package(Glew REQUIRED) \ No newline at end of file diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index d2060366..1546f906 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -9,33 +9,28 @@ set(COMMON_IMPL_DIR ${CMAKE_CURRENT_SOURCE_DIR}/impl) set(COMMON_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include PARENT_SCOPE) set(COMMON_IMPL_DIR ${CMAKE_CURRENT_SOURCE_DIR}/impl PARENT_SCOPE) - if(WITH_GPU) set(SUBSYS_DEPS ${SUBSYS_DEPS} siftgpu) endif() -set(SUBSYS_DEPS ${SUBSYS_DEPS} svm) - -set(build TRUE) - -if(build) +set(SUBSYS_DEPS ${SUBSYS_DEPS} svmlight) - set(SOURCES - "src/pipeline/ODObjectDetector.cpp" - "src/pipeline/ODScene.cpp" - "src/pipeline/ODDetection.cpp" - "src/utils/ODUtils.cpp" - "src/utils/ODFeatureDetector2D.cpp" - "src/bindings/ODSvmlight.cpp" - "src/utils/ODViewer.cpp" - ) +set(SOURCES + "src/pipeline/ODObjectDetector.cpp" + "src/pipeline/ODScene.cpp" + "src/pipeline/ODDetection.cpp" + "src/utils/ODUtils.cpp" + "src/utils/ODFeatureDetector2D.cpp" + "src/bindings/ODSvmlight.cpp" + "src/utils/ODViewer.cpp" + ) - include_directories(${COMMON_INCLUDE_DIR}) - include_directories(${COMMON_IMPL_DIR}) - include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) +include_directories(${COMMON_INCLUDE_DIR}) +include_directories(${COMMON_IMPL_DIR}) +include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) +include_directories(${PCL_INCLUDE_DIRS}) - OD_ADD_LIBRARY(${SUBSYS_NAME} SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) - install(DIRECTORY ${COMMON_INCLUDE_DIR}/od DESTINATION ${OD_INSTALL_INCLUDE_DIR} COMPONENT ${LIB_NAME}) - install(DIRECTORY ${COMMON_IMPL_DIR}/od DESTINATION ${OD_INSTALL_INCLUDE_DIR} COMPONENT ${LIB_NAME}) +OD_ADD_LIBRARY(${SUBSYS_NAME} SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) +install(DIRECTORY ${COMMON_INCLUDE_DIR}/od DESTINATION ${OD_INSTALL_INCLUDE_DIR} COMPONENT ${LIB_NAME}) +install(DIRECTORY ${COMMON_IMPL_DIR}/od DESTINATION ${OD_INSTALL_INCLUDE_DIR} COMPONENT ${LIB_NAME}) -endif(build) \ No newline at end of file diff --git a/detectors/CMakeLists.txt b/detectors/CMakeLists.txt index 17e19fed..8dac82c8 100644 --- a/detectors/CMakeLists.txt +++ b/detectors/CMakeLists.txt @@ -11,6 +11,5 @@ add_subdirectory("src/global2D") add_subdirectory("src/global3D") add_subdirectory("src/misc") - install(DIRECTORY ${DETECTORS_INCLUDE_DIR}/od DESTINATION ${OD_INSTALL_INCLUDE_DIR}) set(${OD_INSTALLED_LIBRARIES} PARENT_SCOPE) diff --git a/detectors/src/global2D/CMakeLists.txt b/detectors/src/global2D/CMakeLists.txt index 3dc1e47f..00a5af12 100644 --- a/detectors/src/global2D/CMakeLists.txt +++ b/detectors/src/global2D/CMakeLists.txt @@ -4,9 +4,7 @@ set(SUBSYS_DESC "global feature based detection in 2D images") set(SUBSYS_DEPS od_common svm) -set(build TRUE) - -if(build) +if(BUILD_GLOBAL_2D_DETECTION) set(SOURCES "ODFaceRecognizer.cpp" @@ -18,7 +16,8 @@ if(build) include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) + include_directories(${PCL_INCLUDE_DIRS}) OD_ADD_LIBRARY("${SUBSYS_NAME}" SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) -endif(build) \ No newline at end of file +endif(BUILD_GLOBAL_2D_DETECTION) \ No newline at end of file diff --git a/detectors/src/global3D/CMakeLists.txt b/detectors/src/global3D/CMakeLists.txt index c169aacb..73db4bb3 100644 --- a/detectors/src/global3D/CMakeLists.txt +++ b/detectors/src/global3D/CMakeLists.txt @@ -3,20 +3,19 @@ set(LIB_NAME od_${SUBSYS_NAME}) set(SUBSYS_DESC "The global detector for point clouds") set(SUBSYS_DEPS od_common siftgpu) -set(build TRUE) - -if(build) +if(BUILD_GLOBAL_3D_DETECTION) set(SOURCES - "ODPointCloudGlobalMatching.cpp" - "training/ODCADDetectTrainer3DGlobal.cpp" - "detection/ODCADDetector3DGlobal.cpp" + "ODPointCloudGlobalMatching.cpp" + "training/ODCADDetectTrainer3DGlobal.cpp" + "detection/ODCADDetector3DGlobal.cpp" ) include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${DETECTORS_IMPL_DIR}) include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${PCL_INCLUDE_DIRS}) OD_ADD_LIBRARY("${SUBSYS_NAME}" SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) -endif(build) \ No newline at end of file +endif(BUILD_GLOBAL_3D_DETECTION) \ No newline at end of file diff --git a/detectors/src/local2D/CMakeLists.txt b/detectors/src/local2D/CMakeLists.txt index 2d8d036c..74b7d819 100644 --- a/detectors/src/local2D/CMakeLists.txt +++ b/detectors/src/local2D/CMakeLists.txt @@ -3,9 +3,7 @@ set(LIB_NAME od_${SUBSYS_NAME}) set(SUBSYS_DESC "The local feature matching based detector") set(SUBSYS_DEPS od_common pugixml) -set(build TRUE) - -if(build) +if(BUILD_LOCAL_2D_DETECTION) set(SOURCES "ODImageLocalMatching.cpp" @@ -24,7 +22,8 @@ if(build) include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${CMAKE_3RDPARTY_DIR}/pugixml/src/) include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${PCL_INCLUDE_DIRS}) OD_ADD_LIBRARY("${SUBSYS_NAME}" SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) -endif(build) \ No newline at end of file +endif(BUILD_LOCAL_2D_DETECTION) \ No newline at end of file diff --git a/detectors/src/misc/CMakeLists.txt b/detectors/src/misc/CMakeLists.txt index d5985f44..840c377a 100644 --- a/detectors/src/misc/CMakeLists.txt +++ b/detectors/src/misc/CMakeLists.txt @@ -3,19 +3,22 @@ set(LIB_NAME od_${SUBSYS_NAME}) set(SUBSYS_DESC "Multi algorithm detector") set(SUBSYS_DEPS od_common od_global_image_detector od_pointcloud_global_detector) -set(build TRUE) +if(BUILD_MISC_DETECTION) + if(TARGET od_global_image_detector AND TARGET od_pointcloud_global_detector) + set(SOURCES + "detection/ODDetectorMultiAlgo.cpp" + ) -if(build) - - set(SOURCES - "detection/ODDetectorMultiAlgo.cpp" - ) + include_directories(${DETECTORS_IMPL_DIR}) + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) + include_directories(${PCL_INCLUDE_DIRS}) - include_directories(${DETECTORS_IMPL_DIR}) - include_directories(${DETECTORS_INCLUDE_DIR}) - include_directories(${COMMON_INCLUDE_DIR}) - include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) + OD_ADD_LIBRARY("${SUBSYS_NAME}" SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) - OD_ADD_LIBRARY("${SUBSYS_NAME}" SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) + else() + message("!!! BUILD_MISC_DETECTION is set to on but BUILD_GLOBAL_2D_DETECTION and BUILD_GLOBAL_3D_DETECTION is necessary to build BUILD_MISC_DETECTION") + endif(TARGET od_global_image_detector AND TARGET od_pointcloud_global_detector) -endif(build) \ No newline at end of file +endif(BUILD_MISC_DETECTION) \ No newline at end of file diff --git a/examples/apps/CMakeLists.txt b/examples/apps/CMakeLists.txt index 2e64c974..8678034e 100644 --- a/examples/apps/CMakeLists.txt +++ b/examples/apps/CMakeLists.txt @@ -1,41 +1,52 @@ option(cad_recog_test_single_model "Build the cad recognition test" ON) if(cad_recog_test_single_model) - - include_directories(${DETECTORS_INCLUDE_DIR}) - include_directories(${COMMON_INCLUDE_DIR}) - include_directories(${CMAKE_3RDPARTY_DIR}/pugixml/src/) - - OD_ADD_EXAMPLE(odcadrecog_test_single_model - FILES cadrecog2D/od_test_single_db_single_model.cpp - LINK_WITH od_local_image_detector - ) -endif() + if(TARGET od_local_image_detector) + + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${CMAKE_3RDPARTY_DIR}/pugixml/src/) + include_directories(${PCL_INCLUDE_DIRS}) + + OD_ADD_EXAMPLE(odcadrecog_test_single_model + FILES cadrecog2D/od_test_single_db_single_model.cpp + LINK_WITH od_local_image_detector + ) + else() + message("!!! cad_recog_test_single_model is set to on but BUILD_GLOBAL_2D_DETECTION is necessary to build cad_recog_test_single_model") + endif(TARGET od_local_image_detector) +endif(cad_recog_test_single_model) ################################################################################## option(multihog_app "Build the multihog app" ON) if(od_multihog_app) - - include_directories(${DETECTORS_INCLUDE_DIR}) - include_directories(${COMMON_INCLUDE_DIR}) + if(TARGET od_global_image_detector) - OD_ADD_EXAMPLE(od_multihog_app - FILES global2D/od_multihog_app.cpp - LINK_WITH od_global_image_detector - ) -endif() + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + + OD_ADD_EXAMPLE(od_multihog_app + FILES global2D/od_multihog_app.cpp + LINK_WITH od_global_image_detector + ) + else() + message("!!! od_multihog_app is set to on but BUILD_GLOBAL_2D_DETECTION is necessary to build od_multihog_app") + endif(TARGET od_global_image_detector) +endif(od_multihog_app) ################################################################################## option(viewer_test "Build the viewer test" ON) if(viewer_test) include_directories(${COMMON_IMPL_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${PCL_INCLUDE_DIRS}) OD_ADD_EXAMPLE(viewer_test FILES visualization/ODVisualizationTest.cpp LINK_WITH od_common ) -endif() +endif(viewer_test) ################################################################################## set(SOURCE_FILES version/opendetection.cpp) diff --git a/examples/objectdetector/CMakeLists.txt b/examples/objectdetector/CMakeLists.txt index 2eb1be68..55883421 100644 --- a/examples/objectdetector/CMakeLists.txt +++ b/examples/objectdetector/CMakeLists.txt @@ -1,213 +1,276 @@ option(camera_recognition_example "Build the camera recognition example" ON) if(od_image_camera_example) - - include_directories(${DETECTORS_INCLUDE_DIR}) - include_directories(${COMMON_INCLUDE_DIR}) - include_directories(${COMMON_IMPL_DIR}) - - OD_ADD_EXAMPLE(od_image_camera - FILES od_image_cadrecog_camera.cpp - LINK_WITH od_local_image_detector) -endif() + if(TARGET od_local_image_detector) + + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) + + OD_ADD_EXAMPLE(od_image_camera + FILES od_image_cadrecog_camera.cpp + LINK_WITH od_local_image_detector) + else() + message("!!! od_image_camera_example is set to on but BUILD_LOCAL_2D_DETECTION is necessary to build od_image_camera_example") + endif(TARGET od_local_image_detector) +endif(od_image_camera_example) ################################################################################## option(image_recognition_example "Build the image recognition example" ON) if(image_recognition_example) + if(TARGET od_local_image_detector) - include_directories(${DETECTORS_INCLUDE_DIR}) - include_directories(${COMMON_INCLUDE_DIR}) - include_directories(${COMMON_IMPL_DIR}) - include_directories(${CMAKE_3RDPARTY_DIR}/pugixml/src/) + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) + include_directories(${CMAKE_3RDPARTY_DIR}/pugixml/src/) - OD_ADD_EXAMPLE(od_example_files - FILES od_image_cadrecog_files.cpp - LINK_WITH od_local_image_detector) -endif() + OD_ADD_EXAMPLE(od_example_files + FILES od_image_cadrecog_files.cpp + LINK_WITH od_local_image_detector) + else() + message("!!! image_recognition_example is set to on but BUILD_LOCAL_2D_DETECTION is necessary to build image_recognition_example") + endif(TARGET od_local_image_detector) +endif(image_recognition_example) ################################################################################## option(hog_train_example "Build the hog train example" ON) if(hog_train_example) - - include_directories(${DETECTORS_INCLUDE_DIR}) - include_directories(${COMMON_INCLUDE_DIR}) - include_directories(${COMMON_IMPL_DIR}) + if(TARGET od_global_image_detector) - OD_ADD_EXAMPLE(od_hog_train - FILES od_hog_train.cpp - LINK_WITH od_global_image_detector) -endif() + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) + + OD_ADD_EXAMPLE(od_hog_train + FILES od_hog_train.cpp + LINK_WITH od_global_image_detector) + else() + message("!!! hog_train_example is set to on but BUILD_GLOBAL_2D_DETECTION is necessary to build hog_train_example") + endif(TARGET od_global_image_detector) +endif(hog_train_example) ################################################################################## option(image_hog_files_example "Build the hog image example" ON) if(image_hog_files_example) + if(TARGET od_global_image_detector) - include_directories(${DETECTORS_INCLUDE_DIR}) - include_directories(${COMMON_INCLUDE_DIR}) - include_directories(${COMMON_IMPL_DIR}) - include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) + include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) - OD_ADD_EXAMPLE(od_image_hog_files - FILES od_image_hog_files.cpp - LINK_WITH od_global_image_detector) -endif() + OD_ADD_EXAMPLE(od_image_hog_files + FILES od_image_hog_files.cpp + LINK_WITH od_global_image_detector) + else() + message("!!! image_hog_files_example is set to on but BUILD_GLOBAL_2D_DETECTION is necessary to build image_hog_files_example") + endif(TARGET od_global_image_detector) +endif(image_hog_files_example) ################################################################################## option(face_detection_files_example "Build the face detection example with file input" ON) if(face_detection_files_example) - - include_directories(${DETECTORS_INCLUDE_DIR}) - include_directories(${COMMON_INCLUDE_DIR}) - include_directories(${COMMON_IMPL_DIR}) + if(TARGET od_global_image_detector) - if(WITH_GPU) - include_directories(${GPU_DETECTORS_INCLUDE_DIR}) - endif() + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) - OD_ADD_EXAMPLE(od_face_detection_files - FILES od_face_detector_files.cpp - LINK_WITH od_global_image_detector - od_gpu_global_image_detector) -endif() + set(SYS_DEP od_global_image_detector) + + if(WITH_GPU) + include_directories(${GPU_DETECTORS_INCLUDE_DIR}) + set(SYS_DEP ${SYS_DEP} od_gpu_global_image_detector) + endif() + + OD_ADD_EXAMPLE(od_face_detection_files + FILES od_face_detector_files.cpp + LINK_WITH ${SYS_DEP}) + else() + message("!!! face_detection_files_example is set to on but BUILD_GLOBAL_2D_DETECTION is necessary to build face_detection_files_example") + endif(TARGET od_global_image_detector) +endif(face_detection_files_example) ################################################################################## option(face_detection_cam_example "Build the face detection example with camera input" ON) if(face_detection_cam_example) + if(TARGET od_global_image_detector) - include_directories(${DETECTORS_INCLUDE_DIR}) - include_directories(${COMMON_INCLUDE_DIR}) - include_directories(${COMMON_IMPL_DIR}) + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) - if(WITH_GPU) - include_directories(${GPU_DETECTORS_INCLUDE_DIR}) - endif() + set(SYS_DEP od_global_image_detector) - OD_ADD_EXAMPLE(od_face_detection_cam - FILES od_face_detector_cam.cpp - LINK_WITH od_global_image_detector - od_gpu_global_image_detector) -endif() + if(WITH_GPU) + include_directories(${GPU_DETECTORS_INCLUDE_DIR}) + set(SYS_DEP ${SYS_DEP} od_gpu_global_image_detector) + endif() + + OD_ADD_EXAMPLE(od_face_detection_cam + FILES od_face_detector_cam.cpp + LINK_WITH ${SYS_DEP}) + else() + message("!!! face_detection_cam_example is set to on but BUILD_GLOBAL_2D_DETECTION is necessary to build face_detection_cam_example") + endif(TARGET od_global_image_detector) +endif(face_detection_cam_example) ################################################################################## option(image_customhog_example "Build the custom hog example" ON) if(image_customhog_example) + if(TARGET od_global_image_detector) - include_directories(${DETECTORS_INCLUDE_DIR}) - include_directories(${COMMON_INCLUDE_DIR}) - include_directories(${COMMON_IMPL_DIR}) + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) - OD_ADD_EXAMPLE(od_image_customhog - FILES od_image_customhog.cpp - LINK_WITH od_global_image_detector) -endif() + OD_ADD_EXAMPLE(od_image_customhog + FILES od_image_customhog.cpp + LINK_WITH od_global_image_detector) + else() + message("!!! image_customhog_example is set to on but BUILD_GLOBAL_2D_DETECTION is necessary to build image_customhog_example") + endif(TARGET od_global_image_detector) +endif(image_customhog_example) ################################################################################## option(multialgo_files_example "Build the multialgo_files example" ON) if(multialgo_files_example) - - include_directories(${DETECTORS_INCLUDE_DIR}) - include_directories(${DETECTORS_IMPL_DIR}) - include_directories(${COMMON_INCLUDE_DIR}) - include_directories(${COMMON_IMPL_DIR}) - - OD_ADD_EXAMPLE(od_multialgo_files - FILES od_multialgo_files.cpp - LINK_WITH od_global_image_detector - od_misc_detector) -endif() + if(TARGET od_misc_detector) + + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${DETECTORS_IMPL_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) + include_directories(${OpenCV_INCLUDE_DIRS}) + + OD_ADD_EXAMPLE(od_multialgo_files + FILES od_multialgo_files.cpp + LINK_WITH od_misc_detector) + else() + message("!!! multialgo_files_example is set to on but BUILD_MISC_DETECTION is necessary to build multialgo_files_example") + endif(TARGET od_misc_detector) +endif(multialgo_files_example) ################################################################################## option(multialgo_pc_example "Build the multialgo_pc example" ON) if(multialgo_pc_example) + if(TARGET od_misc_detector) - include_directories(${DETECTORS_INCLUDE_DIR}) - include_directories(${COMMON_INCLUDE_DIR}) - include_directories(${COMMON_IMPL_DIR}) + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) - OD_ADD_EXAMPLE(od_multialgo_pc - FILES od_multialgo_pc.cpp - LINK_WITH od_global_image_detector - od_misc_detector - od_pointcloud_global_detector) -endif() + OD_ADD_EXAMPLE(od_multialgo_pc + FILES od_multialgo_pc.cpp + LINK_WITH od_misc_detector) + else() + message("!!! multialgo_pc_example is set to on but BUILD_MISC_DETECTION is necessary to build multialgo_pc_example") + endif(TARGET od_misc_detector) +endif(multialgo_pc_example) ################################################################################## option(cascade_cam_example "Build the cascade camera example" ON) if(cascade_cam_example) - - include_directories(${DETECTORS_INCLUDE_DIR}) - include_directories(${COMMON_INCLUDE_DIR}) - include_directories(${COMMON_IMPL_DIR}) - - OD_ADD_EXAMPLE(od_cascade_cam - FILES od_cascade_cam.cpp - LINK_WITH od_global_image_detector) -endif() + if(TARGET od_global_image_detector) + + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) + include_directories(${PCL_INCLUDE_DIRS}) + + OD_ADD_EXAMPLE(od_cascade_cam + FILES od_cascade_cam.cpp + LINK_WITH od_global_image_detector) + else() + message("!!! cascade_cam_example is set to on but BUILD_GLOBAL_2D_DETECTION is necessary to build cascade_cam_example") + endif(TARGET od_global_image_detector) +endif(cascade_cam_example) ################################################################################## option(cascade_files_example "Build the cascade file example" ON) if(cascade_files_example) - - include_directories(${DETECTORS_INCLUDE_DIR}) - include_directories(${COMMON_INCLUDE_DIR}) - include_directories(${COMMON_IMPL_DIR}) + if(TARGET od_global_image_detector) - OD_ADD_EXAMPLE(od_cascade_files - FILES od_cascade_files.cpp - LINK_WITH od_global_image_detector) -endif() + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) + + OD_ADD_EXAMPLE(od_cascade_files + FILES od_cascade_files.cpp + LINK_WITH od_global_image_detector) + else() + message("!!! cascade_files_example is set to on but BUILD_GLOBAL_2D_DETECTION is necessary to build cascade_files_example") + endif(TARGET od_global_image_detector) +endif(cascade_files_example) ################################################################################## option(image_facerecog_example "Build the face recognition example" ON) if(image_facerecog_example) + if(TARGET od_global_image_detector) - include_directories(${DETECTORS_INCLUDE_DIR}) - include_directories(${COMMON_INCLUDE_DIR}) - include_directories(${COMMON_IMPL_DIR}) + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) - OD_ADD_EXAMPLE(od_image_facerecog - FILES od_image_facerecog.cpp - LINK_WITH od_global_image_detector) -endif() + OD_ADD_EXAMPLE(od_image_facerecog + FILES od_image_facerecog.cpp + LINK_WITH od_global_image_detector) + else() + message("!!! image_facerecog_example is set to on but BUILD_GLOBAL_2D_DETECTION is necessary to build image_facerecog_example") + endif(TARGET od_global_image_detector) +endif(image_facerecog_example) ################################################################################## option(pc_global_example "Build the global detector example" ON) if(pc_global_example) + if(TARGET od_pointcloud_global_detector) - include_directories(${DETECTORS_INCLUDE_DIR}) - include_directories(${DETECTORS_IMPL_DIR}) - include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${DETECTORS_IMPL_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) - OD_ADD_EXAMPLE(od_example_pc_global - FILES od_pc_global.cpp - LINK_WITH od_pointcloud_global_detector) -endif() + OD_ADD_EXAMPLE(od_example_pc_global + FILES od_pc_global.cpp + LINK_WITH od_pointcloud_global_detector) + else() + message("!!! pc_global_example is set to on but BUILD_GLOBAL_3D_DETECTION is necessary to build pc_global_example") + endif(TARGET od_pointcloud_global_detector) +endif(pc_global_example) ################################################################################## option(pc_global_files_example "Build the global detector file example" ON) if(pc_global_files_example) + if(TARGET od_pointcloud_global_detector) - include_directories(${DETECTORS_INCLUDE_DIR}) - include_directories(${DETECTORS_IMPL_DIR}) - include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${DETECTORS_IMPL_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) - OD_ADD_EXAMPLE(od_example_pc_global_files - FILES od_pc_global_files.cpp - LINK_WITH od_pointcloud_global_detector) -endif() + OD_ADD_EXAMPLE(od_example_pc_global_files + FILES od_pc_global_files.cpp + LINK_WITH od_pointcloud_global_detector) + else() + message("!!! pc_global_files_example is set to on but BUILD_GLOBAL_3D_DETECTION is necessary to build pc_global_files_example") + endif(TARGET od_pointcloud_global_detector) +endif(pc_global_files_example) ################################################################################## option(pc_global_real_time_example "Build the global detector real time example" ON) if(pc_global_real_time_example) - - include_directories(${DETECTORS_INCLUDE_DIR}) - include_directories(${DETECTORS_IMPL_DIR}) - include_directories(${COMMON_INCLUDE_DIR}) + if(TARGET od_pointcloud_global_detector) - OD_ADD_EXAMPLE(od_example_pc_global_real_time - FILES od_pc_global_real_time.cpp - LINK_WITH od_pointcloud_global_detector) -endif() + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${DETECTORS_IMPL_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + + OD_ADD_EXAMPLE(od_example_pc_global_real_time + FILES od_pc_global_real_time.cpp + LINK_WITH od_pointcloud_global_detector) + else() + message("!!! pc_global_real_time_example is set to on but BUILD_GLOBAL_3D_DETECTION is necessary to build pc_global_real_time_example") + endif(TARGET od_pointcloud_global_detector) +endif(pc_global_real_time_example) ################################################################################## option(framegenerator_example "Build the frame generator example" ON) @@ -215,7 +278,7 @@ if(framegenerator_example) include_directories(${COMMON_INCLUDE_DIR}) include_directories(${COMMON_IMPL_DIR}) - include_directories(${COMMON_IMPL_DIR}) + include_directories(${PCL_INCLUDE_DIRS}) OD_ADD_EXAMPLE(od_framegenerator FILES od_framegenerator.cpp diff --git a/gpu/detectors/src/global2D/CMakeLists.txt b/gpu/detectors/src/global2D/CMakeLists.txt index cd70727b..c10e763c 100644 --- a/gpu/detectors/src/global2D/CMakeLists.txt +++ b/gpu/detectors/src/global2D/CMakeLists.txt @@ -14,6 +14,7 @@ if(build) include_directories(${GPU_DETECTORS_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${PCL_INCLUDE_DIRS}) OD_ADD_LIBRARY("${SUBSYS_NAME}" SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) From 453dd248fda059cce19a9b30a1c947c42b421dbb Mon Sep 17 00:00:00 2001 From: giacomo <giacomo.dabisias@gmail.com> Date: Fri, 15 Jul 2016 16:13:02 +0200 Subject: [PATCH 46/79] adds siftgpu.h dependencies --- common/CMakeLists.txt | 1 + detectors/CMakeLists.txt | 2 + detectors/src/global2D/CMakeLists.txt | 2 +- detectors/src/misc/CMakeLists.txt | 1 + .../gsoc2016_blog_giacomo.md | 41 ++++++++++++++++++- examples/apps/CMakeLists.txt | 1 + examples/objectdetector/CMakeLists.txt | 2 + gpu/detectors/CMakeLists.txt | 2 + 8 files changed, 49 insertions(+), 3 deletions(-) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 1546f906..e09a27b6 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -28,6 +28,7 @@ set(SOURCES include_directories(${COMMON_INCLUDE_DIR}) include_directories(${COMMON_IMPL_DIR}) include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) +include_directories(${CMAKE_3RDPARTY_DIR}/SiftGPU/src/SiftGPU/) include_directories(${PCL_INCLUDE_DIRS}) OD_ADD_LIBRARY(${SUBSYS_NAME} SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) diff --git a/detectors/CMakeLists.txt b/detectors/CMakeLists.txt index 8dac82c8..15a98b94 100644 --- a/detectors/CMakeLists.txt +++ b/detectors/CMakeLists.txt @@ -6,6 +6,8 @@ set(DETECTORS_IMPL_DIR ${DETECTORS_DIR}/impl) set(DETECTORS_INCLUDE_DIR ${DETECTORS_DIR}/include PARENT_SCOPE) set(DETECTORS_IMPL_DIR ${DETECTORS_DIR}/impl PARENT_SCOPE) +include_directories(${CMAKE_3RDPARTY_DIR}/SiftGPU/src/SiftGPU/) + add_subdirectory("src/local2D") add_subdirectory("src/global2D") add_subdirectory("src/global3D") diff --git a/detectors/src/global2D/CMakeLists.txt b/detectors/src/global2D/CMakeLists.txt index 00a5af12..0b10a07e 100644 --- a/detectors/src/global2D/CMakeLists.txt +++ b/detectors/src/global2D/CMakeLists.txt @@ -2,7 +2,7 @@ set(SUBSYS_NAME global_image_detector) set(LIB_NAME od_${SUBSYS_NAME}) set(SUBSYS_DESC "global feature based detection in 2D images") -set(SUBSYS_DEPS od_common svm) +set(SUBSYS_DEPS od_common svmlight) if(BUILD_GLOBAL_2D_DETECTION) diff --git a/detectors/src/misc/CMakeLists.txt b/detectors/src/misc/CMakeLists.txt index 840c377a..21382de2 100644 --- a/detectors/src/misc/CMakeLists.txt +++ b/detectors/src/misc/CMakeLists.txt @@ -13,6 +13,7 @@ if(BUILD_MISC_DETECTION) include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) + include_directories(${PCL_INCLUDE_DIRS}) OD_ADD_LIBRARY("${SUBSYS_NAME}" SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) diff --git a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md index e1a5ca32..b3a971f8 100644 --- a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md +++ b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md @@ -231,7 +231,7 @@ I added in the cmake the two new examples with a custom option and tested them s The next step will be to add the functionality previously described to the frame generator; I will also add the possibility to use standard containers as input sources in roder to be able to use arrays, vectors and lists for example. -##Face detection 2 06/06/15## +##Face detection 2 06/07/15## After talking with Kripa I found out that the CPU cascade detector (the detector used to detect faces) was already rpesent n the library; so only the gpu version had to be added. This implies to create in the library a new gpu folder containing all gpu modules which should be built only if support is present. @@ -283,9 +283,46 @@ There were three possible solutions to how to impement gpu in the library: I removed the previous facedetector classes which I had added and used the new ones in the examples which I had previoulsy created. I also used the new viewer in all examples and fixed the build of the different examples to be independent and clean in the *CMake*. -##Visualizer update 11/06/15## +##Visualizer update 11/07/15## To cover all examples and to have a more versatile viewer I added today an interface so add and remove text, clouds and added some improvments to be able to have multiple clouds in the same viewer at the same time. There is still some testing needed. I also added as usual all the template precompilation for the common PCL types. The next step consists in adding as stated before the std::containers to the frame generator class and then have a look if there are some modules to move to the gpu folder or maybe add some new methods there. +##Modular build 15/07/15## + +Looking at the *PCL* and *OpenCV* libraries I noticed how the libraries are subdivided into modules. I understood then the importance of having this structure also in the OD library and so started to change a bit the structure, expecially the *Cmake* structure, to support such a feature. +With the new changes now each detector is a separate module which can be built or not as also gpu. One issue was that some modules might have dependencies on other modules and can be built only if a specific module was built. For example the misc detector can be only built if other detectors where built. + +To overcome this issue I found that you can specify targets in **IF** statments in *CMake*. This allowas to check if a specific module was built and in case continue with the build process. It the specific module was not built, an error message is displayed explaining which module is missing. The if statment needs the **TARGET** statment in fron of the moduel in order to search in the built targets. An example is the following: + +@code + +if(BUILD_MISC_DETECTION) + if(TARGET od_global_image_detector AND TARGET od_pointcloud_global_detector) + set(SOURCES + "detection/ODDetectorMultiAlgo.cpp" + ) + + include_directories(${DETECTORS_IMPL_DIR}) + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) + include_directories(${PCL_INCLUDE_DIRS}) + + OD_ADD_LIBRARY("${SUBSYS_NAME}" SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) + + else() + message("!!! BUILD_MISC_DETECTION is set to on but BUILD_GLOBAL_2D_DETECTION and BUILD_GLOBAL_3D_DETECTION is necessary to build BUILD_MISC_DETECTION") + endif(TARGET od_global_image_detector AND TARGET od_pointcloud_global_detector) + +@endcode + +As you can see we need to have the global_image_detector and the pointcloud_global_detector in order to build the misc module, along with the option to build the misc module. If the **IF** statment fails an error message is displayed asking to build those missing modules. +The same structure has been adde to all examples so that the correct examples are automatically built depending on the built modules. it is important to notice the repeated **include_directories** for each example. Its true that one is enough for all the built targets in that file, but with this modular built its not possible to know in advance which examples will be built. Also it is not nice to include everything at the beginning since this will only slow down compilation and make the code less readable since it covers up dependencies of example->includes. + +I also built the system on lniux 16.04 with gcc 5.3; I had some initial issues but also those were fixed. I will make a blogpost about them next week since some errors where quite interesting. + + + + diff --git a/examples/apps/CMakeLists.txt b/examples/apps/CMakeLists.txt index 8678034e..f6dd9993 100644 --- a/examples/apps/CMakeLists.txt +++ b/examples/apps/CMakeLists.txt @@ -6,6 +6,7 @@ if(cad_recog_test_single_model) include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) include_directories(${CMAKE_3RDPARTY_DIR}/pugixml/src/) + include_directories(${CMAKE_3RDPARTY_DIR}/SiftGPU/src/SiftGPU/) include_directories(${PCL_INCLUDE_DIRS}) OD_ADD_EXAMPLE(odcadrecog_test_single_model diff --git a/examples/objectdetector/CMakeLists.txt b/examples/objectdetector/CMakeLists.txt index 55883421..a071fc8e 100644 --- a/examples/objectdetector/CMakeLists.txt +++ b/examples/objectdetector/CMakeLists.txt @@ -1,3 +1,5 @@ +include_directories(${CMAKE_3RDPARTY_DIR}/SiftGPU/src/SiftGPU/) + option(camera_recognition_example "Build the camera recognition example" ON) if(od_image_camera_example) diff --git a/gpu/detectors/CMakeLists.txt b/gpu/detectors/CMakeLists.txt index 45661e6d..3ee26013 100644 --- a/gpu/detectors/CMakeLists.txt +++ b/gpu/detectors/CMakeLists.txt @@ -4,6 +4,8 @@ if(WITH_GPU) set(GPU_DETECTORS_INCLUDE_DIR ${GPU_DETECTORS_DIR}/include PARENT_SCOPE) + include_directories(${CMAKE_3RDPARTY_DIR}/SiftGPU/src/SiftGPU/) + add_subdirectory("src/global2D") install(DIRECTORY ${GPU_DETECTORS_INCLUDE_DIR}/od DESTINATION ${OD_INSTALL_INCLUDE_DIR}) From 7480100f779624e965073851a69c53cbb54ec837 Mon Sep 17 00:00:00 2001 From: giacomo <giacomo.dabisias@gmail.com> Date: Wed, 20 Jul 2016 16:10:56 +0200 Subject: [PATCH 47/79] adds gpu feature detectors as separate gpu module --- CMakeLists.txt | 3 +- common/CMakeLists.txt | 6 +- .../od/common/utils/ODFeatureDetector.h | 30 +++ .../od/common/utils/ODFeatureDetector2D.h | 28 +-- .../common/utils/ODFeatureDetectorInterface.h | 36 ++++ common/include/od/common/utils/ODViewer.h | 2 + common/src/utils/ODFeatureDetector.cpp | 46 +++++ common/src/utils/ODFeatureDetector2D.cpp | 169 +--------------- .../src/utils/ODFeatureDetectorInterface.cpp | 13 ++ common/src/utils/ODViewer.cpp | 8 +- .../detection/ODCADRecognizer2DLocal.h | 5 +- .../simple_ransac_detection/ODRobustMatcher.h | 1 + .../detection/ODCADRecognizer2DLocal.cpp | 12 +- gpu/common/CMakeLists.txt | 23 +++ .../od/gpu/common/utils/ODFeatureDetector2D.h | 43 +++++ gpu/common/src/utils/ODFeatureDetector2D.cpp | 182 ++++++++++++++++++ gpu/detectors/src/global2D/CMakeLists.txt | 18 +- 17 files changed, 416 insertions(+), 209 deletions(-) create mode 100644 common/include/od/common/utils/ODFeatureDetector.h create mode 100644 common/include/od/common/utils/ODFeatureDetectorInterface.h create mode 100644 common/src/utils/ODFeatureDetector.cpp create mode 100644 common/src/utils/ODFeatureDetectorInterface.cpp create mode 100644 gpu/common/CMakeLists.txt create mode 100644 gpu/common/include/od/gpu/common/utils/ODFeatureDetector2D.h create mode 100644 gpu/common/src/utils/ODFeatureDetector2D.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 0e36a3b9..40d902f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,10 +51,11 @@ endif() if(WITH_GPU) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWITH_GPU") + include_directories(GPU_COMMON_INCLUDE_DIRS ${OD_SOURCE_DIR}/gpu/common/include) endif() # Set modules -set(OD_MODULES_NAMES 3rdparty common detectors "gpu/detectors" doc examples ) +set(OD_MODULES_NAMES 3rdparty common "gpu/common" detectors "gpu/detectors" doc examples) set(OD_MODULES_DIRS ${OD_MODULES_NAMES}) # Add modules diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index e09a27b6..dcfbf3fc 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -10,7 +10,8 @@ set(COMMON_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include PARENT_SCOPE) set(COMMON_IMPL_DIR ${CMAKE_CURRENT_SOURCE_DIR}/impl PARENT_SCOPE) if(WITH_GPU) - set(SUBSYS_DEPS ${SUBSYS_DEPS} siftgpu) + set(SUBSYS_DEPS ${SUBSYS_DEPS} od_gpu_common) + include_directories(${GPU_COMMON_INCLUDE_DIR}) endif() set(SUBSYS_DEPS ${SUBSYS_DEPS} svmlight) @@ -23,12 +24,13 @@ set(SOURCES "src/utils/ODFeatureDetector2D.cpp" "src/bindings/ODSvmlight.cpp" "src/utils/ODViewer.cpp" + "src/utils/ODFeatureDetector.cpp" + "src/utils/ODFeatureDetectorInterface.cpp" ) include_directories(${COMMON_INCLUDE_DIR}) include_directories(${COMMON_IMPL_DIR}) include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) -include_directories(${CMAKE_3RDPARTY_DIR}/SiftGPU/src/SiftGPU/) include_directories(${PCL_INCLUDE_DIRS}) OD_ADD_LIBRARY(${SUBSYS_NAME} SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) diff --git a/common/include/od/common/utils/ODFeatureDetector.h b/common/include/od/common/utils/ODFeatureDetector.h new file mode 100644 index 00000000..5c280de4 --- /dev/null +++ b/common/include/od/common/utils/ODFeatureDetector.h @@ -0,0 +1,30 @@ +#pragma once +#include "od/common/utils/ODShared_pointers.h" +#include "od/common/utils/ODFeatureDetectorInterface.h" +#include "od/common/utils/ODFeatureDetector2D.h" + + + +namespace od +{ + + class ODFeatureDetector + { + + public: + + ODFeatureDetector(FeatureType type); + + void computeKeypointsAndDescriptors(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints); + + void computeAndSave(const cv::Mat & image, const std::string & path); + + private: + + FeatureType mode_; + bool gpu_; + shared_ptr<ODFeatureDetectorIterface> feature_detector_; + + }; + +} \ No newline at end of file diff --git a/common/include/od/common/utils/ODFeatureDetector2D.h b/common/include/od/common/utils/ODFeatureDetector2D.h index 24e3dfdc..f64ae69e 100644 --- a/common/include/od/common/utils/ODFeatureDetector2D.h +++ b/common/include/od/common/utils/ODFeatureDetector2D.h @@ -2,47 +2,23 @@ // Created by sarkar on 20.04.15. // #pragma once -#include <iostream> -#include <opencv2/core/core.hpp> -#include <opencv2/highgui/highgui.hpp> -#include <opencv2/features2d/features2d.hpp> +#include "od/common/utils/ODFeatureDetectorInterface.h" #include <opencv2/xfeatures2d.hpp> -#include <opencv2/cudafeatures2d.hpp> -#include <GL/gl.h> -#include "SiftGPU.h" - namespace od { - enum FeatureType - { - SIFT, SURF, ORB, SIFT_GPU, ORB_GPU, - }; - - class ODFeatureDetector2D + class ODFeatureDetector2D: public ODFeatureDetectorIterface { public: - ODFeatureDetector2D(const std::string & feature_type, bool use_gpu); ODFeatureDetector2D(FeatureType type); void computeKeypointsAndDescriptors(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints); - void findSiftGPUDescriptors(const std::string & image_name, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints); - - void findSiftGPUDescriptors(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints); - void computeAndSave(const cv::Mat & image, const std::string & path); - private: - - void findSiftGPUDescriptors_(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints); - - cv::Ptr<cv::FeatureDetector> feature_detector_; - cv::Ptr<SiftGPU> sift_gpu_; - int mode_; }; } diff --git a/common/include/od/common/utils/ODFeatureDetectorInterface.h b/common/include/od/common/utils/ODFeatureDetectorInterface.h new file mode 100644 index 00000000..7eeda355 --- /dev/null +++ b/common/include/od/common/utils/ODFeatureDetectorInterface.h @@ -0,0 +1,36 @@ +#pragma once +#include <iostream> +#include <opencv2/core/core.hpp> +#include <opencv2/highgui/highgui.hpp> +#include <opencv2/features2d/features2d.hpp> +#include <map> + +namespace od +{ + + enum FeatureType + { + SIFT, SURF, ORB, SIFT_GPU, ORB_GPU + }; + + extern std::map<std::string, FeatureType> od_enum_map; + + FeatureType string2FeatureType(const std::string & name); + + class ODFeatureDetectorIterface + { + + public: + + virtual void computeKeypointsAndDescriptors(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) = 0; + + virtual void computeAndSave(const cv::Mat & image, const std::string & path) = 0; + + protected: + + FeatureType mode_; + cv::Ptr<cv::FeatureDetector> feature_detector_; + + }; + +} \ No newline at end of file diff --git a/common/include/od/common/utils/ODViewer.h b/common/include/od/common/utils/ODViewer.h index 73ab7e9a..7a53b280 100644 --- a/common/include/od/common/utils/ODViewer.h +++ b/common/include/od/common/utils/ODViewer.h @@ -92,6 +92,8 @@ namespace od { unsigned int wait(unsigned int time) const; + shared_ptr<pcl::visualization::PCLVisualizer> getViewer(); + private: odViewType status_; diff --git a/common/src/utils/ODFeatureDetector.cpp b/common/src/utils/ODFeatureDetector.cpp new file mode 100644 index 00000000..d5197264 --- /dev/null +++ b/common/src/utils/ODFeatureDetector.cpp @@ -0,0 +1,46 @@ +#include "od/common/utils/ODFeatureDetector.h" + +namespace od +{ + + ODFeatureDetector::ODFeatureDetector(FeatureType type) + { + + mode_ = type; + + switch(mode_) + { + case(SIFT) : + case(ORB) : + case(SURF) : + feature_detector_ = make_shared<ODFeatureDetector2D>(mode_); + gpu_ = false; + break; + case(SIFT_GPU): + case(ORB_GPU): +#if WIHT_GPU + feature_detector_ = make_shared<od::gpu::ODFeatureDetector2D>(mode_); + gpu_ = true; +#else + std::cout << "FATAL ERROR, gpu type is not compile. Recompile with WITH_GPU to enable GPU support." << std::endl; + exit(-1); +#endif + default : + std::cout << "FATAL ERROR, type is not defined" << std::endl; + exit(-1); + break; + + } + } + + void ODFeatureDetector::computeKeypointsAndDescriptors(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) + { + feature_detector_->computeKeypointsAndDescriptors(image, descriptors,keypoints); + } + + void ODFeatureDetector::computeAndSave(const cv::Mat & image, const std::string & path) + { + feature_detector_->computeAndSave(image, path); + } + +} \ No newline at end of file diff --git a/common/src/utils/ODFeatureDetector2D.cpp b/common/src/utils/ODFeatureDetector2D.cpp index 3753ea79..cb3bedec 100644 --- a/common/src/utils/ODFeatureDetector2D.cpp +++ b/common/src/utils/ODFeatureDetector2D.cpp @@ -7,37 +7,6 @@ namespace od { - ODFeatureDetector2D::ODFeatureDetector2D(const std::string & feature_type, bool use_gpu) - { - mode_ = SIFT; - - if(use_gpu) { - if(feature_type.compare("ORB") == 0) { - mode_ = ORB_GPU; - feature_detector_ = cv::cuda::ORB::create(); - }else if(feature_type.compare("SIFT") == 0) { - mode_ = SIFT_GPU; - sift_gpu_ = new SiftGPU(); - char *argv[] = {(char *) "-fo", (char *) "-1", (char *) "-v", (char *) "1"}; - int argc = sizeof(argv) / sizeof(char *); - sift_gpu_->ParseParam(argc, argv); - if(sift_gpu_->CreateContextGL() != SiftGPU::SIFTGPU_FULL_SUPPORTED) - std::cout << "FATAL ERROR cannot create SIFTGPU context" << std::endl; - } - } else { - if(feature_type.compare("SIFT") == 0) { - mode_ = SIFT; - feature_detector_ = cv::xfeatures2d::SIFT::create(); - } else if(feature_type.compare("ORB") == 0) { - mode_ = ORB; - feature_detector_ = cv::ORB::create(); - } else if(feature_type.compare("SURF") == 0) { - mode_ = SURF; - feature_detector_ = cv::xfeatures2d::SURF::create(); - } - } - } - ODFeatureDetector2D::ODFeatureDetector2D(FeatureType type) { @@ -56,17 +25,12 @@ namespace od case(SURF) : feature_detector_ = cv::xfeatures2d::SURF::create(); break; - - case(ORB_GPU) : - feature_detector_ = cv::cuda::ORB::create(); - break; + + case(ORB_GPU) : + case(SIFT_GPU) : default : - sift_gpu_ = new SiftGPU(); - char *argv[] = {(char *) "-fo", (char *) "-1", (char *) "-v", (char *) "3", (char *) "-cuda"}; - int argc = sizeof(argv) / sizeof(char *); - sift_gpu_->ParseParam(argc, argv); - if(sift_gpu_->CreateContextGL() != SiftGPU::SIFTGPU_FULL_SUPPORTED) - std::cout << "FATAL ERROR cannot create SIFTGPU context" << std::endl; + std::cout << "FATAL ERROR, type is not defined" << std::endl; + exit(-1); break; } @@ -74,133 +38,18 @@ namespace od void ODFeatureDetector2D::computeKeypointsAndDescriptors(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) { - if(mode_ == SIFT_GPU) { - findSiftGPUDescriptors_(image, descriptors, keypoints); - } else { + feature_detector_->detect(image, keypoints); feature_detector_->compute(image, keypoints, descriptors); - } - } - - void CVMatToSiftGPU(const cv::Mat & image, unsigned char * siftImage, cv::Mat & grey) - { - siftImage = (unsigned char *) malloc(image.rows * image.cols); - cv::Mat tmp; - cv::cvtColor(image, grey, cv::COLOR_BGR2GRAY); - - memcpy(siftImage, grey.data, image.rows * image.cols); } - void ODFeatureDetector2D::findSiftGPUDescriptors_(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) - { - unsigned char * data = image.data; - cv::Mat greyimage; - if(image.type() != CV_8U) { - cv::cvtColor(image, greyimage, cv::COLOR_BGR2GRAY); - data = greyimage.data; - } - sift_gpu_->RunSIFT(image.cols, image.rows, data, GL_LUMINANCE, GL_UNSIGNED_BYTE); - - int nFeat = sift_gpu_->GetFeatureNum();//get feature count - //allocate memory for readback - std::vector<SiftGPU::SiftKeypoint> keys(nFeat); - //read back keypoints and normalized descritpros - //specify NULL if you don’t need keypoints or descriptors - std::vector<float> imageDescriptors(128 * nFeat); - sift_gpu_->GetFeatureVector(&keys[0], &imageDescriptors[0]); - - sift_gpu_->SaveSIFT("2.sift"); - - //to opencv format - keypoints.clear(); - descriptors.create(0, 128, CV_32FC1); - for(int i = 0; i < nFeat; ++i) { - cv::KeyPoint key(keys[i].x, keys[i].y, keys[i].s, keys[i].o); - keypoints.push_back(key); - cv::Mat descriptor(1, 128, CV_32FC1); - - for(int x = 0; x < 128; x++) - descriptor.at<float>(x) = floor(0.5 + (512.0f * imageDescriptors[(i * 128) + x])); - - descriptors.push_back(descriptor); - } - //viewImage(image, keypoints); - - } - - void ODFeatureDetector2D::findSiftGPUDescriptors(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) - { - unsigned char *data = image.data; - cv::Mat greyimage; - if(image.type() != CV_8U) { - cv::Mat tmp; - cv::cvtColor(image, tmp, cv::COLOR_BGR2GRAY); - data = tmp.data; - } - sift_gpu_->RunSIFT(image.cols, image.rows, data, GL_LUMINANCE, GL_UNSIGNED_BYTE); - - int nFeat = sift_gpu_->GetFeatureNum();//get feature count - //allocate memory for readback - std::vector<SiftGPU::SiftKeypoint> keys(nFeat); - //read back keypoints and normalized descritpros - //specify NULL if you don’t need keypoints or descriptors - std::vector<float> imageDescriptors(128 * nFeat); - sift_gpu_->GetFeatureVector(&keys[0], &imageDescriptors[0]); - - sift_gpu_->SaveSIFT("2.sift"); - - //to opencv format - keypoints.clear(); - cv::Mat descriptormat = cv::Mat(1, nFeat, CV_32F, &imageDescriptors[0]); - descriptormat.copyTo(descriptors); - - for(int i = 0; i < nFeat; ++i) { - cv::KeyPoint key(keys[i].x, keys[i].y, keys[i].s, keys[i].o); - keypoints.push_back(key); - } - - - //viewImage(image, keypoints); - - } - - void ODFeatureDetector2D::findSiftGPUDescriptors(const std::string & image_name, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) - { - sift_gpu_->RunSIFT(image_name.c_str()); - - unsigned int nFeat = sift_gpu_->GetFeatureNum();//get feature count - //allocate memory for readback - std::vector<SiftGPU::SiftKeypoint> keys(nFeat); - //read back keypoints and normalized descritpros - //specify NULL if you don’t need keypoints or descriptors - std::vector<float> imageDescriptors(128 * nFeat); - sift_gpu_->GetFeatureVector(&keys[0], &imageDescriptors[0]); - - sift_gpu_->SaveSIFT("1.sift"); - - //to opencv format - keypoints.clear(); - descriptors.create(0, 128, CV_32FC1); - for(size_t i = 0; i < nFeat; ++i) { - cv::KeyPoint key(keys[i].x, keys[i].y, keys[i].s, keys[i].o); - keypoints.push_back(key); - cv::Mat descriptor(1, 128, CV_32FC1); - for(size_t x = 0; x < 128; x++) - descriptor.at<float>(x) = floor(0.5 + 512.0f * imageDescriptors[(i << 7) + x]); - descriptors.push_back(descriptor); - } - cv::Mat image = cv::imread(image_name); - } void ODFeatureDetector2D::computeAndSave(const cv::Mat & image, const std::string & path) { cv::Mat descriptors; std::vector<cv::KeyPoint> keypoints; - if(mode_ == SIFT_GPU) { - findSiftGPUDescriptors_(image, descriptors, keypoints); - sift_gpu_->SaveSIFT(path.c_str()); - } else { - //DO NOTHING! IMPLEMENT LATER - } + + //TODO implementation + } } diff --git a/common/src/utils/ODFeatureDetectorInterface.cpp b/common/src/utils/ODFeatureDetectorInterface.cpp new file mode 100644 index 00000000..03caf360 --- /dev/null +++ b/common/src/utils/ODFeatureDetectorInterface.cpp @@ -0,0 +1,13 @@ +#include "od/common/utils/ODFeatureDetectorInterface.h" + +namespace od +{ + + std::map<std::string, FeatureType> od_enum_map = {{"SIFT", SIFT}, {"SURF", SURF}, {"ORB", ORB}, {"SIFT_GPU", SIFT_GPU}, {"ORB_GPU", ORB_GPU}}; + + FeatureType string2FeatureType(const std::string & name) + { + return od_enum_map[name]; + } + +} \ No newline at end of file diff --git a/common/src/utils/ODViewer.cpp b/common/src/utils/ODViewer.cpp index 22dd10c1..b36ecb81 100644 --- a/common/src/utils/ODViewer.cpp +++ b/common/src/utils/ODViewer.cpp @@ -114,7 +114,6 @@ namespace od { { update(to_display.getCVImage(), window_name); } - void ODViewer::update(shared_ptr<ODSceneImage> to_display, const std::string & window_name) { update(to_display->getCVImage(), window_name); @@ -282,6 +281,13 @@ namespace od { } + shared_ptr<pcl::visualization::PCLVisualizer> ODViewer::getViewer() + { + return viewer_; + } + + + // Explicit template function instantiation template void ODViewer::render<pcl::PointXYZ>(shared_ptr<pcl::PointCloud<pcl::PointXYZ> >, const std::string &, bool); template void ODViewer::render<pcl::PointXYZRGB>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> >, const std::string &, bool); diff --git a/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h b/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h index 00e169a0..1a43c95b 100644 --- a/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h +++ b/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h @@ -30,6 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #pragma once #include "od/detectors/local2D/ODImageLocalMatching.h" #include "od/common/pipeline/ODScene.h" +#include "od/common/utils/ODFeatureDetector.h" #include "od/detectors/local2D/simple_ransac_detection/ODMesh.h" #include "od/detectors/local2D/simple_ransac_detection/ODRobustMatcher.h" @@ -137,8 +138,8 @@ namespace od std::vector<std::string> model_names_; std::vector<Model> models_; PnPProblem pnp_detection_; - std::string f_type_default_; - shared_ptr<ODFeatureDetector2D> feature_detector_; + FeatureType f_type_default_; + shared_ptr<ODFeatureDetector> feature_detector_; }; diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/ODRobustMatcher.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/ODRobustMatcher.h index 28905059..03865120 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/ODRobustMatcher.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/ODRobustMatcher.h @@ -10,6 +10,7 @@ #include <opencv2/highgui/highgui.hpp> #include <opencv2/features2d/features2d.hpp> #include <opencv2/features2d.hpp> +#include <opencv2/cudafeatures2d.hpp> #include <opencv2/xfeatures2d.hpp> #include <opencv2/ml.hpp> #include "od/common/utils/ODFeatureDetector2D.h" diff --git a/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp b/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp index fa6cb602..204bd5e3 100644 --- a/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp +++ b/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp @@ -60,12 +60,12 @@ namespace od min_inliers_ = 30; // Kalman threshold updating pnp_method_ = cv::SOLVEPNP_EPNP; - f_type_default_ = std::string("SIFT"); + f_type_default_ = SIFT; #ifdef WITH_BOOST_SHARED_PTR - feature_detector_ = shared_ptr<ODFeatureDetector2D>(new ODFeatureDetector2D(f_type_default_, use_gpu_)); + feature_detector_ = shared_ptr<ODFeatureDetector>(new ODFeatureDetector(f_type_default_)); #else - feature_detector_ = make_shared<ODFeatureDetector2D>(f_type_default_, use_gpu_); + feature_detector_ = make_shared<ODFeatureDetector>(f_type_default_); #endif } @@ -256,12 +256,12 @@ namespace od models_.push_back(model); } if(models_.size() > 0) - f_type_default_ = models_[0].f_type_; + f_type_default_ = string2FeatureType(models_[0].f_type_); #ifdef WITH_BOOST_SHARED_PTR - feature_detector_ = shared_ptr<ODFeatureDetector2D>(new ODFeatureDetector2D(f_type_default_, use_gpu_)); + feature_detector_ = shared_ptr<ODFeatureDetector>(new ODFeatureDetector(f_type_default_)); #else - feature_detector_ = make_shared<ODFeatureDetector2D>(f_type_default_, use_gpu_); + feature_detector_ = make_shared<ODFeatureDetector>(f_type_default_); #endif } diff --git a/gpu/common/CMakeLists.txt b/gpu/common/CMakeLists.txt new file mode 100644 index 00000000..89e583ba --- /dev/null +++ b/gpu/common/CMakeLists.txt @@ -0,0 +1,23 @@ +if(WITH_GPU) + set(GPU_COMMON_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + set(GPU_COMMON_INCLUDE_DIR ${GPU_COMMON_DIR}/include) + + set(SUBSYS_NAME gpu_common) + set(LIB_NAME od_${SUBSYS_NAME}) + set(SUBSYS_DESC "common gpu feature utilities") + + set(SUBSYS_DEPS siftgpu) + + set(SOURCES + "src/utils/ODFeatureDetector2D.cpp" + ) + + include_directories(${GPU_COMMON_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${CMAKE_3RDPARTY_DIR}/SiftGPU/src/SiftGPU/) + + OD_ADD_LIBRARY("${SUBSYS_NAME}" SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) + + install(DIRECTORY ${GPU_COMMON_INCLUDE_DIR}/od DESTINATION ${OD_INSTALL_INCLUDE_DIR}) + set(${OD_INSTALLED_LIBRARIES} PARENT_SCOPE) +endif() diff --git a/gpu/common/include/od/gpu/common/utils/ODFeatureDetector2D.h b/gpu/common/include/od/gpu/common/utils/ODFeatureDetector2D.h new file mode 100644 index 00000000..57d0e6fa --- /dev/null +++ b/gpu/common/include/od/gpu/common/utils/ODFeatureDetector2D.h @@ -0,0 +1,43 @@ +// +// Created by sarkar on 20.04.15. +// +#pragma once +#include "od/common/utils/ODFeatureDetectorInterface.h" +#include <opencv2/cudafeatures2d.hpp> +#include <GL/gl.h> +#include "SiftGPU.h" + + +namespace od +{ + + namespace gpu + { + + class ODFeatureDetector2D : public ODFeatureDetectorIterface + { + + public: + + ODFeatureDetector2D(FeatureType type); + + void computeKeypointsAndDescriptors(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints); + + void computeAndSave(const cv::Mat & image, const std::string & path); + + void findSiftGPUDescriptors(const std::string & image_name, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints); + + void findSiftGPUDescriptors(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints); + + private: + + void findSiftGPUDescriptors_(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints); + + cv::Ptr<SiftGPU> sift_gpu_; + + }; + + } + +} + diff --git a/gpu/common/src/utils/ODFeatureDetector2D.cpp b/gpu/common/src/utils/ODFeatureDetector2D.cpp new file mode 100644 index 00000000..5a985539 --- /dev/null +++ b/gpu/common/src/utils/ODFeatureDetector2D.cpp @@ -0,0 +1,182 @@ +// +// Created by sarkar on 20.04.15. +// + +#include "od/gpu/common/utils/ODFeatureDetector2D.h" + +namespace od +{ + + namespace gpu + { + + + ODFeatureDetector2D::ODFeatureDetector2D(FeatureType type) + { + + mode_ = type; + + switch(type) + { + + case(SIFT_GPU) : + { + sift_gpu_ = new SiftGPU(); + char *argv[] = {(char *) "-fo", (char *) "-1", (char *) "-v", (char *) "3", (char *) "-cuda"}; + int argc = sizeof(argv) / sizeof(char *); + sift_gpu_->ParseParam(argc, argv); + if(sift_gpu_->CreateContextGL() != SiftGPU::SIFTGPU_FULL_SUPPORTED) + std::cout << "FATAL ERROR cannot create SIFTGPU context" << std::endl; + exit(-1); + break; + } + + case(ORB_GPU) : + feature_detector_ = cv::cuda::ORB::create(); + break; + + case(SURF) : + case(SIFT) : + case(ORB) : + default : + std::cout << "FATAL ERROR, type is not implemented." << std::endl; + break; + + } + } + + void ODFeatureDetector2D::computeKeypointsAndDescriptors(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) + { + if(mode_ == SIFT_GPU) { + findSiftGPUDescriptors_(image, descriptors, keypoints); + } else { + feature_detector_->detect(image, keypoints); + feature_detector_->compute(image, keypoints, descriptors); + } + } + + void CVMatToSiftGPU(const cv::Mat & image, unsigned char * siftImage, cv::Mat & grey) + { + siftImage = (unsigned char *) malloc(image.rows * image.cols); + cv::Mat tmp; + cv::cvtColor(image, grey, cv::COLOR_BGR2GRAY); + + memcpy(siftImage, grey.data, image.rows * image.cols); + } + + void ODFeatureDetector2D::findSiftGPUDescriptors_(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) + { + unsigned char * data = image.data; + cv::Mat greyimage; + if(image.type() != CV_8U) { + cv::cvtColor(image, greyimage, cv::COLOR_BGR2GRAY); + data = greyimage.data; + } + sift_gpu_->RunSIFT(image.cols, image.rows, data, GL_LUMINANCE, GL_UNSIGNED_BYTE); + + int nFeat = sift_gpu_->GetFeatureNum();//get feature count + //allocate memory for readback + std::vector<SiftGPU::SiftKeypoint> keys(nFeat); + //read back keypoints and normalized descritpros + //specify NULL if you don’t need keypoints or descriptors + std::vector<float> imageDescriptors(128 * nFeat); + sift_gpu_->GetFeatureVector(&keys[0], &imageDescriptors[0]); + + sift_gpu_->SaveSIFT("2.sift"); + + //to opencv format + keypoints.clear(); + descriptors.create(0, 128, CV_32FC1); + for(int i = 0; i < nFeat; ++i) { + cv::KeyPoint key(keys[i].x, keys[i].y, keys[i].s, keys[i].o); + keypoints.push_back(key); + cv::Mat descriptor(1, 128, CV_32FC1); + + for(int x = 0; x < 128; x++) + descriptor.at<float>(x) = floor(0.5 + (512.0f * imageDescriptors[(i * 128) + x])); + + descriptors.push_back(descriptor); + } + //viewImage(image, keypoints); + + } + + void ODFeatureDetector2D::findSiftGPUDescriptors(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) + { + unsigned char *data = image.data; + cv::Mat greyimage; + if(image.type() != CV_8U) { + cv::Mat tmp; + cv::cvtColor(image, tmp, cv::COLOR_BGR2GRAY); + data = tmp.data; + } + sift_gpu_->RunSIFT(image.cols, image.rows, data, GL_LUMINANCE, GL_UNSIGNED_BYTE); + + int nFeat = sift_gpu_->GetFeatureNum();//get feature count + //allocate memory for readback + std::vector<SiftGPU::SiftKeypoint> keys(nFeat); + //read back keypoints and normalized descritpros + //specify NULL if you don’t need keypoints or descriptors + std::vector<float> imageDescriptors(128 * nFeat); + sift_gpu_->GetFeatureVector(&keys[0], &imageDescriptors[0]); + + sift_gpu_->SaveSIFT("2.sift"); + + //to opencv format + keypoints.clear(); + cv::Mat descriptormat = cv::Mat(1, nFeat, CV_32F, &imageDescriptors[0]); + descriptormat.copyTo(descriptors); + + for(int i = 0; i < nFeat; ++i) { + cv::KeyPoint key(keys[i].x, keys[i].y, keys[i].s, keys[i].o); + keypoints.push_back(key); + } + + + //viewImage(image, keypoints); + + } + + void ODFeatureDetector2D::findSiftGPUDescriptors(const std::string & image_name, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) + { + sift_gpu_->RunSIFT(image_name.c_str()); + + unsigned int nFeat = sift_gpu_->GetFeatureNum();//get feature count + //allocate memory for readback + std::vector<SiftGPU::SiftKeypoint> keys(nFeat); + //read back keypoints and normalized descritpros + //specify NULL if you don’t need keypoints or descriptors + std::vector<float> imageDescriptors(128 * nFeat); + sift_gpu_->GetFeatureVector(&keys[0], &imageDescriptors[0]); + + sift_gpu_->SaveSIFT("1.sift"); + + //to opencv format + keypoints.clear(); + descriptors.create(0, 128, CV_32FC1); + for(size_t i = 0; i < nFeat; ++i) { + cv::KeyPoint key(keys[i].x, keys[i].y, keys[i].s, keys[i].o); + keypoints.push_back(key); + cv::Mat descriptor(1, 128, CV_32FC1); + for(size_t x = 0; x < 128; x++) + descriptor.at<float>(x) = floor(0.5 + 512.0f * imageDescriptors[(i << 7) + x]); + descriptors.push_back(descriptor); + } + cv::Mat image = cv::imread(image_name); + } + + void ODFeatureDetector2D::computeAndSave(const cv::Mat & image, const std::string & path) + { + cv::Mat descriptors; + std::vector<cv::KeyPoint> keypoints; + if(mode_ == SIFT_GPU) { + findSiftGPUDescriptors_(image, descriptors, keypoints); + sift_gpu_->SaveSIFT(path.c_str()); + } else { + //DO NOTHING! IMPLEMENT LATER + } + } + + } + +} diff --git a/gpu/detectors/src/global2D/CMakeLists.txt b/gpu/detectors/src/global2D/CMakeLists.txt index c10e763c..750c1b8d 100644 --- a/gpu/detectors/src/global2D/CMakeLists.txt +++ b/gpu/detectors/src/global2D/CMakeLists.txt @@ -4,18 +4,14 @@ set(SUBSYS_DESC "global gpu feature based detection in 2D images") set(SUBSYS_DEPS od_common) -set(build TRUE) -if(build) +set(SOURCES + "detection/ODCascadeDetector.cpp" +) - set(SOURCES - "detection/ODCascadeDetector.cpp" - ) - - include_directories(${GPU_DETECTORS_INCLUDE_DIR}) - include_directories(${COMMON_INCLUDE_DIR}) - include_directories(${PCL_INCLUDE_DIRS}) +include_directories(${GPU_DETECTORS_INCLUDE_DIR}) +include_directories(${COMMON_INCLUDE_DIR}) +include_directories(${PCL_INCLUDE_DIRS}) - OD_ADD_LIBRARY("${SUBSYS_NAME}" SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) +OD_ADD_LIBRARY("${SUBSYS_NAME}" SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) -endif(build) \ No newline at end of file From d927522422bbc8959e1dd439e3171c90b4667203 Mon Sep 17 00:00:00 2001 From: giacomo <giacomo.dabisias@gmail.com> Date: Wed, 20 Jul 2016 16:12:05 +0200 Subject: [PATCH 48/79] adds cascade decetor as separate module in gpu with new interface but needs to be tested --- CMakeLists.txt | 2 + .../misc/detection/ODDetectorMultiAlgo.hpp | 12 +- .../global2D/detection/ODCascadeDetector.h | 16 +- .../detection/ODCascadeDetectorImpl.h | 98 +++++++++++ .../detection/ODCascadeDetectorInterface.h | 30 ++++ detectors/src/global2D/CMakeLists.txt | 5 +- .../global2D/detection/ODCascadeDetector.cpp | 108 ++++-------- .../detection/ODCascadeDetectorImpl.cpp | 165 ++++++++++++++++++ examples/objectdetector/CMakeLists.txt | 2 +- .../objectdetector/od_face_detector_cam.cpp | 16 +- .../objectdetector/od_face_detector_files.cpp | 16 +- ...cadeDetector.h => ODCascadeDetectorImpl.h} | 15 +- gpu/detectors/src/global2D/CMakeLists.txt | 4 +- ...Detector.cpp => ODCascadeDetectorImpl.cpp} | 30 ++-- 14 files changed, 382 insertions(+), 137 deletions(-) create mode 100644 detectors/include/od/detectors/global2D/detection/ODCascadeDetectorImpl.h create mode 100644 detectors/include/od/detectors/global2D/detection/ODCascadeDetectorInterface.h create mode 100644 detectors/src/global2D/detection/ODCascadeDetectorImpl.cpp rename gpu/detectors/include/od/gpu/detectors/global2D/detection/{ODCascadeDetector.h => ODCascadeDetectorImpl.h} (89%) rename gpu/detectors/src/global2D/detection/{ODCascadeDetector.cpp => ODCascadeDetectorImpl.cpp} (80%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 40d902f1..00b96957 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,7 +51,9 @@ endif() if(WITH_GPU) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWITH_GPU") + # Needed by od_common. There is a circular dependency between od_common and od_gpu_common include dirs. include_directories(GPU_COMMON_INCLUDE_DIRS ${OD_SOURCE_DIR}/gpu/common/include) + include_directories(GPU_DETECTORS_INCLUDE_DIRS ${OD_SOURCE_DIR}/gpu/detectors/include) endif() # Set modules diff --git a/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp b/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp index b46445f5..9d33a8f2 100644 --- a/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp +++ b/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp @@ -104,10 +104,18 @@ namespace od //vector<ODDetector *> detectors = {new ODCascadeDetector(trained_data_location_), new ODHOGDetector(trained_data_location_), new ODCADRecognizer2DLocal(trained_data_location_)}; #ifdef WITH_BOOST_SHARED_PTR - detectors_2d_.push_back(shared_ptr<g2d::ODCascadeDetector>(new g2d::ODCascadeDetector(trained_data_location_))); + #if WITH_GPU + detectors_2d_.push_back(shared_ptr<g2d::ODCascadeDetector>(new g2d::ODCascadeDetector(true, trained_data_location_))); + #else + detectors_2d_.push_back(shared_ptr<g2d::ODCascadeDetector>(new g2d::ODCascadeDetector(false, trained_data_location_))); + #endif detectors_2d_.push_back(shared_ptr<g2d::ODHOGDetector>(new g2d::ODHOGDetector(trained_data_location_))); #else - detectors_2d_.push_back(make_shared<g2d::ODCascadeDetector>(trained_data_location_)); + #if WITH_GPU + detectors_2d_.push_back(make_shared<g2d::ODCascadeDetector>(true, trained_data_location_)); + #else + detectors_2d_.push_back(make_shared<g2d::ODCascadeDetector>(false, trained_data_location_)); + #endif detectors_2d_.push_back(make_shared<g2d::ODHOGDetector>(trained_data_location_)); #endif diff --git a/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h b/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h index 81a8b126..30afeacf 100644 --- a/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h +++ b/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h @@ -31,6 +31,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/common/pipeline/ODScene.h" #include "od/common/utils/ODUtils.h" #include "od/common/utils/ODFeatureDetector2D.h" +#include "od/detectors/global2D/detection/ODCascadeDetectorInterface.h" +#include "od/gpu/detectors/global2D/detection/ODCascadeDetectorImpl.h" +#include "od/detectors/global2D/detection/ODCascadeDetectorImpl.h" #include <opencv2/opencv.hpp> @@ -49,9 +52,10 @@ namespace od class ODCascadeDetector : public ODDetector2D { + public: - ODCascadeDetector(const std::string & trainer = std::string("cascade.xml"), const std::string & trained_data_location = std::string(""), double scale_factor = 1.1, int min_neighbors = 3, + ODCascadeDetector(bool gpu = false, const std::string & trainer = std::string("cascade.xml"), const std::string & trained_data_location = std::string(""), double scale_factor = 1.1, int min_neighbors = 3, int flags = 0, const cv::Size & min_size = cv::Size(), const cv::Size & max_size = cv::Size()); void init(); @@ -75,17 +79,13 @@ namespace od private: - shared_ptr<cv::CascadeClassifier> haar_cascade_; - - double scale_factor_; - int min_neighbors_; - cv::Size min_size_; - cv::Size max_size_; + shared_ptr<ODCascadeDetectorInterface> cascade_detector_; }; + /** \examples objectdetector/od_image_cascade.cpp * \examples objectdetector/od_image_cascade_files.cpp */ } -} +} \ No newline at end of file diff --git a/detectors/include/od/detectors/global2D/detection/ODCascadeDetectorImpl.h b/detectors/include/od/detectors/global2D/detection/ODCascadeDetectorImpl.h new file mode 100644 index 00000000..f38a1014 --- /dev/null +++ b/detectors/include/od/detectors/global2D/detection/ODCascadeDetectorImpl.h @@ -0,0 +1,98 @@ +/* +Copyright (c) 2015, Kripasindhu Sarkar +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions 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. + * Neither the name of the copyright holder(s) nor the + names of its contributors may 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 COPYRIGHT HOLDER 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. +*/// +// Created by sarkar on 17.07.15. +// +#pragma once +#include "od/common/pipeline/ODDetector.h" +#include "od/common/pipeline/ODScene.h" +#include "od/common/utils/ODUtils.h" +#include "od/common/utils/ODFeatureDetector2D.h" +#include "od/detectors/global2D/detection/ODCascadeDetectorInterface.h" +#include <opencv2/opencv.hpp> + +namespace od +{ + namespace g2d + { + /** \brief A class for detection using Cascade classifiers. + * Given a scene and a cascade classifier, this class performs a classification and returns detections. The training is not supported in OD currently but is compatible to the cascade training of OpenCV. + * Train your cascade classifiers using OpenCV's *opencv_traincascade* utility (http://docs.opencv.org/master/dc/d88/tutorial_traincascade.html#gsc.tab=0). It is a great tool for traning your cascade. + * Paste the generated xml in trained_data_location_/TD_CASCADE/\*.cascade.xml to use your trained cascade. + * + * \author Kripasindhu Sarkar + * + */ + class ODCascadeDetectorImpl : public ODCascadeDetectorInterface + { + public: + + ODCascadeDetectorImpl(const std::string & trainer = std::string("cascade.xml"), const std::string & trained_data_location = std::string(""), double scale_factor = 1.1, int min_neighbors = 3, + int flags = 0, const cv::Size & min_size = cv::Size(), const cv::Size & max_size = cv::Size()); + + void init(); + + shared_ptr<ODDetections2D> detectOmni(shared_ptr<ODSceneImage> scene); + shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene); + + shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene); + + void setScale(const float scale); + float getScale() const; + + void setMinNeighbors(const unsigned int min_neighbors); + unsigned int getMinNeighbors() const; + + void setMinSize(const cv::Size & size); + cv::Size getMinSize() const; + + void setMaxSize(const cv::Size & size); + cv::Size getMaxSize() const; + + private: + + shared_ptr<cv::CascadeClassifier> haar_cascade_; + + bool meta_info_; + + std::string trained_data_location_, trained_data_id_, trained_location_identifier_; + double scale_factor_; + int min_neighbors_; + cv::Size min_size_; + cv::Size max_size_; + + + }; + /** \examples objectdetector/od_image_cascade.cpp + * \examples objectdetector/od_image_cascade_files.cpp + */ + } + +} + + + + diff --git a/detectors/include/od/detectors/global2D/detection/ODCascadeDetectorInterface.h b/detectors/include/od/detectors/global2D/detection/ODCascadeDetectorInterface.h new file mode 100644 index 00000000..994e99ce --- /dev/null +++ b/detectors/include/od/detectors/global2D/detection/ODCascadeDetectorInterface.h @@ -0,0 +1,30 @@ +#pragma once + +namespace od +{ + class ODCascadeDetectorInterface + { + public: + + virtual shared_ptr<ODDetections2D> detectOmni(shared_ptr<ODSceneImage> scene) = 0; + virtual shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene) = 0; + + virtual shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene) = 0; + + virtual void init() = 0; + + virtual void setScale(const float scale) = 0; + virtual float getScale() const = 0; + + virtual void setMinNeighbors(const unsigned int min_neighbors) = 0; + virtual unsigned int getMinNeighbors() const = 0; + + virtual void setMinSize(const cv::Size & size) = 0; + virtual cv::Size getMinSize() const = 0; + + virtual void setMaxSize(const cv::Size & size) = 0; + virtual cv::Size getMaxSize() const = 0; + + }; + +} \ No newline at end of file diff --git a/detectors/src/global2D/CMakeLists.txt b/detectors/src/global2D/CMakeLists.txt index 0b10a07e..71c7b1ae 100644 --- a/detectors/src/global2D/CMakeLists.txt +++ b/detectors/src/global2D/CMakeLists.txt @@ -2,18 +2,21 @@ set(SUBSYS_NAME global_image_detector) set(LIB_NAME od_${SUBSYS_NAME}) set(SUBSYS_DESC "global feature based detection in 2D images") -set(SUBSYS_DEPS od_common svmlight) +set(SUBSYS_DEPS od_common svmlight od_gpu_global_image_detector) if(BUILD_GLOBAL_2D_DETECTION) set(SOURCES "ODFaceRecognizer.cpp" "detection/ODCascadeDetector.cpp" + "detection/ODCascadeDetectorImpl.cpp" "detection/ODHOGDetector.cpp" "training/ODHOGTrainer.cpp" ) include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${GPU_DETECTORS_INCLUDE_DIRS}) + message("DIR " ${GPU_DETECTORS_INCLUDE_DIRS}) include_directories(${COMMON_INCLUDE_DIR}) include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) include_directories(${PCL_INCLUDE_DIRS}) diff --git a/detectors/src/global2D/detection/ODCascadeDetector.cpp b/detectors/src/global2D/detection/ODCascadeDetector.cpp index b51e5336..65cf20b8 100644 --- a/detectors/src/global2D/detection/ODCascadeDetector.cpp +++ b/detectors/src/global2D/detection/ODCascadeDetector.cpp @@ -35,130 +35,88 @@ namespace od namespace g2d { - ODCascadeDetector::ODCascadeDetector(const std::string & trainer, const std::string & trained_data_location, double scale_factor, int min_neighbors, int flags, - const cv::Size & min_size, const cv::Size & max_size): - ODDetector2D(trained_data_location), scale_factor_(scale_factor), min_neighbors_(min_neighbors), - min_size_(min_size), max_size_(max_size) + ODCascadeDetector::ODCascadeDetector(bool gpu, const std::string & trainer, const std::string & trained_data_location, double scale_factor, int min_neighbors, int flags, + const cv::Size & min_size, const cv::Size & max_size) { - trained_location_identifier_ = std::string("CASCADE"); - trained_data_id_ = trainer; - meta_info_ = true; + if(gpu){ +#if WITH_GPU + #ifdef WITH_BOOST_SHARED_PTR + cascade_detector_ = shared_ptr<ODCascadeDetectorImpl>(new ODCascadeDetectorImpl(trainer, trained_data_location, scale_factor, min_neighbors, flags, min_size, max_size)); + #else + cascade_detector_ = make_shared<ODCascadeDetectorImpl>(trainer, trained_data_location, scale_factor, min_neighbors, flags, min_size, max_size); + #endif +#else + std::cout << "Error !! GPU CascadeDetector has not been compiled. Recompile with WITH_GPU." << std::endl; + exit(-1); +#endif + } + else { +#ifdef WITH_BOOST_SHARED_PTR + cascade_detector_ = shared_ptr<od::gpu::g2d::ODCascadeDetectorImpl>(new od::gpu::g2d::ODCascadeDetectorImpl(trainer, trained_data_location, scale_factor, min_neighbors, flags, min_size, max_size)); +#else + cascade_detector_ = make_shared<od::gpu::g2d::ODCascadeDetectorImpl>(trainer, trained_data_location, scale_factor, min_neighbors, flags, min_size, max_size); +#endif + } } shared_ptr<ODDetections> ODCascadeDetector::detectOmni(shared_ptr<ODScene> scene) { - std::cout << "not implemented, use detect()" <<std::endl; - return nullptr; + return cascade_detector_->detectOmni(scene); }; void ODCascadeDetector::init() { -#ifdef WITH_BOOST_SHARED_PTR - haar_cascade_ = shared_ptr<cv::CascadeClassifier>(new cv::CascadeClassifier(trained_data_id_)); -#else - haar_cascade_ = make_shared<cv::CascadeClassifier>(trained_data_id_); -#endif + cascade_detector_->init(); } shared_ptr<ODDetections2D> ODCascadeDetector::detectOmni(shared_ptr<ODSceneImage> scene) { - - if(!haar_cascade_){ - std::cout << "Call init() first!" << std::endl; - return nullptr; - } - - cv::Mat gray; - cv::cvtColor(scene->getCVImage(), gray, CV_BGR2GRAY); - - std::vector<cv::Rect_<int> > objects; - haar_cascade_->detectMultiScale(gray, objects, scale_factor_, min_neighbors_, 0, min_size_, max_size_); - - //always create detections - shared_ptr<ODDetections2D> detections = make_shared<ODDetections2D>(); - cv::Mat viz = scene->getCVImage(); - for(auto & o : objects) - { - // Process object by object: - shared_ptr<ODDetection2D> detection2D = make_shared<ODDetection2D>(ODDetection::OD_CLASSIFICATION, "OBJ", 1); - detection2D->setBoundingBox(o); - detections->push_back(detection2D); - - if(meta_info_) - { - cv::rectangle(viz, o, CV_RGB(0, 255, 0), 1); - } - } - detections->setMetainfoImage(viz); - - return detections; + return cascade_detector_->detectOmni(scene); } shared_ptr<ODDetections> ODCascadeDetector::detect(shared_ptr<ODSceneImage> scene) { - - if(!haar_cascade_){ - std::cout << "Call init() first!" << std::endl; - return nullptr; - } - - //always create detections - shared_ptr<ODDetections> detections = make_shared<ODDetections>(); - - cv::Mat gray; - cv::cvtColor(scene->getCVImage(), gray, CV_BGR2GRAY); - // Find the objects in the frame: - std::vector<cv::Rect_<int> > objects; - - //hack for single detection, - //note: maxsize = minsize = size of input image for single window detection - //todo: implement in some other way of fast single detection; currently this will work, but maynot be fast - haar_cascade_->detectMultiScale(gray, objects, 5, min_neighbors_, 0, gray.size(), gray.size()); - if(objects.size() > 0) - { - detections->push_back(make_shared<ODDetection>(ODDetection::OD_CLASSIFICATION, "OBJ", 1)); - } - return detections; + return cascade_detector_->detect(scene); } void ODCascadeDetector::setScale(const float scale) { - scale_factor_ = scale; + cascade_detector_->setScale(scale); } float ODCascadeDetector::getScale() const { - return scale_factor_; + return cascade_detector_->getScale(); } void ODCascadeDetector::setMinNeighbors(const unsigned int min_neighbors) { - min_neighbors_ = min_neighbors; + cascade_detector_->setMinNeighbors(min_neighbors); } unsigned int ODCascadeDetector::getMinNeighbors() const { - return min_neighbors_; + return cascade_detector_->getMinNeighbors(); } void ODCascadeDetector::setMinSize(const cv::Size & size) { - min_size_ = size; + cascade_detector_->setMinSize(size); } cv::Size ODCascadeDetector::getMinSize() const { - return min_size_; + return cascade_detector_->getMinSize(); } void ODCascadeDetector::setMaxSize(const cv::Size & size) { - max_size_ = size; + cascade_detector_->setMaxSize(size); } cv::Size ODCascadeDetector::getMaxSize() const { - return max_size_; + return cascade_detector_->getMaxSize(); } } diff --git a/detectors/src/global2D/detection/ODCascadeDetectorImpl.cpp b/detectors/src/global2D/detection/ODCascadeDetectorImpl.cpp new file mode 100644 index 00000000..6ae93e40 --- /dev/null +++ b/detectors/src/global2D/detection/ODCascadeDetectorImpl.cpp @@ -0,0 +1,165 @@ +/* +Copyright (c) 2015, Kripasindhu Sarkar +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions 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. + * Neither the name of the copyright holder(s) nor the + names of its contributors may 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 COPYRIGHT HOLDER 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. +*/// +// Created by sarkar on 17.07.15. +// + +#include "od/detectors/global2D/detection/ODCascadeDetectorImpl.h" + +namespace od +{ + + namespace g2d + { + + ODCascadeDetectorImpl::ODCascadeDetectorImpl(const std::string & trainer, const std::string & trained_data_location, double scale_factor, int min_neighbors, int flags, + const cv::Size & min_size, const cv::Size & max_size): + trained_data_location_(trained_data_location), scale_factor_(scale_factor), min_neighbors_(min_neighbors), + min_size_(min_size), max_size_(max_size) + { + trained_location_identifier_ = std::string("CASCADE"); + trained_data_id_ = trainer; + meta_info_ = true; + } + + shared_ptr<ODDetections> ODCascadeDetectorImpl::detectOmni(shared_ptr<ODScene> scene) + { + std::cout << "not implemented, use detect()" <<std::endl; + return nullptr; + }; + + void ODCascadeDetectorImpl::init() + { +#ifdef WITH_BOOST_SHARED_PTR + haar_cascade_ = shared_ptr<cv::CascadeClassifier>(new cv::CascadeClassifier(trained_data_id_)); +#else + haar_cascade_ = make_shared<cv::CascadeClassifier>(trained_data_id_); +#endif + } + + shared_ptr<ODDetections2D> ODCascadeDetectorImpl::detectOmni(shared_ptr<ODSceneImage> scene) + { + + if(!haar_cascade_){ + std::cout << "Call init() first!" << std::endl; + return nullptr; + } + + cv::Mat gray; + cv::cvtColor(scene->getCVImage(), gray, CV_BGR2GRAY); + + std::vector<cv::Rect_<int> > objects; + haar_cascade_->detectMultiScale(gray, objects, scale_factor_, min_neighbors_, 0, min_size_, max_size_); + + //always create detections + shared_ptr<ODDetections2D> detections = make_shared<ODDetections2D>(); + cv::Mat viz = scene->getCVImage(); + for(auto & o : objects) + { + // Process object by object: + shared_ptr<ODDetection2D> detection2D = make_shared<ODDetection2D>(ODDetection::OD_CLASSIFICATION, "OBJ", 1); + detection2D->setBoundingBox(o); + detections->push_back(detection2D); + + if(meta_info_) + { + cv::rectangle(viz, o, CV_RGB(0, 255, 0), 1); + } + } + detections->setMetainfoImage(viz); + + return detections; + } + + shared_ptr<ODDetections> ODCascadeDetectorImpl::detect(shared_ptr<ODSceneImage> scene) + { + + if(!haar_cascade_){ + std::cout << "Call init() first!" << std::endl; + return nullptr; + } + + //always create detections + shared_ptr<ODDetections> detections = make_shared<ODDetections>(); + + cv::Mat gray; + cv::cvtColor(scene->getCVImage(), gray, CV_BGR2GRAY); + // Find the objects in the frame: + std::vector<cv::Rect_<int> > objects; + + //hack for single detection, + //note: maxsize = minsize = size of input image for single window detection + //todo: implement in some other way of fast single detection; currently this will work, but maynot be fast + haar_cascade_->detectMultiScale(gray, objects, 5, min_neighbors_, 0, gray.size(), gray.size()); + if(objects.size() > 0) + { + detections->push_back(make_shared<ODDetection>(ODDetection::OD_CLASSIFICATION, "OBJ", 1)); + } + return detections; + } + + void ODCascadeDetectorImpl::setScale(const float scale) + { + scale_factor_ = scale; + } + + float ODCascadeDetectorImpl::getScale() const + { + return scale_factor_; + } + + void ODCascadeDetectorImpl::setMinNeighbors(const unsigned int min_neighbors) + { + min_neighbors_ = min_neighbors; + } + + unsigned int ODCascadeDetectorImpl::getMinNeighbors() const + { + return min_neighbors_; + } + + void ODCascadeDetectorImpl::setMinSize(const cv::Size & size) + { + min_size_ = size; + } + + cv::Size ODCascadeDetectorImpl::getMinSize() const + { + return min_size_; + } + + void ODCascadeDetectorImpl::setMaxSize(const cv::Size & size) + { + max_size_ = size; + } + + cv::Size ODCascadeDetectorImpl::getMaxSize() const + { + return max_size_; + } + + } +} \ No newline at end of file diff --git a/examples/objectdetector/CMakeLists.txt b/examples/objectdetector/CMakeLists.txt index a071fc8e..bd3d7a3e 100644 --- a/examples/objectdetector/CMakeLists.txt +++ b/examples/objectdetector/CMakeLists.txt @@ -64,7 +64,7 @@ if(image_hog_files_example) OD_ADD_EXAMPLE(od_image_hog_files FILES od_image_hog_files.cpp - LINK_WITH od_global_image_detector) + LINK_WITH od_global_image_detector od_gpu_global_image_detector) else() message("!!! image_hog_files_example is set to on but BUILD_GLOBAL_2D_DETECTION is necessary to build image_hog_files_example") endif(TARGET od_global_image_detector) diff --git a/examples/objectdetector/od_face_detector_cam.cpp b/examples/objectdetector/od_face_detector_cam.cpp index 248a6652..463e0e7c 100644 --- a/examples/objectdetector/od_face_detector_cam.cpp +++ b/examples/objectdetector/od_face_detector_cam.cpp @@ -1,7 +1,4 @@ #include "od/detectors/global2D/detection/ODCascadeDetector.h" -#ifdef WITH_GPU - #include "od/gpu/detectors/global2D/detection/ODCascadeDetector.h" -#endif #include "od/common/utils/ODFrameGenerator.h" #include "od/common/utils/ODViewer.h" @@ -33,17 +30,8 @@ int main(int argc, char * argv[]) boost::shared_ptr<od::ODDetector2D> detector; - if(gpu) - { -#ifdef WITH_GPU - detector = boost::make_shared<od::gpu::g2d::ODCascadeDetector>(argv[1]); -#else - std::cout << "Compiled without gpu support. Recompile to use gpu support" << std::endl; - return -1; -#endif - }else{ - detector = boost::make_shared<od::g2d::ODCascadeDetector>(argv[1]); - } + //ADD GPU + detector = boost::make_shared<od::g2d::ODCascadeDetector>(gpu, argv[1]); detector->init(); diff --git a/examples/objectdetector/od_face_detector_files.cpp b/examples/objectdetector/od_face_detector_files.cpp index 6fef557a..9dd79db9 100644 --- a/examples/objectdetector/od_face_detector_files.cpp +++ b/examples/objectdetector/od_face_detector_files.cpp @@ -1,7 +1,5 @@ #include "od/detectors/global2D/detection/ODCascadeDetector.h" -#ifdef WITH_GPU - #include "od/gpu/detectors/global2D/detection/ODCascadeDetector.h" -#endif + #include "od/common/utils/ODFrameGenerator.h" #include "od/common/utils/ODViewer.h" @@ -27,18 +25,8 @@ int main(int argc, char * argv[]) boost::shared_ptr<od::ODDetector2D> detector; + detector = boost::make_shared<od::g2d::ODCascadeDetector>(gpu, argv[1]); - if(gpu) - { -#ifdef WITH_GPU - detector = boost::make_shared<od::gpu::g2d::ODCascadeDetector>(argv[1]); -#else - std::cout << "Compiled without gpu support. Recompile to use gpu support" << std::endl; - return -1; -#endif - }else{ - detector = boost::make_shared<od::g2d::ODCascadeDetector>(argv[1]); - } detector->init(); diff --git a/gpu/detectors/include/od/gpu/detectors/global2D/detection/ODCascadeDetector.h b/gpu/detectors/include/od/gpu/detectors/global2D/detection/ODCascadeDetectorImpl.h similarity index 89% rename from gpu/detectors/include/od/gpu/detectors/global2D/detection/ODCascadeDetector.h rename to gpu/detectors/include/od/gpu/detectors/global2D/detection/ODCascadeDetectorImpl.h index 6296a0e8..9757222e 100644 --- a/gpu/detectors/include/od/gpu/detectors/global2D/detection/ODCascadeDetector.h +++ b/gpu/detectors/include/od/gpu/detectors/global2D/detection/ODCascadeDetectorImpl.h @@ -31,6 +31,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/common/pipeline/ODScene.h" #include "od/common/utils/ODUtils.h" #include "od/common/utils/ODFeatureDetector2D.h" +#include "od/detectors/global2D/detection/ODCascadeDetectorInterface.h" #include <opencv2/cudaobjdetect.hpp> #include <opencv2/cudaimgproc.hpp> @@ -54,11 +55,11 @@ namespace od * */ - class ODCascadeDetector : public ODDetector2D + class ODCascadeDetectorImpl : public ODCascadeDetectorInterface { public: - ODCascadeDetector(const std::string & trainer = std::string("cascade.xml"), const std::string & trained_data_location = std::string(""), double scale_factor = 1.1, int min_neighbors = 3, + ODCascadeDetectorImpl(const std::string & trainer = std::string("cascade.xml"), const std::string & trained_data_location = std::string(""), double scale_factor = 1.1, int min_neighbors = 3, int flags = 0, const cv::Size & min_size = cv::Size(), const cv::Size & max_size = cv::Size()); void init(); @@ -85,17 +86,21 @@ namespace od private: + double scale_factor_; + int min_neighbors_; + cv::Size min_size_; + cv::Size max_size_; + cv::Ptr<cv::cuda::CascadeClassifier> haar_cascade_; cv::cuda::GpuMat gray_, buf_, color_; std::vector<cv::Rect_<int> > objects_; - double scale_factor_; - int min_neighbors_; - cv::Size min_size_, max_size_; std::string trained_data_id_; bool meta_info_, largest_; + std::string trained_data_location_; + }; /** \examples objectdetector/od_image_cascade.cpp * \examples objectdetector/od_image_cascade_files.cpp diff --git a/gpu/detectors/src/global2D/CMakeLists.txt b/gpu/detectors/src/global2D/CMakeLists.txt index 750c1b8d..7ea39ef6 100644 --- a/gpu/detectors/src/global2D/CMakeLists.txt +++ b/gpu/detectors/src/global2D/CMakeLists.txt @@ -4,12 +4,12 @@ set(SUBSYS_DESC "global gpu feature based detection in 2D images") set(SUBSYS_DEPS od_common) - set(SOURCES - "detection/ODCascadeDetector.cpp" + "detection/ODCascadeDetectorImpl.cpp" ) include_directories(${GPU_DETECTORS_INCLUDE_DIR}) +include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) include_directories(${PCL_INCLUDE_DIRS}) diff --git a/gpu/detectors/src/global2D/detection/ODCascadeDetector.cpp b/gpu/detectors/src/global2D/detection/ODCascadeDetectorImpl.cpp similarity index 80% rename from gpu/detectors/src/global2D/detection/ODCascadeDetector.cpp rename to gpu/detectors/src/global2D/detection/ODCascadeDetectorImpl.cpp index 0f07c7da..5f0db01a 100644 --- a/gpu/detectors/src/global2D/detection/ODCascadeDetector.cpp +++ b/gpu/detectors/src/global2D/detection/ODCascadeDetectorImpl.cpp @@ -27,7 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 17.07.15. // -#include "od/gpu/detectors/global2D/detection/ODCascadeDetector.h" +#include "od/gpu/detectors/global2D/detection/ODCascadeDetectorImpl.h" namespace od { @@ -38,24 +38,24 @@ namespace od namespace g2d { - ODCascadeDetector::ODCascadeDetector(const std::string & trainer, const std::string & trained_data_location, double scale_factor, int min_neighbors, int flags, + ODCascadeDetectorImpl::ODCascadeDetectorImpl(const std::string & trainer, const std::string & trained_data_location, double scale_factor, int min_neighbors, int flags, const cv::Size & min_size, const cv::Size & max_size): - ODDetector2D(trained_data_location), scale_factor_(scale_factor), min_neighbors_(min_neighbors), + trained_data_location_(trained_data_location), scale_factor_(scale_factor), min_neighbors_(min_neighbors), min_size_(min_size), max_size_(max_size), trained_data_id_(trainer), meta_info_(true), largest_(true) {} - shared_ptr<ODDetections> ODCascadeDetector::detectOmni(shared_ptr<ODScene> scene) + shared_ptr<ODDetections> ODCascadeDetectorImpl::detectOmni(shared_ptr<ODScene> scene) { std::cout << "not implemented, use detect()" <<std::endl; return nullptr; } - void ODCascadeDetector::init() + void ODCascadeDetectorImpl::init() { haar_cascade_ = cv::cuda::CascadeClassifier::create(trained_data_id_); } - shared_ptr<ODDetections2D> ODCascadeDetector::detectOmni(shared_ptr<ODSceneImage> scene) + shared_ptr<ODDetections2D> ODCascadeDetectorImpl::detectOmni(shared_ptr<ODSceneImage> scene) { if(!haar_cascade_){ std::cout << "Call init() first!" << std::endl; @@ -99,7 +99,7 @@ namespace od return detections; } - shared_ptr<ODDetections> ODCascadeDetector::detect(shared_ptr<ODSceneImage> scene) + shared_ptr<ODDetections> ODCascadeDetectorImpl::detect(shared_ptr<ODSceneImage> scene) { if(!haar_cascade_){ @@ -137,42 +137,42 @@ namespace od return detections; } - void ODCascadeDetector::setScale(const float scale) + void ODCascadeDetectorImpl::setScale(const float scale) { scale_factor_ = scale; } - float ODCascadeDetector::getScale() const + float ODCascadeDetectorImpl::getScale() const { return scale_factor_; } - void ODCascadeDetector::setMinNeighbors(const unsigned int min_neighbors) + void ODCascadeDetectorImpl::setMinNeighbors(const unsigned int min_neighbors) { min_neighbors_ = min_neighbors; } - unsigned int ODCascadeDetector::getMinNeighbors() const + unsigned int ODCascadeDetectorImpl::getMinNeighbors() const { return min_neighbors_; } - void ODCascadeDetector::setMinSize(const cv::Size & size) + void ODCascadeDetectorImpl::setMinSize(const cv::Size & size) { min_size_ = size; } - cv::Size ODCascadeDetector::getMinSize() const + cv::Size ODCascadeDetectorImpl::getMinSize() const { return min_size_; } - void ODCascadeDetector::setMaxSize(const cv::Size & size) + void ODCascadeDetectorImpl::setMaxSize(const cv::Size & size) { max_size_ = size; } - cv::Size ODCascadeDetector::getMaxSize() const + cv::Size ODCascadeDetectorImpl::getMaxSize() const { return max_size_; } From 4551072944b7e2b1d135cb6b503f2c78eadf47f8 Mon Sep 17 00:00:00 2001 From: giacomo <giacomo.dabisias@gmail.com> Date: Wed, 20 Jul 2016 16:22:52 +0200 Subject: [PATCH 49/79] adds feature detector string constructor --- common/include/od/common/utils/ODFeatureDetector.h | 1 + common/src/utils/ODFeatureDetector.cpp | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/common/include/od/common/utils/ODFeatureDetector.h b/common/include/od/common/utils/ODFeatureDetector.h index 5c280de4..c82bb6d5 100644 --- a/common/include/od/common/utils/ODFeatureDetector.h +++ b/common/include/od/common/utils/ODFeatureDetector.h @@ -14,6 +14,7 @@ namespace od public: ODFeatureDetector(FeatureType type); + ODFeatureDetector(const std::string & type); void computeKeypointsAndDescriptors(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints); diff --git a/common/src/utils/ODFeatureDetector.cpp b/common/src/utils/ODFeatureDetector.cpp index d5197264..7f49d7e2 100644 --- a/common/src/utils/ODFeatureDetector.cpp +++ b/common/src/utils/ODFeatureDetector.cpp @@ -33,6 +33,11 @@ namespace od } } + ODFeatureDetector::ODFeatureDetector(const std::string & type) + { + ODFeatureDetector(string2FeatureType(type)); + } + void ODFeatureDetector::computeKeypointsAndDescriptors(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) { feature_detector_->computeKeypointsAndDescriptors(image, descriptors,keypoints); From ef24a46dd3acc25751563694c070e5bb4a34ddb5 Mon Sep 17 00:00:00 2001 From: giacomo <giacomo.dabisias@gmail.com> Date: Wed, 20 Jul 2016 20:07:13 +0200 Subject: [PATCH 50/79] fixed switched gpu parameters in cascade detector and removed createthred in viewer since it was crashing --- common/src/utils/ODViewer.cpp | 4 ++-- detectors/src/global2D/CMakeLists.txt | 2 -- detectors/src/global2D/detection/ODCascadeDetector.cpp | 10 ++++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/common/src/utils/ODViewer.cpp b/common/src/utils/ODViewer.cpp index b36ecb81..7bc722a4 100644 --- a/common/src/utils/ODViewer.cpp +++ b/common/src/utils/ODViewer.cpp @@ -29,7 +29,7 @@ namespace od { status_ = CVMAT; mat_window_name_ = window_name; mat_ = make_shared<cv::Mat>(to_display); - cvStartWindowThread(); + //cvStartWindowThread(); cvNamedWindow(mat_window_name_.c_str(), CV_WINDOW_AUTOSIZE); } @@ -65,7 +65,7 @@ namespace od { status_ = CVMAT; mat_window_name_ = window_name; mat_ = make_shared<cv::Mat>(); - cvStartWindowThread(); + //cvStartWindowThread(); cvNamedWindow(mat_window_name_.c_str(), CV_WINDOW_AUTOSIZE); } diff --git a/detectors/src/global2D/CMakeLists.txt b/detectors/src/global2D/CMakeLists.txt index 71c7b1ae..28abd408 100644 --- a/detectors/src/global2D/CMakeLists.txt +++ b/detectors/src/global2D/CMakeLists.txt @@ -15,8 +15,6 @@ if(BUILD_GLOBAL_2D_DETECTION) ) include_directories(${DETECTORS_INCLUDE_DIR}) - include_directories(${GPU_DETECTORS_INCLUDE_DIRS}) - message("DIR " ${GPU_DETECTORS_INCLUDE_DIRS}) include_directories(${COMMON_INCLUDE_DIR}) include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) include_directories(${PCL_INCLUDE_DIRS}) diff --git a/detectors/src/global2D/detection/ODCascadeDetector.cpp b/detectors/src/global2D/detection/ODCascadeDetector.cpp index 65cf20b8..7030f8f5 100644 --- a/detectors/src/global2D/detection/ODCascadeDetector.cpp +++ b/detectors/src/global2D/detection/ODCascadeDetector.cpp @@ -39,11 +39,12 @@ namespace od const cv::Size & min_size, const cv::Size & max_size) { if(gpu){ + std::cout << "creating gpu cascade detector" << std::endl; #if WITH_GPU #ifdef WITH_BOOST_SHARED_PTR - cascade_detector_ = shared_ptr<ODCascadeDetectorImpl>(new ODCascadeDetectorImpl(trainer, trained_data_location, scale_factor, min_neighbors, flags, min_size, max_size)); + cascade_detector_ = shared_ptr<od::gpu::g2d::ODCascadeDetectorImpl>(new od::gpu::g2d::ODCascadeDetectorImpl(trainer, trained_data_location, scale_factor, min_neighbors, flags, min_size, max_size)); #else - cascade_detector_ = make_shared<ODCascadeDetectorImpl>(trainer, trained_data_location, scale_factor, min_neighbors, flags, min_size, max_size); + cascade_detector_ = make_shared<od::gpu::g2d::ODCascadeDetectorImpl>(trainer, trained_data_location, scale_factor, min_neighbors, flags, min_size, max_size); #endif #else std::cout << "Error !! GPU CascadeDetector has not been compiled. Recompile with WITH_GPU." << std::endl; @@ -51,10 +52,11 @@ namespace od #endif } else { + std::cout << "creating cpu cascade detector" << std::endl; #ifdef WITH_BOOST_SHARED_PTR - cascade_detector_ = shared_ptr<od::gpu::g2d::ODCascadeDetectorImpl>(new od::gpu::g2d::ODCascadeDetectorImpl(trainer, trained_data_location, scale_factor, min_neighbors, flags, min_size, max_size)); + cascade_detector_ = shared_ptr<ODCascadeDetectorImpl>(new ODCascadeDetectorImpl(trainer, trained_data_location, scale_factor, min_neighbors, flags, min_size, max_size)); #else - cascade_detector_ = make_shared<od::gpu::g2d::ODCascadeDetectorImpl>(trainer, trained_data_location, scale_factor, min_neighbors, flags, min_size, max_size); + cascade_detector_ = make_shared<ODCascadeDetectorImpl>(trainer, trained_data_location, scale_factor, min_neighbors, flags, min_size, max_size); #endif } } From a4618f92e0d7e27dc9e3d949786a3632e49ca0af Mon Sep 17 00:00:00 2001 From: giacomo <giacomo.dabisias@gmail.com> Date: Thu, 21 Jul 2016 12:27:14 +0200 Subject: [PATCH 51/79] fixes cmake options, adds options file, adds warning option, adds svlight as optional --- 3rdparty/CMakeLists.txt | 4 +- 3rdparty/svmlightlib | 2 +- CMakeLists.txt | 33 +------ cmake/ODDependency.cmake | 6 +- cmake/ODOptions.cmake | 42 +++++++++ common/CMakeLists.txt | 11 ++- .../global2D/detection/ODCascadeDetector.h | 3 +- .../detection/ODCascadeDetectorImpl.h | 1 - .../detection/ODCascadeDetectorInterface.h | 31 ++++++- detectors/src/global2D/CMakeLists.txt | 16 +++- detectors/src/global3D/CMakeLists.txt | 6 +- examples/objectdetector/CMakeLists.txt | 90 +++++++++++-------- 12 files changed, 161 insertions(+), 84 deletions(-) create mode 100644 cmake/ODOptions.cmake diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 094ec4d6..e7f0b2a3 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -1,7 +1,9 @@ #add mandatory 3rdparty libraries add_subdirectory(pugixml_build) -add_subdirectory(svmlightlib) +if(WITH_SVMLIGHT) + add_subdirectory(svmlightlib) +endif() if(WITH_GPU) add_subdirectory(SiftGPU) diff --git a/3rdparty/svmlightlib b/3rdparty/svmlightlib index c8297570..b1ff32b8 160000 --- a/3rdparty/svmlightlib +++ b/3rdparty/svmlightlib @@ -1 +1 @@ -Subproject commit c82975709dc5237718e94d0859cbf737a13e4889 +Subproject commit b1ff32b8cd68570c33ceee63e464a8773adb6413 diff --git a/CMakeLists.txt b/CMakeLists.txt index 00b96957..5edf3c21 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,17 +1,6 @@ cmake_minimum_required(VERSION 2.8) project(OpenDetection) - # Set correct flags depending on the OS and compiler -if(WIN32) - if(MSVC) - set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} /SUBSYSTEM:WINDOWS /DNOMINMAX") - elseif(CMAKE_COMPILER_IS_GNUCXX) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mwindows -std=c++11 -Wall") - endif() -elseif(UNIX) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall") -endif() - # Initialize variables set(OD_SOURCE_DIR ${OpenDetection_SOURCE_DIR}) set(OD_BINARY_DIR ${OpenDetection_BINARY_DIR}) @@ -25,6 +14,7 @@ set(OD_VERSION ${OD_MAJOR_VERSION}.${OD_MINOR_VERSION}) set(OD_VERSION_DETAILED ${OD_MAJOR_VERSION}.${OD_MINOR_VERSION}.${OD_BUILD_VERSION}) # Set up macros and configurations +include(${OD_CMAKE_DIR}/ODOptions.cmake) include(${OD_CMAKE_DIR}/ODTargets.cmake) include(${OD_CMAKE_DIR}/ODDependency.cmake) include(${OD_CMAKE_DIR}/ODMacros.cmake) @@ -35,27 +25,6 @@ configure_file(${OD_CMAKE_DIR}/ODVersion.h.in ${OD_BINARY_DIR}/generated/od_vers install(FILES ${OD_BINARY_DIR}/generated/od_version.h DESTINATION ${OD_INSTALL_INCLUDE_DIR}) configure_file(${OD_CMAKE_DIR}/ODConfig.cmake.in ${OD_BINARY_DIR}/FindOD.cmake) -# Optional parameters -option(WITH_DOCUMENTATION "Build the OD documentation" ON) -option(WITH_GPU "Build GPU modules" ON) -option(WITH_EXAMPLES "Build examples" OFF) -option(WITH_BOOST_SHARED_PTR "use boost::shared_ptr instead of std::shared_ptr" ON) -option(BUILD_GLOBAL_2D_DETECTION "build global 2D detection" ON) -option(BUILD_GLOBAL_3D_DETECTION "build global 3D detection" ON) -option(BUILD_LOCAL_2D_DETECTION "build local 2D detection" ON) -option(BUILD_MISC_DETECTION "build miscellaneous detection" ON) - -if(WITH_BOOST_SHARED_PTR) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWITH_BOOST_SHARED_PTR") -endif() - -if(WITH_GPU) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWITH_GPU") - # Needed by od_common. There is a circular dependency between od_common and od_gpu_common include dirs. - include_directories(GPU_COMMON_INCLUDE_DIRS ${OD_SOURCE_DIR}/gpu/common/include) - include_directories(GPU_DETECTORS_INCLUDE_DIRS ${OD_SOURCE_DIR}/gpu/detectors/include) -endif() - # Set modules set(OD_MODULES_NAMES 3rdparty common "gpu/common" detectors "gpu/detectors" doc examples) set(OD_MODULES_DIRS ${OD_MODULES_NAMES}) diff --git a/cmake/ODDependency.cmake b/cmake/ODDependency.cmake index 4ced772c..5e857b3c 100644 --- a/cmake/ODDependency.cmake +++ b/cmake/ODDependency.cmake @@ -1,6 +1,8 @@ find_package(PCL REQUIRED) find_package(OpenCV 3 REQUIRED) find_package(VTK REQUIRED) -find_package(Eigen REQUIRED) find_package(Boost 1.40 COMPONENTS program_options REQUIRED) -find_package(Glew REQUIRED) \ No newline at end of file + +# Needed by od_common. There is a circular dependency between od_common and od_gpu_common include dirs. +include_directories(GPU_COMMON_INCLUDE_DIRS ${OD_SOURCE_DIR}/gpu/common/include) +include_directories(GPU_DETECTORS_INCLUDE_DIRS ${OD_SOURCE_DIR}/gpu/detectors/include) \ No newline at end of file diff --git a/cmake/ODOptions.cmake b/cmake/ODOptions.cmake new file mode 100644 index 00000000..96c55d3f --- /dev/null +++ b/cmake/ODOptions.cmake @@ -0,0 +1,42 @@ +# Set correct flags depending on the OS and compiler +if(WIN32) + if(MSVC) + set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} /SUBSYSTEM:WINDOWS /DNOMINMAX") + elseif(CMAKE_COMPILER_IS_GNUCXX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mwindows -std=c++11") + endif() +elseif(UNIX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") +endif() + +# Optional parameters +option(WITH_WARNINGS "Add build warnings" OFF) +option(WITH_DOCUMENTATION "Build the OD documentation" ON) +option(WITH_GPU "Build GPU modules" ON) +option(WITH_EXAMPLES "Build examples" OFF) +option(WITH_SVMLIGHT "Build with svmlight support" ON) +option(WITH_BOOST_SHARED_PTR "use boost::shared_ptr instead of std::shared_ptr" ON) +option(BUILD_GLOBAL_2D_DETECTION "build global 2D detection" ON) +option(BUILD_GLOBAL_3D_DETECTION "build global 3D detection" ON) +option(BUILD_LOCAL_2D_DETECTION "build local 2D detection" ON) +option(BUILD_MISC_DETECTION "build miscellaneous detection" ON) + +if(WITH_WARNINGS) + if(WIN32) + if(MSVC) + set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} /WALL") + elseif(CMAKE_COMPILER_IS_GNUCXX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") + endif() + elseif(UNIX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") + endif() +endif() + +if(WITH_BOOST_SHARED_PTR) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWITH_BOOST_SHARED_PTR") +endif() + +if(WITH_GPU) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWITH_GPU") +endif() diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index dcfbf3fc..89ffd8d1 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -14,23 +14,26 @@ if(WITH_GPU) include_directories(${GPU_COMMON_INCLUDE_DIR}) endif() -set(SUBSYS_DEPS ${SUBSYS_DEPS} svmlight) - set(SOURCES "src/pipeline/ODObjectDetector.cpp" "src/pipeline/ODScene.cpp" "src/pipeline/ODDetection.cpp" "src/utils/ODUtils.cpp" "src/utils/ODFeatureDetector2D.cpp" - "src/bindings/ODSvmlight.cpp" "src/utils/ODViewer.cpp" "src/utils/ODFeatureDetector.cpp" "src/utils/ODFeatureDetectorInterface.cpp" ) +if(WITH_SVMLIGHT) + set(SOURCES ${SOURCES} "src/bindings/ODSvmlight.cpp") + include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) + set(SUBSYS_DEPS ${SUBSYS_DEPS} svmlight) +endif() + + include_directories(${COMMON_INCLUDE_DIR}) include_directories(${COMMON_IMPL_DIR}) -include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) include_directories(${PCL_INCLUDE_DIRS}) OD_ADD_LIBRARY(${SUBSYS_NAME} SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) diff --git a/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h b/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h index 30afeacf..5d1a7d2c 100644 --- a/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h +++ b/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h @@ -30,10 +30,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/common/pipeline/ODDetector.h" #include "od/common/pipeline/ODScene.h" #include "od/common/utils/ODUtils.h" -#include "od/common/utils/ODFeatureDetector2D.h" #include "od/detectors/global2D/detection/ODCascadeDetectorInterface.h" -#include "od/gpu/detectors/global2D/detection/ODCascadeDetectorImpl.h" #include "od/detectors/global2D/detection/ODCascadeDetectorImpl.h" +#include "od/gpu/detectors/global2D/detection/ODCascadeDetectorImpl.h" #include <opencv2/opencv.hpp> diff --git a/detectors/include/od/detectors/global2D/detection/ODCascadeDetectorImpl.h b/detectors/include/od/detectors/global2D/detection/ODCascadeDetectorImpl.h index f38a1014..29570185 100644 --- a/detectors/include/od/detectors/global2D/detection/ODCascadeDetectorImpl.h +++ b/detectors/include/od/detectors/global2D/detection/ODCascadeDetectorImpl.h @@ -30,7 +30,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/common/pipeline/ODDetector.h" #include "od/common/pipeline/ODScene.h" #include "od/common/utils/ODUtils.h" -#include "od/common/utils/ODFeatureDetector2D.h" #include "od/detectors/global2D/detection/ODCascadeDetectorInterface.h" #include <opencv2/opencv.hpp> diff --git a/detectors/include/od/detectors/global2D/detection/ODCascadeDetectorInterface.h b/detectors/include/od/detectors/global2D/detection/ODCascadeDetectorInterface.h index 994e99ce..98e5b43e 100644 --- a/detectors/include/od/detectors/global2D/detection/ODCascadeDetectorInterface.h +++ b/detectors/include/od/detectors/global2D/detection/ODCascadeDetectorInterface.h @@ -1,5 +1,34 @@ +/* +Copyright (c) 2015, Kripasindhu Sarkar +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions 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. + * Neither the name of the copyright holder(s) nor the + names of its contributors may 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 COPYRIGHT HOLDER 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. +*/// +// Created by Giacomo Dabisias on 21.07.16. +// #pragma once - +#include <opencv2/opencv.hpp> +#include "od/common/utils/ODShared_pointers.h" namespace od { class ODCascadeDetectorInterface diff --git a/detectors/src/global2D/CMakeLists.txt b/detectors/src/global2D/CMakeLists.txt index 28abd408..30a2db1f 100644 --- a/detectors/src/global2D/CMakeLists.txt +++ b/detectors/src/global2D/CMakeLists.txt @@ -2,21 +2,29 @@ set(SUBSYS_NAME global_image_detector) set(LIB_NAME od_${SUBSYS_NAME}) set(SUBSYS_DESC "global feature based detection in 2D images") -set(SUBSYS_DEPS od_common svmlight od_gpu_global_image_detector) +set(SUBSYS_DEPS od_common) if(BUILD_GLOBAL_2D_DETECTION) + if(WITH_GPU) + set(SUBSYS_DEPS ${SUBSYS_DEPS} od_gpu_global_image_detector) + endif() + set(SOURCES "ODFaceRecognizer.cpp" "detection/ODCascadeDetector.cpp" "detection/ODCascadeDetectorImpl.cpp" - "detection/ODHOGDetector.cpp" - "training/ODHOGTrainer.cpp" + ) + + if(WITH_SVMLIGHT) + set(SOURCES ${SOURCES} "training/ODHOGTrainer.cpp" "detection/ODHOGDetector.cpp") + include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) + set(SUBSYS_DEPS ${SUBSYS_DEPS} svmlight) + endif() include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) - include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) include_directories(${PCL_INCLUDE_DIRS}) OD_ADD_LIBRARY("${SUBSYS_NAME}" SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) diff --git a/detectors/src/global3D/CMakeLists.txt b/detectors/src/global3D/CMakeLists.txt index 73db4bb3..07156136 100644 --- a/detectors/src/global3D/CMakeLists.txt +++ b/detectors/src/global3D/CMakeLists.txt @@ -1,10 +1,14 @@ set(SUBSYS_NAME pointcloud_global_detector) set(LIB_NAME od_${SUBSYS_NAME}) set(SUBSYS_DESC "The global detector for point clouds") -set(SUBSYS_DEPS od_common siftgpu) +set(SUBSYS_DEPS od_common) if(BUILD_GLOBAL_3D_DETECTION) + if(WITH_GPU) + set(SUBSYS_DEPS ${SUBSYS_DEPS} siftgpu) + endif() + set(SOURCES "ODPointCloudGlobalMatching.cpp" "training/ODCADDetectTrainer3DGlobal.cpp" diff --git a/examples/objectdetector/CMakeLists.txt b/examples/objectdetector/CMakeLists.txt index bd3d7a3e..f9399fd6 100644 --- a/examples/objectdetector/CMakeLists.txt +++ b/examples/objectdetector/CMakeLists.txt @@ -39,14 +39,18 @@ endif(image_recognition_example) option(hog_train_example "Build the hog train example" ON) if(hog_train_example) if(TARGET od_global_image_detector) + if(WITH_SVMLIGHT) - include_directories(${DETECTORS_INCLUDE_DIR}) - include_directories(${COMMON_INCLUDE_DIR}) - include_directories(${COMMON_IMPL_DIR}) + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) - OD_ADD_EXAMPLE(od_hog_train - FILES od_hog_train.cpp - LINK_WITH od_global_image_detector) + OD_ADD_EXAMPLE(od_hog_train + FILES od_hog_train.cpp + LINK_WITH od_global_image_detector) + else() + message("!!! built WITH_SVMLIGHT to enable hog_train_example") + endif() else() message("!!! hog_train_example is set to on but BUILD_GLOBAL_2D_DETECTION is necessary to build hog_train_example") endif(TARGET od_global_image_detector) @@ -56,15 +60,19 @@ endif(hog_train_example) option(image_hog_files_example "Build the hog image example" ON) if(image_hog_files_example) if(TARGET od_global_image_detector) + if(WITH_SVMLIGHT) - include_directories(${DETECTORS_INCLUDE_DIR}) - include_directories(${COMMON_INCLUDE_DIR}) - include_directories(${COMMON_IMPL_DIR}) - include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) + include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) - OD_ADD_EXAMPLE(od_image_hog_files - FILES od_image_hog_files.cpp - LINK_WITH od_global_image_detector od_gpu_global_image_detector) + OD_ADD_EXAMPLE(od_image_hog_files + FILES od_image_hog_files.cpp + LINK_WITH od_global_image_detector od_gpu_global_image_detector) + else() + message("!!! built WITH_SVMLIGHT to enable image_hog_files_example") + endif() else() message("!!! image_hog_files_example is set to on but BUILD_GLOBAL_2D_DETECTION is necessary to build image_hog_files_example") endif(TARGET od_global_image_detector) @@ -122,33 +130,41 @@ endif(face_detection_cam_example) option(image_customhog_example "Build the custom hog example" ON) if(image_customhog_example) if(TARGET od_global_image_detector) + if(WITH_SVMLIGHT) - include_directories(${DETECTORS_INCLUDE_DIR}) - include_directories(${COMMON_INCLUDE_DIR}) - include_directories(${COMMON_IMPL_DIR}) + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) - OD_ADD_EXAMPLE(od_image_customhog - FILES od_image_customhog.cpp - LINK_WITH od_global_image_detector) + OD_ADD_EXAMPLE(od_image_customhog + FILES od_image_customhog.cpp + LINK_WITH od_global_image_detector) else() - message("!!! image_customhog_example is set to on but BUILD_GLOBAL_2D_DETECTION is necessary to build image_customhog_example") - endif(TARGET od_global_image_detector) + message("!!! built WITH_SVMLIGHT to enable image_customhog_example") + endif() + else() + message("!!! image_customhog_example is set to on but BUILD_GLOBAL_2D_DETECTION is necessary to build image_customhog_example") + endif(TARGET od_global_image_detector) endif(image_customhog_example) ################################################################################## option(multialgo_files_example "Build the multialgo_files example" ON) if(multialgo_files_example) if(TARGET od_misc_detector) + if(WITH_SVMLIGHT) - include_directories(${DETECTORS_INCLUDE_DIR}) - include_directories(${DETECTORS_IMPL_DIR}) - include_directories(${COMMON_INCLUDE_DIR}) - include_directories(${COMMON_IMPL_DIR}) - include_directories(${OpenCV_INCLUDE_DIRS}) + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${DETECTORS_IMPL_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) + include_directories(${OpenCV_INCLUDE_DIRS}) - OD_ADD_EXAMPLE(od_multialgo_files - FILES od_multialgo_files.cpp - LINK_WITH od_misc_detector) + OD_ADD_EXAMPLE(od_multialgo_files + FILES od_multialgo_files.cpp + LINK_WITH od_misc_detector) + else() + message("!!! built WITH_SVMLIGHT to enable multialgo_files_example") + endif() else() message("!!! multialgo_files_example is set to on but BUILD_MISC_DETECTION is necessary to build multialgo_files_example") endif(TARGET od_misc_detector) @@ -158,14 +174,18 @@ endif(multialgo_files_example) option(multialgo_pc_example "Build the multialgo_pc example" ON) if(multialgo_pc_example) if(TARGET od_misc_detector) + if(WITH_SVMLIGHT) - include_directories(${DETECTORS_INCLUDE_DIR}) - include_directories(${COMMON_INCLUDE_DIR}) - include_directories(${COMMON_IMPL_DIR}) + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) - OD_ADD_EXAMPLE(od_multialgo_pc - FILES od_multialgo_pc.cpp - LINK_WITH od_misc_detector) + OD_ADD_EXAMPLE(od_multialgo_pc + FILES od_multialgo_pc.cpp + LINK_WITH od_misc_detector) + else() + message("!!! built WITH_SVMLIGHT to enable multialgo_pc_example") + endif() else() message("!!! multialgo_pc_example is set to on but BUILD_MISC_DETECTION is necessary to build multialgo_pc_example") endif(TARGET od_misc_detector) From a832c4bd8a160f0ea6f4f58e7e531d89884955c9 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <giacomo.dabisias@gmail.com> Date: Thu, 21 Jul 2016 12:39:21 +0200 Subject: [PATCH 52/79] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 775b22fe..fd7f7f58 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ OpenDetection ============= OpenDetection is a standalone open source project for object detection and recognition in images and 3D point clouds. -To clone the dependencies (SiftGPU and pugixml) clone with --recursive flag or pull submodules with ``git submodule init && git submodule update`` . +To clone the dependencies (SiftGPU and pugixml) clone with --recursive flag or pull submodules with ``git submodule init && git submodule update && git submodule foreach git pull origin master`` . Website - http://opendetection.com or http://krips89.github.io/opendetection_docs From 8ac572978ab8061a4026cda8ccd2728827418430 Mon Sep 17 00:00:00 2001 From: giacomo <giacomo.dabisias@gmail.com> Date: Thu, 21 Jul 2016 12:52:14 +0200 Subject: [PATCH 53/79] fixes glob names --- cmake/ODOptions.cmake | 1 + common/include/od/common/utils/ODFrameGenerator.h | 2 +- common/include/od/common/utils/ODUtils.h | 2 +- common/src/utils/ODUtils.cpp | 4 ++-- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cmake/ODOptions.cmake b/cmake/ODOptions.cmake index 96c55d3f..ccb59afb 100644 --- a/cmake/ODOptions.cmake +++ b/cmake/ODOptions.cmake @@ -40,3 +40,4 @@ endif() if(WITH_GPU) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWITH_GPU") endif() + diff --git a/common/include/od/common/utils/ODFrameGenerator.h b/common/include/od/common/utils/ODFrameGenerator.h index 9d0be708..f17f39ad 100644 --- a/common/include/od/common/utils/ODFrameGenerator.h +++ b/common/include/od/common/utils/ODFrameGenerator.h @@ -53,7 +53,7 @@ namespace od public: ODFrameGenerator(const std::string & input = std::string("")) { - file_list_ = myglob(input); + file_list_ = od_glob(input); curr_image_ = -1; exhausted_ = false; } diff --git a/common/include/od/common/utils/ODUtils.h b/common/include/od/common/utils/ODUtils.h index 5b496e5a..6e1de376 100644 --- a/common/include/od/common/utils/ODUtils.h +++ b/common/include/od/common/utils/ODUtils.h @@ -40,7 +40,7 @@ namespace od { //TODO REMOVE AND SUBSTITUTE USING BOOST FILESYSTEM - std::vector<std::string> myglob(const std::string & pat); + std::vector<std::string> od_glob(const std::string & pat); void normL2(cv::Mat &descriptors); diff --git a/common/src/utils/ODUtils.cpp b/common/src/utils/ODUtils.cpp index 260d6141..a045316c 100644 --- a/common/src/utils/ODUtils.cpp +++ b/common/src/utils/ODUtils.cpp @@ -9,7 +9,7 @@ namespace od #ifdef WIN32 -std::vector<std::string> myglob(const std::string & pat) +std::vector<std::string> od_glob(const std::string & pat) { HANDLE hFind; WIN32_FIND_DATA data; @@ -26,7 +26,7 @@ std::vector<std::string> myglob(const std::string & pat) return ret; } #else - std::vector<std::string> myglob(const std::string & pat) + std::vector<std::string> od_glob(const std::string & pat) { glob_t glob_result; glob(pat.c_str(), GLOB_TILDE, nullptr, &glob_result); From 5e9a46a08bde6ab7bf35a00f94005e6449032f20 Mon Sep 17 00:00:00 2001 From: giacomo <giacomo.dabisias@gmail.com> Date: Fri, 22 Jul 2016 15:35:39 +0200 Subject: [PATCH 54/79] fixes cmake install --- CMakeLists.txt | 2 +- cmake/ODOptions.cmake | 2 + cmake/modules/FindEigen.cmake | 50 ------------------- .../gsoc2016_blog_giacomo.md | 42 ++++++++++++++++ .../visualization/ODVisualizationTest.cpp | 1 - 5 files changed, 45 insertions(+), 52 deletions(-) delete mode 100644 cmake/modules/FindEigen.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 5edf3c21..22f5be81 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,7 +23,6 @@ include(${OD_CMAKE_DIR}/ODDeb.cmake) # Prepare configuration files configure_file(${OD_CMAKE_DIR}/ODVersion.h.in ${OD_BINARY_DIR}/generated/od_version.h) install(FILES ${OD_BINARY_DIR}/generated/od_version.h DESTINATION ${OD_INSTALL_INCLUDE_DIR}) -configure_file(${OD_CMAKE_DIR}/ODConfig.cmake.in ${OD_BINARY_DIR}/FindOD.cmake) # Set modules set(OD_MODULES_NAMES 3rdparty common "gpu/common" detectors "gpu/detectors" doc examples) @@ -36,6 +35,7 @@ endforeach(subdir) # Install the FindPackage configuration file get_property(OD_INSTALLED_LIBRARIES GLOBAL PROPERTY OD_INSTALLED_LIBRARIES) +configure_file(${OD_CMAKE_DIR}/ODConfig.cmake.in ${OD_BINARY_DIR}/FindOD.cmake) install(FILES ${OD_BINARY_DIR}/FindOD.cmake DESTINATION ${OD_CMAKE_INSTALL_DIR}) # uninstall target diff --git a/cmake/ODOptions.cmake b/cmake/ODOptions.cmake index ccb59afb..123bd300 100644 --- a/cmake/ODOptions.cmake +++ b/cmake/ODOptions.cmake @@ -9,6 +9,8 @@ elseif(UNIX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") endif() +set(CMAKE_BUILD_TYPE Release) + # Optional parameters option(WITH_WARNINGS "Add build warnings" OFF) option(WITH_DOCUMENTATION "Build the OD documentation" ON) diff --git a/cmake/modules/FindEigen.cmake b/cmake/modules/FindEigen.cmake deleted file mode 100644 index 5819a5c7..00000000 --- a/cmake/modules/FindEigen.cmake +++ /dev/null @@ -1,50 +0,0 @@ -############################################################################### -# Find Eigen3 -# -# This sets the following variables: -# EIGEN_FOUND - True if Eigen was found. -# EIGEN_INCLUDE_DIRS - Directories containing the Eigen include files. -# EIGEN_DEFINITIONS - Compiler flags for Eigen. -# EIGEN_VERSION - Package version - -find_package(PkgConfig QUIET) -pkg_check_modules(PC_EIGEN eigen3) -set(EIGEN_DEFINITIONS ${PC_EIGEN_CFLAGS_OTHER}) - -if(CMAKE_SYSTEM_NAME STREQUAL Linux) - set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} /usr /usr/local) -endif(CMAKE_SYSTEM_NAME STREQUAL Linux) -if(APPLE) - list(APPEND CMAKE_INCLUDE_PATH /opt/local) - set(CMAKE_FIND_FRAMEWORK NEVER) -endif() - -find_path(EIGEN_INCLUDE_DIR Eigen/Core - HINTS ${PC_EIGEN_INCLUDEDIR} ${PC_EIGEN_INCLUDE_DIRS} "${EIGEN_ROOT}" "$ENV{EIGEN_ROOT}" - PATHS "$ENV{PROGRAMFILES}/Eigen" "$ENV{PROGRAMW6432}/Eigen" - "$ENV{PROGRAMFILES}/Eigen 3.0.0" "$ENV{PROGRAMW6432}/Eigen 3.0.0" - PATH_SUFFIXES eigen3 include/eigen3 include) - -if(EIGEN_INCLUDE_DIR) - file(READ "${EIGEN_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h" _eigen_version_header) - - string(REGEX MATCH "define[ \t]+EIGEN_WORLD_VERSION[ \t]+([0-9]+)" _eigen_world_version_match "${_eigen_version_header}") - set(EIGEN_WORLD_VERSION "${CMAKE_MATCH_1}") - string(REGEX MATCH "define[ \t]+EIGEN_MAJOR_VERSION[ \t]+([0-9]+)" _eigen_major_version_match "${_eigen_version_header}") - set(EIGEN_MAJOR_VERSION "${CMAKE_MATCH_1}") - string(REGEX MATCH "define[ \t]+EIGEN_MINOR_VERSION[ \t]+([0-9]+)" _eigen_minor_version_match "${_eigen_version_header}") - set(EIGEN_MINOR_VERSION "${CMAKE_MATCH_1}") - set(EIGEN_VERSION ${EIGEN_WORLD_VERSION}.${EIGEN_MAJOR_VERSION}.${EIGEN_MINOR_VERSION}) -endif(EIGEN_INCLUDE_DIR) - -set(EIGEN_INCLUDE_DIRS ${EIGEN_INCLUDE_DIR}) -set(CMAKE_FIND_FRAMEWORK) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Eigen DEFAULT_MSG EIGEN_INCLUDE_DIR) - -mark_as_advanced(EIGEN_INCLUDE_DIR) - -if(EIGEN_FOUND) - message(STATUS "Eigen found (include: ${EIGEN_INCLUDE_DIRS}, version: ${EIGEN_VERSION})") -endif(EIGEN_FOUND) diff --git a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md index b3a971f8..c876d08a 100644 --- a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md +++ b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md @@ -324,5 +324,47 @@ The same structure has been adde to all examples so that the correct examples ar I also built the system on lniux 16.04 with gcc 5.3; I had some initial issues but also those were fixed. I will make a blogpost about them next week since some errors where quite interesting. +##Modular build 2 and new interface for GPU 22/07/15## +After discussing with my mentor, I did some changes to the code. +The first new feature is that svmlight is optional, even if it comes as subdependency with the library for free. I added an **WITH_SVMLIGHT** option in the CMake to make it optional. Automatically all the dependand source files will be excluded (they should be the one using hog, the miscdetector and the depending examples). + +The second change is that I created a separate option Cmake file to set and create all options and I added a new option to show or remove compilation warnings. + +The third change is a major change composed by two parts. The first one involves changing the interface of the **ODCascadeDetector** to have a simple interface for the users. I changed it by creating an interface called **ODCascadeDetector** which derives from **ODDetector2D** so that we can use it in all the containers. This class contains a shared pointer of a common interface, called **ODCascadeDetectorInterface**. Both the GPU and the CPU version of the **ODCascadeDetector** are called the same, **ODCascadeDetectorImpl**, but are in different namespaces and modules. This way I maintained the code separated and easy to maintain. In the **ODCascadeDetector** class, the constructor checks the first parameter and builds accordingly the correct object. Also there is a **Pragma** to check that the lirbary was compiled with GPU support. + +@code + + ODCascadeDetector::ODCascadeDetector(bool gpu, const std::string & trainer, const std::string & trained_data_location, double scale_factor, int min_neighbors, int flags, + const cv::Size & min_size, const cv::Size & max_size) + { + if(gpu){ + std::cout << "creating gpu cascade detector" << std::endl; +#if WITH_GPU + #ifdef WITH_BOOST_SHARED_PTR + cascade_detector_ = shared_ptr<od::gpu::g2d::ODCascadeDetectorImpl>(new od::gpu::g2d::ODCascadeDetectorImpl(trainer, trained_data_location, scale_factor, min_neighbors, flags, min_size, max_size)); + #else + cascade_detector_ = make_shared<od::gpu::g2d::ODCascadeDetectorImpl>(trainer, trained_data_location, scale_factor, min_neighbors, flags, min_size, max_size); + #endif +#else + std::cout << "Error !! GPU CascadeDetector has not been compiled. Recompile with WITH_GPU." << std::endl; + exit(-1); +#endif + } + else { + std::cout << "creating cpu cascade detector" << std::endl; +#ifdef WITH_BOOST_SHARED_PTR + cascade_detector_ = shared_ptr<ODCascadeDetectorImpl>(new ODCascadeDetectorImpl(trainer, trained_data_location, scale_factor, min_neighbors, flags, min_size, max_size)); +#else + cascade_detector_ = make_shared<ODCascadeDetectorImpl>(trainer, trained_data_location, scale_factor, min_neighbors, flags, min_size, max_size); +#endif + } + } + +@endcode + +The second main change was to create the same interface for the **ODFeatureDetector2D** which could create gpu detectors or cpu ones. I did the same interface as before creating **ODFeatureDetector**, **ODFeatureDetector2D**, **ODFeatureDetectorInterface**. The constructor switches the passed parameter and based on the type allocates the correct feature detector, cpu or gpu. Also the gpu part is in the gpu module to maintain the advantages described before. +The only small drawback of this structure is that the common module and the gpu_common module have a circular dependency on the include files, so we have to set the **include_directories** statment outside of the two modules. I do this in the **ODDependency.cmake** file. + +Obviously I had to change all the examples to adapt to this new structure, but that was not so hard, while the new interface was quite tricky to be created in a clean way. \ No newline at end of file diff --git a/examples/apps/visualization/ODVisualizationTest.cpp b/examples/apps/visualization/ODVisualizationTest.cpp index ecab4b26..66ffbadf 100644 --- a/examples/apps/visualization/ODVisualizationTest.cpp +++ b/examples/apps/visualization/ODVisualizationTest.cpp @@ -37,7 +37,6 @@ int main(int argc, char * argv[]){ viewer.spin(); } - //This will fail since test_cloud has been overwritten by test_cloud2 viewer.update(cloud, std::string("test_cloud")); while(!viewer.toStop()){ From 8589a63a3924e861336692f82a5cfe699b30da76 Mon Sep 17 00:00:00 2001 From: giacomo <giacomo.dabisias@gmail.com> Date: Fri, 22 Jul 2016 15:45:02 +0200 Subject: [PATCH 55/79] cleans cmake from unused commands and sets correctly build type --- cmake/ODOptions.cmake | 7 ++++++- cmake/modules/FindGlew.cmake | 17 ----------------- detectors/CMakeLists.txt | 1 - gpu/common/CMakeLists.txt | 1 - gpu/detectors/CMakeLists.txt | 3 --- 5 files changed, 6 insertions(+), 23 deletions(-) delete mode 100644 cmake/modules/FindGlew.cmake diff --git a/cmake/ODOptions.cmake b/cmake/ODOptions.cmake index 123bd300..e50d5e2f 100644 --- a/cmake/ODOptions.cmake +++ b/cmake/ODOptions.cmake @@ -9,7 +9,12 @@ elseif(UNIX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") endif() -set(CMAKE_BUILD_TYPE Release) +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release" CACHE STRING + "Choose the type of build, options are: Debug Release +RelWithDebInfo MinSizeRel." + FORCE) +endif(NOT CMAKE_BUILD_TYPE) # Optional parameters option(WITH_WARNINGS "Add build warnings" OFF) diff --git a/cmake/modules/FindGlew.cmake b/cmake/modules/FindGlew.cmake deleted file mode 100644 index 722da208..00000000 --- a/cmake/modules/FindGlew.cmake +++ /dev/null @@ -1,17 +0,0 @@ -# Find the GLEW library -# -# Defines: -# -# GLEW_FOUND - system has GLEW -# GLEW_INCLUDE_DIRS - the GLEW include directories -# GLEW_LIBRARY - The GLEW library -# -find_path(GLEW_INCLUDE_DIR GL/glew.h) -find_library(GLEW_LIBRARY NAMES GLEW glew32) - -set(GLEW_INCLUDE_DIRS "${GLEW_INCLUDE_DIR}") - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(GLEW DEFAULT_MSG GLEW_INCLUDE_DIR GLEW_LIBRARY) - -mark_as_advanced(GLEW_INCLUDE_DIR GLEW_LIBRARY) diff --git a/detectors/CMakeLists.txt b/detectors/CMakeLists.txt index 15a98b94..dfac6ca2 100644 --- a/detectors/CMakeLists.txt +++ b/detectors/CMakeLists.txt @@ -14,4 +14,3 @@ add_subdirectory("src/global3D") add_subdirectory("src/misc") install(DIRECTORY ${DETECTORS_INCLUDE_DIR}/od DESTINATION ${OD_INSTALL_INCLUDE_DIR}) -set(${OD_INSTALLED_LIBRARIES} PARENT_SCOPE) diff --git a/gpu/common/CMakeLists.txt b/gpu/common/CMakeLists.txt index 89e583ba..9ea05c01 100644 --- a/gpu/common/CMakeLists.txt +++ b/gpu/common/CMakeLists.txt @@ -19,5 +19,4 @@ if(WITH_GPU) OD_ADD_LIBRARY("${SUBSYS_NAME}" SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) install(DIRECTORY ${GPU_COMMON_INCLUDE_DIR}/od DESTINATION ${OD_INSTALL_INCLUDE_DIR}) - set(${OD_INSTALLED_LIBRARIES} PARENT_SCOPE) endif() diff --git a/gpu/detectors/CMakeLists.txt b/gpu/detectors/CMakeLists.txt index 3ee26013..ed593f29 100644 --- a/gpu/detectors/CMakeLists.txt +++ b/gpu/detectors/CMakeLists.txt @@ -2,12 +2,9 @@ if(WITH_GPU) set(GPU_DETECTORS_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(GPU_DETECTORS_INCLUDE_DIR ${GPU_DETECTORS_DIR}/include) - set(GPU_DETECTORS_INCLUDE_DIR ${GPU_DETECTORS_DIR}/include PARENT_SCOPE) - include_directories(${CMAKE_3RDPARTY_DIR}/SiftGPU/src/SiftGPU/) add_subdirectory("src/global2D") install(DIRECTORY ${GPU_DETECTORS_INCLUDE_DIR}/od DESTINATION ${OD_INSTALL_INCLUDE_DIR}) - set(${OD_INSTALLED_LIBRARIES} PARENT_SCOPE) endif() From 14e64dfcf721b87104877e1e24aa6110145abc72 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Tue, 26 Jul 2016 14:03:35 +0200 Subject: [PATCH 56/79] fixed opencv include dir missing if opencv is in a custom prefix --- common/CMakeLists.txt | 4 ++-- common/include/od/common/pipeline/ODScene.h | 1 + common/include/od/common/utils/ODFeatureDetector2D.h | 1 + detectors/CMakeLists.txt | 2 -- detectors/src/global2D/CMakeLists.txt | 1 + detectors/src/global3D/CMakeLists.txt | 1 + detectors/src/local2D/CMakeLists.txt | 1 + detectors/src/misc/CMakeLists.txt | 2 +- examples/apps/CMakeLists.txt | 4 +++- gpu/common/CMakeLists.txt | 1 + gpu/detectors/src/global2D/CMakeLists.txt | 1 + 11 files changed, 13 insertions(+), 6 deletions(-) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 89ffd8d1..ceaa6721 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -31,10 +31,10 @@ if(WITH_SVMLIGHT) set(SUBSYS_DEPS ${SUBSYS_DEPS} svmlight) endif() - +include_directories(${OpenCV_INCLUDE_DIRS}) include_directories(${COMMON_INCLUDE_DIR}) include_directories(${COMMON_IMPL_DIR}) -include_directories(${PCL_INCLUDE_DIRS}) +include_directories(${PCL_INCLUDE_DIRS}s) OD_ADD_LIBRARY(${SUBSYS_NAME} SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) install(DIRECTORY ${COMMON_INCLUDE_DIR}/od DESTINATION ${OD_INSTALL_INCLUDE_DIR} COMPONENT ${LIB_NAME}) diff --git a/common/include/od/common/pipeline/ODScene.h b/common/include/od/common/pipeline/ODScene.h index 93315de0..a797ba3a 100644 --- a/common/include/od/common/pipeline/ODScene.h +++ b/common/include/od/common/pipeline/ODScene.h @@ -31,6 +31,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #pragma once #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> +#include <opencv2/features2d/features2d.hpp> #include <pcl/point_cloud.h> #include <pcl/io/pcd_io.h> #include "od/common/utils/ODShared_pointers.h" diff --git a/common/include/od/common/utils/ODFeatureDetector2D.h b/common/include/od/common/utils/ODFeatureDetector2D.h index f64ae69e..fa9763c5 100644 --- a/common/include/od/common/utils/ODFeatureDetector2D.h +++ b/common/include/od/common/utils/ODFeatureDetector2D.h @@ -5,6 +5,7 @@ #include "od/common/utils/ODFeatureDetectorInterface.h" #include <opencv2/xfeatures2d.hpp> + namespace od { diff --git a/detectors/CMakeLists.txt b/detectors/CMakeLists.txt index dfac6ca2..00ad1808 100644 --- a/detectors/CMakeLists.txt +++ b/detectors/CMakeLists.txt @@ -6,8 +6,6 @@ set(DETECTORS_IMPL_DIR ${DETECTORS_DIR}/impl) set(DETECTORS_INCLUDE_DIR ${DETECTORS_DIR}/include PARENT_SCOPE) set(DETECTORS_IMPL_DIR ${DETECTORS_DIR}/impl PARENT_SCOPE) -include_directories(${CMAKE_3RDPARTY_DIR}/SiftGPU/src/SiftGPU/) - add_subdirectory("src/local2D") add_subdirectory("src/global2D") add_subdirectory("src/global3D") diff --git a/detectors/src/global2D/CMakeLists.txt b/detectors/src/global2D/CMakeLists.txt index 30a2db1f..41ab6898 100644 --- a/detectors/src/global2D/CMakeLists.txt +++ b/detectors/src/global2D/CMakeLists.txt @@ -23,6 +23,7 @@ if(BUILD_GLOBAL_2D_DETECTION) set(SUBSYS_DEPS ${SUBSYS_DEPS} svmlight) endif() + include_directories(${OpenCV_INCLUDE_DIRS}) include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) include_directories(${PCL_INCLUDE_DIRS}) diff --git a/detectors/src/global3D/CMakeLists.txt b/detectors/src/global3D/CMakeLists.txt index 07156136..9322d82c 100644 --- a/detectors/src/global3D/CMakeLists.txt +++ b/detectors/src/global3D/CMakeLists.txt @@ -15,6 +15,7 @@ if(BUILD_GLOBAL_3D_DETECTION) "detection/ODCADDetector3DGlobal.cpp" ) + include_directories(${OpenCV_INCLUDE_DIRS}) include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${DETECTORS_IMPL_DIR}) include_directories(${COMMON_INCLUDE_DIR}) diff --git a/detectors/src/local2D/CMakeLists.txt b/detectors/src/local2D/CMakeLists.txt index 74b7d819..d3ac8e16 100644 --- a/detectors/src/local2D/CMakeLists.txt +++ b/detectors/src/local2D/CMakeLists.txt @@ -21,6 +21,7 @@ if(BUILD_LOCAL_2D_DETECTION) include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${CMAKE_3RDPARTY_DIR}/pugixml/src/) + include_directories(${OpenCV_INCLUDE_DIRS}) include_directories(${COMMON_INCLUDE_DIR}) include_directories(${PCL_INCLUDE_DIRS}) diff --git a/detectors/src/misc/CMakeLists.txt b/detectors/src/misc/CMakeLists.txt index 21382de2..877cbf63 100644 --- a/detectors/src/misc/CMakeLists.txt +++ b/detectors/src/misc/CMakeLists.txt @@ -12,8 +12,8 @@ if(BUILD_MISC_DETECTION) include_directories(${DETECTORS_IMPL_DIR}) include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${OpenCV_INCLUDE_DIRS}) include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) - include_directories(${PCL_INCLUDE_DIRS}) OD_ADD_LIBRARY("${SUBSYS_NAME}" SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) diff --git a/examples/apps/CMakeLists.txt b/examples/apps/CMakeLists.txt index f6dd9993..c6ef1887 100644 --- a/examples/apps/CMakeLists.txt +++ b/examples/apps/CMakeLists.txt @@ -3,6 +3,7 @@ option(cad_recog_test_single_model "Build the cad recognition test" ON) if(cad_recog_test_single_model) if(TARGET od_local_image_detector) + include_directories(${OpenCV_INCLUDE_DIRS}) include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) include_directories(${CMAKE_3RDPARTY_DIR}/pugixml/src/) @@ -23,6 +24,7 @@ option(multihog_app "Build the multihog app" ON) if(od_multihog_app) if(TARGET od_global_image_detector) + include_directories(${OpenCV_INCLUDE_DIRS}) include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) @@ -38,7 +40,7 @@ endif(od_multihog_app) ################################################################################## option(viewer_test "Build the viewer test" ON) if(viewer_test) - + include_directories(${COMMON_IMPL_DIR}) include_directories(${COMMON_INCLUDE_DIR}) include_directories(${PCL_INCLUDE_DIRS}) diff --git a/gpu/common/CMakeLists.txt b/gpu/common/CMakeLists.txt index 9ea05c01..85aea3f2 100644 --- a/gpu/common/CMakeLists.txt +++ b/gpu/common/CMakeLists.txt @@ -14,6 +14,7 @@ if(WITH_GPU) include_directories(${GPU_COMMON_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${OpenCV_INCLUDE_DIRS}) include_directories(${CMAKE_3RDPARTY_DIR}/SiftGPU/src/SiftGPU/) OD_ADD_LIBRARY("${SUBSYS_NAME}" SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) diff --git a/gpu/detectors/src/global2D/CMakeLists.txt b/gpu/detectors/src/global2D/CMakeLists.txt index 7ea39ef6..c2858916 100644 --- a/gpu/detectors/src/global2D/CMakeLists.txt +++ b/gpu/detectors/src/global2D/CMakeLists.txt @@ -11,6 +11,7 @@ set(SOURCES include_directories(${GPU_DETECTORS_INCLUDE_DIR}) include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) +include_directories(${OpenCV_INCLUDE_DIRS}) include_directories(${PCL_INCLUDE_DIRS}) OD_ADD_LIBRARY("${SUBSYS_NAME}" SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) From d9849776b391ea919bdd617bd5b7ef3d726fcd0e Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Tue, 26 Jul 2016 16:55:44 +0200 Subject: [PATCH 57/79] fixes opencv include and adds build example --- CMakeLists.txt | 4 ++-- cmake/ODConfig.cmake.in | 13 ++++++++++++- cmake/ODOptions.cmake | 6 +++--- cmake/ODTargets.cmake | 2 +- .../od/common/utils/ODShared_pointers.h | 14 +++++++------- examples/apps/CMakeLists.txt | 15 --------------- examples/apps/buildExample/CMakeLists.txt | 18 ++++++++++++++++++ .../ODVisualizationTest.cpp | 3 +-- 8 files changed, 44 insertions(+), 31 deletions(-) create mode 100644 examples/apps/buildExample/CMakeLists.txt rename examples/apps/{visualization => buildExample}/ODVisualizationTest.cpp (90%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 22f5be81..af93c4b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,8 +35,8 @@ endforeach(subdir) # Install the FindPackage configuration file get_property(OD_INSTALLED_LIBRARIES GLOBAL PROPERTY OD_INSTALLED_LIBRARIES) -configure_file(${OD_CMAKE_DIR}/ODConfig.cmake.in ${OD_BINARY_DIR}/FindOD.cmake) -install(FILES ${OD_BINARY_DIR}/FindOD.cmake DESTINATION ${OD_CMAKE_INSTALL_DIR}) +configure_file(${OD_CMAKE_DIR}/ODConfig.cmake.in ${OD_BINARY_DIR}/ODConfig.cmake) +install(FILES ${OD_BINARY_DIR}/ODConfig.cmake DESTINATION ${OD_CMAKE_INSTALL_DIR}) # uninstall target configure_file( diff --git a/cmake/ODConfig.cmake.in b/cmake/ODConfig.cmake.in index 0a78855e..0d10f43a 100644 --- a/cmake/ODConfig.cmake.in +++ b/cmake/ODConfig.cmake.in @@ -1,3 +1,14 @@ + set(OD_INCLUDE_DIRS ${CMAKE_INSTALL_PREFIX}/${OD_INSTALL_INCLUDE_DIR}) set(OD_LIBRARY_PATH ${CMAKE_INSTALL_PREFIX}/${OD_INSTALL_LIBRARY_DIR}) -set(OD_LIBRARIES ${OD_INSTALLED_LIBRARIES}) \ No newline at end of file +set(OD_LIBRARIES ${OD_INSTALLED_LIBRARIES}) + +if(WIN32) + if(MSVC) + set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} /SUBSYSTEM:WINDOWS /DNOMINMAX") + elseif(CMAKE_COMPILER_IS_GNUCXX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mwindows -std=c++11") + endif() +elseif(UNIX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") +endif() \ No newline at end of file diff --git a/cmake/ODOptions.cmake b/cmake/ODOptions.cmake index e50d5e2f..d818a743 100644 --- a/cmake/ODOptions.cmake +++ b/cmake/ODOptions.cmake @@ -22,7 +22,7 @@ option(WITH_DOCUMENTATION "Build the OD documentation" ON) option(WITH_GPU "Build GPU modules" ON) option(WITH_EXAMPLES "Build examples" OFF) option(WITH_SVMLIGHT "Build with svmlight support" ON) -option(WITH_BOOST_SHARED_PTR "use boost::shared_ptr instead of std::shared_ptr" ON) +option(WITH_STD_SHARED_PTR "use std::shared_ptr instead of boost::shared_ptr" OFF) option(BUILD_GLOBAL_2D_DETECTION "build global 2D detection" ON) option(BUILD_GLOBAL_3D_DETECTION "build global 3D detection" ON) option(BUILD_LOCAL_2D_DETECTION "build local 2D detection" ON) @@ -40,8 +40,8 @@ if(WITH_WARNINGS) endif() endif() -if(WITH_BOOST_SHARED_PTR) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWITH_BOOST_SHARED_PTR") +if(WITH_STD_SHARED_PTR) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWITH_STD_SHARED_PTR") endif() if(WITH_GPU) diff --git a/cmake/ODTargets.cmake b/cmake/ODTargets.cmake index 26aecfb9..677a9776 100644 --- a/cmake/ODTargets.cmake +++ b/cmake/ODTargets.cmake @@ -24,7 +24,7 @@ if(NOT OD_INSTALL_DOC_DIR) endif() if(NOT OD_CMAKE_INSTALL_DIR) - set(OD_CMAKE_INSTALL_DIR lib/cmake) + set(OD_CMAKE_INSTALL_DIR lib/cmake/OD) endif() if(NOT OD_INSTALL_PACKAGE_DIR) diff --git a/common/include/od/common/utils/ODShared_pointers.h b/common/include/od/common/utils/ODShared_pointers.h index 696274fb..b66242b1 100644 --- a/common/include/od/common/utils/ODShared_pointers.h +++ b/common/include/od/common/utils/ODShared_pointers.h @@ -1,5 +1,11 @@ #pragma once -#ifdef WITH_BOOST_SHARED_PTR +#ifdef WITH_STD_SHARED_PTR + #include <memory> + using std::shared_ptr; + using std::make_shared; + using std::static_pointer_cast; + using std::dynamic_pointer_cast; +#else #include <boost/shared_ptr.hpp> #include <boost/make_shared.hpp> #include <boost/pointer_cast.hpp> @@ -7,10 +13,4 @@ using boost::make_shared; using boost::static_pointer_cast; using boost::dynamic_pointer_cast; -#else - #include <memory> - using std::shared_ptr; - using std::make_shared; - using std::static_pointer_cast; - using std::dynamic_pointer_cast; #endif \ No newline at end of file diff --git a/examples/apps/CMakeLists.txt b/examples/apps/CMakeLists.txt index c6ef1887..f554c9e8 100644 --- a/examples/apps/CMakeLists.txt +++ b/examples/apps/CMakeLists.txt @@ -1,4 +1,3 @@ - option(cad_recog_test_single_model "Build the cad recognition test" ON) if(cad_recog_test_single_model) if(TARGET od_local_image_detector) @@ -37,20 +36,6 @@ if(od_multihog_app) endif(TARGET od_global_image_detector) endif(od_multihog_app) -################################################################################## -option(viewer_test "Build the viewer test" ON) -if(viewer_test) - - include_directories(${COMMON_IMPL_DIR}) - include_directories(${COMMON_INCLUDE_DIR}) - include_directories(${PCL_INCLUDE_DIRS}) - - OD_ADD_EXAMPLE(viewer_test - FILES visualization/ODVisualizationTest.cpp - LINK_WITH od_common - ) -endif(viewer_test) - ################################################################################## set(SOURCE_FILES version/opendetection.cpp) include_directories(${OD_BINARY_DIR}/generated) diff --git a/examples/apps/buildExample/CMakeLists.txt b/examples/apps/buildExample/CMakeLists.txt new file mode 100644 index 00000000..44b24530 --- /dev/null +++ b/examples/apps/buildExample/CMakeLists.txt @@ -0,0 +1,18 @@ +# Example on how to build a project using the OpenDetection Library + +cmake_minimum_required(VERSION 2.8) +project(viewer) + +find_package(OD REQUIRED) +find_package(PCL REQUIRED) +find_package(OpenCV REQUIRED) + +include_directories(${OD_INCLUDE_DIRS}) +include_directories(${PCL_INCLUDE_DIRS}) +include_directories(${OpenCV_INCLUDE_DIRS}) + +link_directories(${OD_LIBRARY_PATH}) + +add_executable(viewer ODVisualizationTest.cpp) +target_link_libraries(viewer ${OD_LIBRARIES} ${OpenCV_LIBS} ${PCL_LIBRARIES}) + diff --git a/examples/apps/visualization/ODVisualizationTest.cpp b/examples/apps/buildExample/ODVisualizationTest.cpp similarity index 90% rename from examples/apps/visualization/ODVisualizationTest.cpp rename to examples/apps/buildExample/ODVisualizationTest.cpp index 66ffbadf..436fa046 100644 --- a/examples/apps/visualization/ODVisualizationTest.cpp +++ b/examples/apps/buildExample/ODVisualizationTest.cpp @@ -1,7 +1,6 @@ #include "od/common/utils/ODViewer.h" #include <iostream> #include <pcl/io/pcd_io.h> -#include <pcl/point_types.h> int main(int argc, char * argv[]){ @@ -24,7 +23,7 @@ int main(int argc, char * argv[]){ viewer.spin(); } - cv::Mat image = cv::imread(argv[2], CV_LOAD_IMAGE_COLOR); + cv::Mat image = cv::imread(argv[2], cv::IMREAD_COLOR); viewer.render(image, std::string("test_image")); while(!viewer.toStop()){ From 48c65c138c094f7f95abee5abb556ebf5f8c3867 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Fri, 29 Jul 2016 15:46:55 +0200 Subject: [PATCH 58/79] more files --- common/impl/od/common/utils/ODViewer.hpp | 2 +- .../include/od/common/pipeline/ODDetection.h | 92 +++++++++++++++++-- .../od/common/utils/ODFrameGenerator.h | 43 ++++++++- common/src/pipeline/ODDetection.cpp | 1 - 4 files changed, 129 insertions(+), 9 deletions(-) diff --git a/common/impl/od/common/utils/ODViewer.hpp b/common/impl/od/common/utils/ODViewer.hpp index f8bf1bd3..be04c5cf 100644 --- a/common/impl/od/common/utils/ODViewer.hpp +++ b/common/impl/od/common/utils/ODViewer.hpp @@ -2,7 +2,7 @@ #include "od/common/utils/ODViewer.h" namespace od { - + template<typename PointT> void ODViewer::render(shared_ptr<pcl::PointCloud<PointT> > to_display, const std::string & cloud_name, bool colored) { diff --git a/common/include/od/common/pipeline/ODDetection.h b/common/include/od/common/pipeline/ODDetection.h index dd5a1081..d5026d5b 100644 --- a/common/include/od/common/pipeline/ODDetection.h +++ b/common/include/od/common/pipeline/ODDetection.h @@ -47,30 +47,52 @@ namespace od * \author Kripasindhu Sarkar * */ - class ODDetection + class Detection { public: - OD_DEFINE_ENUM_WITH_STRING_CONVERSIONS(DetectionType, (OD_RECOGNITION)(OD_CLASSIFICATION)(OD_DETECTION)(OD_DETECTION_NULL)) - virtual ~ODDetection() {} + virtual ~Detection(){} - ODDetection(const DetectionType & type = OD_DETECTION_NULL, const std::string & id = std::string(""), double confidence = 1.0); + /** \brief Constructor of the Detection class. + \param type The detection type. + \param id The detection identifier. + \param confidence The detection confidence. + */ + Detection(const DetectionType & type = DETECTION_NULL, const std::string & id = std::string(""), double confidence = 1.0); + /** \brief Prints type and id of the detection. + */ void printSelf(); + /** \brief Get the type of the detection. This can be OD_RECOGNITION, OD_CLASSIFICATION, OD_DETECTION, OD_DETECTION_NULL. + \return The type of the detection. + */ const DetectionType & getType() const; + + /** \brief Set the type of the detection. This can be OD_RECOGNITION, OD_CLASSIFICATION, OD_DETECTION, OD_DETECTION_NULL. + \param id The type of the detection. + */ void setType(const DetectionType & type); + /** \brief Get the id of the detection. + \return The id of the detection. + */ const std::string & getId() const; + + /** \brief Set the id of the detection. + \param id The id of the detection. + */ void setId(const std::string & id); - /** \brief Get/Set the confidence of the detection. ODDetector can use this to provide confidence amnong several detections. + /** \brief Get the confidence of the detection. ODDetector can use this to provide confidence amnong several detections. + \return The confidence level of the detection. */ double getConfidence() const; - /** \brief Get/Set the confidence of the detection. ODDetector can use this to provide confidence amnong several detections. + /** \brief Set the confidence of the detection. ODDetector can use this to provide confidence amnong several detections. + \param confidence The confidence level of the detection. */ void setConfidence(double confidence); @@ -93,16 +115,41 @@ namespace od virtual ~ODDetection2D(){} + /** \brief Constructor of the ODDetection2D class. + \param type The detection type. + \param id The detection identifier. + \param confidence The detection confidence. + */ ODDetection2D(const DetectionType & type = OD_DETECTION_NULL, const std::string & id = std::string(""), double confidence = 1.0); + /** \brief Get the 2D location of the detection. + \return The 2D vector of the detection position. + */ const Eigen::Vector3d & getLocation() const; + /** \brief Set the 2D location of the detection. + \param location The 2D location of the detection. + */ void setLocation(const Eigen::Vector3d & location); + /** \brief Get the bounding box of the detection. + \return The bounding box if the detection in the frame. + */ const cv::Rect & getBoundingBox() const; + + /** \brief Set the bounding box of the detection. + \param bounding_box The bounding box of the detection. + */ void setBoundingBox(const cv::Rect & bounding_box); + /** \brief Get the image template concerning the detection. + \return Image template concerning the detection. + */ const cv::Mat & getMetainfoImage() const; + + /** \brief Set the image template concerning the detection. + \param metainfo_image The image template concerning the detection. + */ void setMetainfoImage(const cv::Mat & metainfo_image); Eigen::Vector3d location_2d_; @@ -122,19 +169,52 @@ namespace od virtual ~ODDetection3D(){} + /** \brief Constructor of the ODDetection3D class. + \param type The detection type. + \param id The detection identifier. + \param confidence The detection confidence. + */ ODDetection3D(const DetectionType & type = OD_DETECTION_NULL, const std::string & id = std::string(""), double confidence = 1.0); + + /** \brief Get the 3D location of the detection. + \return The 3D vector of the detection position. + */ const Eigen::Vector4d & getLocation() const; + /** \brief Set the 3D location of the detection. + \param locationThe 3D location of the detection. + */ void setLocation(const Eigen::Vector4d & location); + + /** \brief Set the 3D location of the detection. + \param location The 3D location of the detection. + */ void setLocation(const cv::Mat & location); + /** \brief Returns the pose of the detection. + \return The detection orientation. + */ const Eigen::Matrix3Xd & getPose() const; + /** \brief Set the 3D pose of the detection. + \param pose The 3D pose of the detection. + */ void setPose(const Eigen::Matrix3Xd & pose); + + /** \brief Set the 3D location of the detection. + \param pose_cv The 3D pose of the detection. + */ void setPose(const cv::Mat & pose_cv); + /** \brief Get the scale of the detection. + \return Scale of the detection. + */ double getScale() const; + + /** \brief Set the scale of the detection. + \param scale scale of the detection. + */ void setScale(double scale); const cv::Mat & getMetainfoImage() const; diff --git a/common/include/od/common/utils/ODFrameGenerator.h b/common/include/od/common/utils/ODFrameGenerator.h index f17f39ad..4369b002 100644 --- a/common/include/od/common/utils/ODFrameGenerator.h +++ b/common/include/od/common/utils/ODFrameGenerator.h @@ -10,7 +10,7 @@ namespace od { enum GeneratorType { - GENERATOR_TYPE_FILE_LIST, GENERATOR_TYPE_DEVICE + GENERATOR_TYPE_FILE_LIST, GENERATOR_TYPE_DEVICE, GENERATOR_TYPE_CONTAINER }; @@ -96,6 +96,47 @@ namespace od }; +/* + template<typename SceneT, template<class, class> class TContainer, class TObject> + class ODFrameGenerator<SceneT, GENERATOR_TYPE_CONTAINER> + { + public: + + explicit ODFrameGenerator(TContainer<TObject*, std::allocator<TObject*>> & container) :container_(container) + { + curr_image_ = -1; + exhausted_ = false; + } + + shared_ptr<SceneT> getNextFrame() + { + if(exhausted_) + { + cout << "Files Exhausted!"; + return nullptr; + } + + curr_image_++; + if(curr_image_ == file_list_.size() - 1) + exhausted_ = true; + + } + + bool isValid() + { + return !exhausted_; + } + + private: + + TContainer<TObject*, std::allocator<TObject*>> & container_; + bool exhausted_; + int curr_image_; + + }; +*/ + + template<> class ODFrameGenerator<ODSceneImage, GENERATOR_TYPE_DEVICE> { diff --git a/common/src/pipeline/ODDetection.cpp b/common/src/pipeline/ODDetection.cpp index 95358c1e..616d8150 100644 --- a/common/src/pipeline/ODDetection.cpp +++ b/common/src/pipeline/ODDetection.cpp @@ -32,7 +32,6 @@ namespace od id_ = id_; } - double ODDetection::getConfidence() const { return confidence_; From 5d88be67ae3c499b44261470d06ee25fc0f097f5 Mon Sep 17 00:00:00 2001 From: giacomo <giacomo.dabisias@gmail.com> Date: Fri, 29 Jul 2016 17:55:08 +0200 Subject: [PATCH 59/79] adds the renaming of the library --- common/CMakeLists.txt | 18 +- .../impl/od/common/utils/FrameGenerator.hpp | 0 common/impl/od/common/utils/Scene.hpp | 43 +++ .../common/utils/{ODViewer.hpp => Viewer.hpp} | 20 +- .../bindings/{ODSvmlight.h => Svmlight.h} | 5 +- .../pipeline/{ODDetection.h => Detection.h} | 96 +++--- .../pipeline/{ODDetector.h => Detector.h} | 66 ++-- .../{ODObjectDetector.h => ObjectDetector.h} | 18 +- .../od/common/pipeline/{ODScene.h => Scene.h} | 106 +++--- .../pipeline/{ODTrainer.h => Trainer.h} | 10 +- .../include/od/common/utils/FeatureDetector.h | 31 ++ ...eatureDetector2D.h => FeatureDetector2D.h} | 6 +- ...Interface.h => FeatureDetectorInterface.h} | 2 +- .../{ODFrameGenerator.h => FrameGenerator.h} | 50 +-- .../od/common/utils/ODFeatureDetector.h | 31 -- common/include/od/common/utils/ODViewer.h | 141 -------- ...{ODShared_pointers.h => Shared_pointers.h} | 0 .../od/common/utils/{ODUtils.h => Utils.h} | 6 +- common/include/od/common/utils/Viewer.h | 143 ++++++++ .../bindings/{ODSvmlight.cpp => Svmlight.cpp} | 4 +- common/src/pipeline/Detection.cpp | 290 ++++++++++++++++ common/src/pipeline/ODDetection.cpp | 291 ---------------- common/src/pipeline/ODScene.cpp | 83 ----- ...DObjectDetector.cpp => ObjectDetector.cpp} | 22 +- common/src/pipeline/Scene.cpp | 127 +++++++ ...eatureDetector.cpp => FeatureDetector.cpp} | 20 +- ...reDetector2D.cpp => FeatureDetector2D.cpp} | 10 +- ...rface.cpp => FeatureDetectorInterface.cpp} | 2 +- common/src/utils/FrameGenerator.cpp | 0 common/src/utils/{ODUtils.cpp => Utils.cpp} | 2 +- common/src/utils/{ODViewer.cpp => Viewer.cpp} | 90 ++--- ...or3DGlobal.hpp => CADDetector3DGlobal.hpp} | 82 ++--- .../misc/detection/DetectorMultiAlgo.hpp | 321 ++++++++++++++++++ .../misc/detection/ODDetectorMultiAlgo.hpp | 321 ------------------ .../{ODFaceRecognizer.h => FaceRecognizer.h} | 22 +- ...{ODCascadeDetector.h => CascadeDetector.h} | 28 +- ...deDetectorImpl.h => CascadeDetectorImpl.h} | 22 +- ...Interface.h => CascadeDetectorInterface.h} | 12 +- .../{ODHOGDetector.h => HOGDetector.h} | 32 +- .../training/{ODHOGTrainer.h => HOGTrainer.h} | 12 +- ...lMatching.h => PointCloudGlobalMatching.h} | 16 +- ...r3DGlobal.h => CADDetectTrainer3DGlobal.h} | 14 +- ...geLocalMatching.h => ImageLocalMatching.h} | 40 +-- ...gnizer2DLocal.h => CADRecognizer2DLocal.h} | 30 +- .../{ODCsvReader.h => CsvReader.h} | 2 +- .../{ODCsvWriter.h => CsvWriter.h} | 2 +- .../{ODMesh.h => Mesh.h} | 2 +- .../{ODModel.h => Model.h} | 2 +- ...odelRegistration.h => ModelRegistration.h} | 0 .../{ODPnPProblem.h => PnPProblem.h} | 6 +- .../{ODRobustMatcher.h => RobustMatcher.h} | 10 +- .../{ODUtils.h => Utils.h} | 8 +- ...Based.h => CADRecogTrainerSnapshotBased.h} | 12 +- detectors/src/global2D/CMakeLists.txt | 9 +- ...DFaceRecognizer.cpp => FaceRecognizer.cpp} | 52 +-- ...ascadeDetector.cpp => CascadeDetector.cpp} | 42 +-- ...tectorImpl.cpp => CascadeDetectorImpl.cpp} | 40 +-- .../{ODHOGDetector.cpp => HOGDetector.cpp} | 74 ++-- .../{ODHOGTrainer.cpp => HOGTrainer.cpp} | 70 ++-- detectors/src/global3D/CMakeLists.txt | 7 +- ...ching.cpp => PointCloudGlobalMatching.cpp} | 8 +- .../detection/CADDetector3DGlobal.cpp | 59 ++++ .../detection/ODCADDetector3DGlobal.cpp | 59 ---- ...lobal.cpp => CADDetectTrainer3DGlobal.cpp} | 14 +- detectors/src/local2D/CMakeLists.txt | 23 +- ...calMatching.cpp => ImageLocalMatching.cpp} | 26 +- ...er2DLocal.cpp => CADRecognizer2DLocal.cpp} | 98 +++--- .../{ODCsvReader.cpp => CsvReader.cpp} | 2 +- .../{ODCsvWriter.cpp => CsvWriter.cpp} | 2 +- .../{ODMesh.cpp => Mesh.cpp} | 2 +- .../{ODModel.cpp => Model.cpp} | 4 +- ...Registration.cpp => ModelRegistration.cpp} | 4 +- .../{ODPnPProblem.cpp => PnPProblem.cpp} | 4 +- ...{ODRobustMatcher.cpp => RobustMatcher.cpp} | 6 +- .../{ODUtils.cpp => Utils.cpp} | 2 +- ...d.cpp => CADRecogTrainerSnapshotBased.cpp} | 8 +- detectors/src/misc/CMakeLists.txt | 3 +- .../src/misc/detection/DetectorMultiAlgo.cpp | 107 ++++++ .../misc/detection/ODDetectorMultiAlgo.cpp | 107 ------ doc/doxygen/od_root.md | 4 +- .../gsoc2016_blog_giacomo.md | 37 +- examples/apps/CMakeLists.txt | 6 +- examples/apps/buildExample/CMakeLists.txt | 2 +- ...lizationTest.cpp => VisualizationTest.cpp} | 4 +- ...el.cpp => test_single_db_single_model.cpp} | 18 +- .../{od_multihog_app.cpp => multihog_app.cpp} | 26 +- examples/objectdetector/CMakeLists.txt | 34 +- .../{od_cascade_cam.cpp => cascade_cam.cpp} | 18 +- ...od_cascade_files.cpp => cascade_files.cpp} | 18 +- ...detector_cam.cpp => face_detector_cam.cpp} | 20 +- ...ctor_files.cpp => face_detector_files.cpp} | 18 +- ..._framegenerator.cpp => framegenerator.cpp} | 12 +- .../{od_hog_train.cpp => hog_train.cpp} | 22 +- ...g_camera.cpp => image_cadrecog_camera.cpp} | 24 +- ...cog_files.cpp => image_cadrecog_files.cpp} | 22 +- ...mage_customhog.cpp => image_customhog.cpp} | 20 +- ...mage_facerecog.cpp => image_facerecog.cpp} | 20 +- ...mage_hog_files.cpp => image_hog_files.cpp} | 22 +- ...ultialgo_files.cpp => multialgo_files.cpp} | 18 +- .../{od_multialgo_pc.cpp => multialgo_pc.cpp} | 20 +- .../{od_pc_global.cpp => pc_global.cpp} | 12 +- ...c_global_files.cpp => pc_global_files.cpp} | 16 +- ..._real_time.cpp => pc_global_real_time.cpp} | 20 +- gpu/common/CMakeLists.txt | 2 +- ...eatureDetector2D.h => FeatureDetector2D.h} | 6 +- ...reDetector2D.cpp => FeatureDetector2D.cpp} | 14 +- ...deDetectorImpl.h => CascadeDetectorImpl.h} | 24 +- gpu/detectors/src/global2D/CMakeLists.txt | 3 +- ...tectorImpl.cpp => CascadeDetectorImpl.cpp} | 38 +-- 109 files changed, 2110 insertions(+), 1971 deletions(-) create mode 100644 common/impl/od/common/utils/FrameGenerator.hpp create mode 100644 common/impl/od/common/utils/Scene.hpp rename common/impl/od/common/utils/{ODViewer.hpp => Viewer.hpp} (75%) rename common/include/od/common/bindings/{ODSvmlight.h => Svmlight.h} (96%) rename common/include/od/common/pipeline/{ODDetection.h => Detection.h} (70%) rename common/include/od/common/pipeline/{ODDetector.h => Detector.h} (62%) rename common/include/od/common/pipeline/{ODObjectDetector.h => ObjectDetector.h} (90%) rename common/include/od/common/pipeline/{ODScene.h => Scene.h} (51%) rename common/include/od/common/pipeline/{ODTrainer.h => Trainer.h} (83%) create mode 100644 common/include/od/common/utils/FeatureDetector.h rename common/include/od/common/utils/{ODFeatureDetector2D.h => FeatureDetector2D.h} (67%) rename common/include/od/common/utils/{ODFeatureDetectorInterface.h => FeatureDetectorInterface.h} (95%) rename common/include/od/common/utils/{ODFrameGenerator.h => FrameGenerator.h} (78%) delete mode 100644 common/include/od/common/utils/ODFeatureDetector.h delete mode 100644 common/include/od/common/utils/ODViewer.h rename common/include/od/common/utils/{ODShared_pointers.h => Shared_pointers.h} (100%) rename common/include/od/common/utils/{ODUtils.h => Utils.h} (95%) create mode 100644 common/include/od/common/utils/Viewer.h rename common/src/bindings/{ODSvmlight.cpp => Svmlight.cpp} (98%) create mode 100644 common/src/pipeline/Detection.cpp delete mode 100644 common/src/pipeline/ODDetection.cpp delete mode 100644 common/src/pipeline/ODScene.cpp rename common/src/pipeline/{ODObjectDetector.cpp => ObjectDetector.cpp} (79%) create mode 100644 common/src/pipeline/Scene.cpp rename common/src/utils/{ODFeatureDetector.cpp => FeatureDetector.cpp} (53%) rename common/src/utils/{ODFeatureDetector2D.cpp => FeatureDetector2D.cpp} (67%) rename common/src/utils/{ODFeatureDetectorInterface.cpp => FeatureDetectorInterface.cpp} (82%) create mode 100644 common/src/utils/FrameGenerator.cpp rename common/src/utils/{ODUtils.cpp => Utils.cpp} (99%) rename common/src/utils/{ODViewer.cpp => Viewer.cpp} (58%) rename detectors/impl/od/detectors/global3D/detection/{ODCADDetector3DGlobal.hpp => CADDetector3DGlobal.hpp} (69%) create mode 100644 detectors/impl/od/detectors/misc/detection/DetectorMultiAlgo.hpp delete mode 100644 detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp rename detectors/include/od/detectors/global2D/{ODFaceRecognizer.h => FaceRecognizer.h} (73%) rename detectors/include/od/detectors/global2D/detection/{ODCascadeDetector.h => CascadeDetector.h} (72%) rename detectors/include/od/detectors/global2D/detection/{ODCascadeDetectorImpl.h => CascadeDetectorImpl.h} (78%) rename detectors/include/od/detectors/global2D/detection/{ODCascadeDetectorInterface.h => CascadeDetectorInterface.h} (83%) rename detectors/include/od/detectors/global2D/detection/{ODHOGDetector.h => HOGDetector.h} (76%) rename detectors/include/od/detectors/global2D/training/{ODHOGTrainer.h => HOGTrainer.h} (89%) rename detectors/include/od/detectors/global3D/{ODPointCloudGlobalMatching.h => PointCloudGlobalMatching.h} (74%) rename detectors/include/od/detectors/global3D/training/{ODCADDetectTrainer3DGlobal.h => CADDetectTrainer3DGlobal.h} (84%) rename detectors/include/od/detectors/local2D/{ODImageLocalMatching.h => ImageLocalMatching.h} (59%) rename detectors/include/od/detectors/local2D/detection/{ODCADRecognizer2DLocal.h => CADRecognizer2DLocal.h} (78%) rename detectors/include/od/detectors/local2D/simple_ransac_detection/{ODCsvReader.h => CsvReader.h} (94%) rename detectors/include/od/detectors/local2D/simple_ransac_detection/{ODCsvWriter.h => CsvWriter.h} (89%) rename detectors/include/od/detectors/local2D/simple_ransac_detection/{ODMesh.h => Mesh.h} (96%) rename detectors/include/od/detectors/local2D/simple_ransac_detection/{ODModel.h => Model.h} (96%) rename detectors/include/od/detectors/local2D/simple_ransac_detection/{ODModelRegistration.h => ModelRegistration.h} (100%) rename detectors/include/od/detectors/local2D/simple_ransac_detection/{ODPnPProblem.h => PnPProblem.h} (92%) rename detectors/include/od/detectors/local2D/simple_ransac_detection/{ODRobustMatcher.h => RobustMatcher.h} (92%) rename detectors/include/od/detectors/local2D/simple_ransac_detection/{ODUtils.h => Utils.h} (90%) rename detectors/include/od/detectors/local2D/training/{ODCADRecogTrainerSnapshotBased.h => CADRecogTrainerSnapshotBased.h} (88%) rename detectors/src/global2D/{ODFaceRecognizer.cpp => FaceRecognizer.cpp} (72%) rename detectors/src/global2D/detection/{ODCascadeDetector.cpp => CascadeDetector.cpp} (59%) rename detectors/src/global2D/detection/{ODCascadeDetectorImpl.cpp => CascadeDetectorImpl.cpp} (72%) rename detectors/src/global2D/detection/{ODHOGDetector.cpp => HOGDetector.cpp} (71%) rename detectors/src/global2D/training/{ODHOGTrainer.cpp => HOGTrainer.cpp} (84%) rename detectors/src/global3D/{ODPointCloudGlobalMatching.cpp => PointCloudGlobalMatching.cpp} (84%) create mode 100644 detectors/src/global3D/detection/CADDetector3DGlobal.cpp delete mode 100644 detectors/src/global3D/detection/ODCADDetector3DGlobal.cpp rename detectors/src/global3D/training/{ODCADDetectTrainer3DGlobal.cpp => CADDetectTrainer3DGlobal.cpp} (78%) rename detectors/src/local2D/{ODImageLocalMatching.cpp => ImageLocalMatching.cpp} (67%) rename detectors/src/local2D/detection/{ODCADRecognizer2DLocal.cpp => CADRecognizer2DLocal.cpp} (75%) rename detectors/src/local2D/simple_ransac_detection/{ODCsvReader.cpp => CsvReader.cpp} (97%) rename detectors/src/local2D/simple_ransac_detection/{ODCsvWriter.cpp => CsvWriter.cpp} (95%) rename detectors/src/local2D/simple_ransac_detection/{ODMesh.cpp => Mesh.cpp} (97%) rename detectors/src/local2D/simple_ransac_detection/{ODModel.cpp => Model.cpp} (97%) rename detectors/src/local2D/simple_ransac_detection/{ODModelRegistration.cpp => ModelRegistration.cpp} (91%) rename detectors/src/local2D/simple_ransac_detection/{ODPnPProblem.cpp => PnPProblem.cpp} (99%) rename detectors/src/local2D/simple_ransac_detection/{ODRobustMatcher.cpp => RobustMatcher.cpp} (98%) rename detectors/src/local2D/simple_ransac_detection/{ODUtils.cpp => Utils.cpp} (99%) rename detectors/src/local2D/training/{ODCADRecogTrainerSnapshotBased.cpp => CADRecogTrainerSnapshotBased.cpp} (98%) create mode 100644 detectors/src/misc/detection/DetectorMultiAlgo.cpp delete mode 100644 detectors/src/misc/detection/ODDetectorMultiAlgo.cpp rename examples/apps/buildExample/{ODVisualizationTest.cpp => VisualizationTest.cpp} (93%) rename examples/apps/cadrecog2D/{od_test_single_db_single_model.cpp => test_single_db_single_model.cpp} (80%) rename examples/apps/global2D/{od_multihog_app.cpp => multihog_app.cpp} (78%) rename examples/objectdetector/{od_cascade_cam.cpp => cascade_cam.cpp} (80%) rename examples/objectdetector/{od_cascade_files.cpp => cascade_files.cpp} (81%) rename examples/objectdetector/{od_face_detector_cam.cpp => face_detector_cam.cpp} (50%) rename examples/objectdetector/{od_face_detector_files.cpp => face_detector_files.cpp} (56%) rename examples/objectdetector/{od_framegenerator.cpp => framegenerator.cpp} (82%) rename examples/objectdetector/{od_hog_train.cpp => hog_train.cpp} (79%) rename examples/objectdetector/{od_image_cadrecog_camera.cpp => image_cadrecog_camera.cpp} (77%) rename examples/objectdetector/{od_image_cadrecog_files.cpp => image_cadrecog_files.cpp} (77%) rename examples/objectdetector/{od_image_customhog.cpp => image_customhog.cpp} (77%) rename examples/objectdetector/{od_image_facerecog.cpp => image_facerecog.cpp} (79%) rename examples/objectdetector/{od_image_hog_files.cpp => image_hog_files.cpp} (76%) rename examples/objectdetector/{od_multialgo_files.cpp => multialgo_files.cpp} (77%) rename examples/objectdetector/{od_multialgo_pc.cpp => multialgo_pc.cpp} (83%) rename examples/objectdetector/{od_pc_global.cpp => pc_global.cpp} (81%) rename examples/objectdetector/{od_pc_global_files.cpp => pc_global_files.cpp} (78%) rename examples/objectdetector/{od_pc_global_real_time.cpp => pc_global_real_time.cpp} (81%) rename gpu/common/include/od/gpu/common/utils/{ODFeatureDetector2D.h => FeatureDetector2D.h} (83%) rename gpu/common/src/utils/{ODFeatureDetector2D.cpp => FeatureDetector2D.cpp} (86%) rename gpu/detectors/include/od/gpu/detectors/global2D/detection/{ODCascadeDetectorImpl.h => CascadeDetectorImpl.h} (79%) rename gpu/detectors/src/global2D/detection/{ODCascadeDetectorImpl.cpp => CascadeDetectorImpl.cpp} (75%) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index ceaa6721..91110dcf 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -15,18 +15,18 @@ if(WITH_GPU) endif() set(SOURCES - "src/pipeline/ODObjectDetector.cpp" - "src/pipeline/ODScene.cpp" - "src/pipeline/ODDetection.cpp" - "src/utils/ODUtils.cpp" - "src/utils/ODFeatureDetector2D.cpp" - "src/utils/ODViewer.cpp" - "src/utils/ODFeatureDetector.cpp" - "src/utils/ODFeatureDetectorInterface.cpp" + "src/pipeline/ObjectDetector.cpp" + "src/pipeline/Scene.cpp" + "src/pipeline/Detection.cpp" + "src/utils/Utils.cpp" + "src/utils/FeatureDetector2D.cpp" + "src/utils/Viewer.cpp" + "src/utils/FeatureDetector.cpp" + "src/utils/FeatureDetectorInterface.cpp" ) if(WITH_SVMLIGHT) - set(SOURCES ${SOURCES} "src/bindings/ODSvmlight.cpp") + set(SOURCES ${SOURCES} "src/bindings/Svmlight.cpp") include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) set(SUBSYS_DEPS ${SUBSYS_DEPS} svmlight) endif() diff --git a/common/impl/od/common/utils/FrameGenerator.hpp b/common/impl/od/common/utils/FrameGenerator.hpp new file mode 100644 index 00000000..e69de29b diff --git a/common/impl/od/common/utils/Scene.hpp b/common/impl/od/common/utils/Scene.hpp new file mode 100644 index 00000000..b162923d --- /dev/null +++ b/common/impl/od/common/utils/Scene.hpp @@ -0,0 +1,43 @@ +#pragma once + + +template <typename PointType> +ScenePointCloud<PointType>::ScenePointCloud(const shared_ptr<pcl::PointCloud<PointType> > point_cloud) +{ + point_cloud_ = point_cloud; +} + +template <typename PointType> +ScenePointCloud<PointType>::ScenePointCloud(const std::string & point_cloud_file): + point_cloud_(new pcl::PointCloud<PointType>()) +{ + if(pcl::io::loadPCDFile<PointType> (point_cloud_file, *point_cloud_ ) == -1) + { + std::cout << "ERROR: Couldn't read the file "<< point_cloud_file << std::endl; + } + path_ = point_cloud_file; +} + +template <typename PointType> +shared_ptr<pcl::PointCloud<PointType> > ScenePointCloud<PointType>::getPointCloud() const +{ + return point_cloud_; +} + +template <typename PointType> +shared_ptr<pcl::PointCloud<PointType> > ScenePointCloud<PointType>::getPointCloudRef() const +{ + return point_cloud_; +} + +template <typename PointType> +void ScenePointCloud<PointType>::setPointCloud(const shared_ptr<pcl::PointCloud<PointType> > point_cloud) +{ + point_cloud_ = point_cloud; +} + +template <typename PointType> +void * ScenePointCloud<PointType>::getData() +{ + return (void *)point_cloud_.get(); +} \ No newline at end of file diff --git a/common/impl/od/common/utils/ODViewer.hpp b/common/impl/od/common/utils/Viewer.hpp similarity index 75% rename from common/impl/od/common/utils/ODViewer.hpp rename to common/impl/od/common/utils/Viewer.hpp index be04c5cf..ff4d2fa9 100644 --- a/common/impl/od/common/utils/ODViewer.hpp +++ b/common/impl/od/common/utils/Viewer.hpp @@ -1,10 +1,10 @@ #pragma once -#include "od/common/utils/ODViewer.h" +#include "od/common/utils/Viewer.h" namespace od { template<typename PointT> - void ODViewer::render(shared_ptr<pcl::PointCloud<PointT> > to_display, const std::string & cloud_name, bool colored) + void Viewer::render(shared_ptr<pcl::PointCloud<PointT> > to_display, const std::string & cloud_name, bool colored) { if(status_ != POINTCLOUD){ std::cout << "Switching viewer to PointCloud mode" << std::endl; @@ -16,7 +16,7 @@ namespace od { status_ = POINTCLOUD; if(!viewer_ ){ -#ifdef WITH_BOOST_SHARED_PTR +#ifndef WITH_BSTD_SHARED_PTR viewer_ = shared_ptr<pcl::visualization::PCLVisualizer>(new pcl::visualization::PCLVisualizer(cloud_name)); #else viewer_ = make_shared<pcl::visualization::PCLVisualizer>(cloud_name); @@ -36,7 +36,7 @@ namespace od { } template<typename PointT> - void ODViewer::render(shared_ptr<pcl::PointCloud<PointT> > to_display, + void Viewer::render(shared_ptr<pcl::PointCloud<PointT> > to_display, pcl::visualization::PointCloudColorHandlerRandom<PointT> & random_handler, const std::string & cloud_name) { if(status_ != POINTCLOUD){ @@ -49,7 +49,7 @@ namespace od { status_ = POINTCLOUD; if(!viewer_ ){ -#ifdef WITH_BOOST_SHARED_PTR +#ifndef WITH_BOOST_SHARED_PTR viewer_ = shared_ptr<pcl::visualization::PCLVisualizer>(new pcl::visualization::PCLVisualizer(cloud_name)); #else viewer_ = make_shared<pcl::visualization::PCLVisualizer>(cloud_name); @@ -64,19 +64,19 @@ namespace od { } template<typename PointT> - void ODViewer::render(shared_ptr<ODScenePointCloud<PointT> > to_display, const std::string & cloud_name, bool colored) + void Viewer::render(shared_ptr<ScenePointCloud<PointT> > to_display, const std::string & cloud_name, bool colored) { render(to_display->getPointCloud(), cloud_name, colored); } template<typename PointT> - void ODViewer::render(const ODScenePointCloud<PointT> & to_display, const std::string & cloud_name, bool colored) + void Viewer::render(const ScenePointCloud<PointT> & to_display, const std::string & cloud_name, bool colored) { render(to_display.getPointCloud(), cloud_name, colored); } template<typename PointT> - void ODViewer::update(shared_ptr<pcl::PointCloud<PointT> > to_display, const std::string & cloud_name, bool colored) + void Viewer::update(shared_ptr<pcl::PointCloud<PointT> > to_display, const std::string & cloud_name, bool colored) { if(status_ != POINTCLOUD){ std::cout << "No PointCloud to render! use render(shared_ptr<pcl::PointCloud<PointT>>) first!" << std::endl; @@ -101,13 +101,13 @@ namespace od { } template<typename PointT> - void ODViewer::update(shared_ptr<ODScenePointCloud<PointT> > to_display, const std::string & cloud_name, bool colored) + void Viewer::update(shared_ptr<ScenePointCloud<PointT> > to_display, const std::string & cloud_name, bool colored) { update(to_display->getPointCloud(), cloud_name, colored); } template<typename PointT> - void ODViewer::update(const ODScenePointCloud<PointT> & to_display, const std::string & cloud_name, bool colored) + void Viewer::update(const ScenePointCloud<PointT> & to_display, const std::string & cloud_name, bool colored) { update(to_display.getPointCloud(), cloud_name, colored); } diff --git a/common/include/od/common/bindings/ODSvmlight.h b/common/include/od/common/bindings/Svmlight.h similarity index 96% rename from common/include/od/common/bindings/ODSvmlight.h rename to common/include/od/common/bindings/Svmlight.h index e5666af6..ea929568 100644 --- a/common/include/od/common/bindings/ODSvmlight.h +++ b/common/include/od/common/bindings/Svmlight.h @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -61,6 +61,9 @@ namespace svmlight { } #endif +enum type { + CLASSIFICATION, REGRESSION, RANKING, OPTIMIZATION +}; class SVMlight { private: diff --git a/common/include/od/common/pipeline/ODDetection.h b/common/include/od/common/pipeline/Detection.h similarity index 70% rename from common/include/od/common/pipeline/ODDetection.h rename to common/include/od/common/pipeline/Detection.h index d5026d5b..9b4ef7a7 100644 --- a/common/include/od/common/pipeline/ODDetection.h +++ b/common/include/od/common/pipeline/Detection.h @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -28,9 +28,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 12.06.15. // #pragma once -#include "od/common/utils/ODUtils.h" -#include "od/common/pipeline/ODScene.h" -#include "od/common/utils/ODShared_pointers.h" +#include "od/common/utils/Utils.h" +#include "od/common/pipeline/Scene.h" +#include "od/common/utils/Shared_pointers.h" #include <Eigen/Core> #include <opencv2/core/eigen.hpp> @@ -41,8 +41,8 @@ namespace od /** \brief The base class of all the detection. * - * This is the base class of all the detection classes containing the detection information. All the ODDetectors return a collection of this class (in the form of ODDetections). - * Supports three modes: recognition (with type OD_RECOGNITION) and classification (with type OD_CLASSIFICATION) and detection (with type OD_DETECTION). Along with the type, ODDetector sets an ID to identify what class or what instance of recognition is detected/recognied. + * This is the base class of all the detection classes containing the detection information. All the Detectors return a collection of this class (in the form of Detections). + * Supports three modes: recognition (with type RECOGNITION) and classification (with type CLASSIFICATION) and detection (with type DETECTION). Along with the type, Detector sets an ID to identify what class or what instance of recognition is detected/recognied. * * \author Kripasindhu Sarkar * @@ -51,7 +51,7 @@ namespace od { public: - OD_DEFINE_ENUM_WITH_STRING_CONVERSIONS(DetectionType, (OD_RECOGNITION)(OD_CLASSIFICATION)(OD_DETECTION)(OD_DETECTION_NULL)) + _DEFINE_ENUM_WITH_STRING_CONVERSIONS(DetectionType, (RECOGNITION)(CLASSIFICATION)(DETECTION)(DETECTION_NULL)) virtual ~Detection(){} @@ -66,13 +66,13 @@ namespace od */ void printSelf(); - /** \brief Get the type of the detection. This can be OD_RECOGNITION, OD_CLASSIFICATION, OD_DETECTION, OD_DETECTION_NULL. + /** \brief Get the type of the detection. This can be _RECOGNITION, _CLASSIFICATION, _DETECTION, _DETECTION_NULL. \return The type of the detection. */ const DetectionType & getType() const; - /** \brief Set the type of the detection. This can be OD_RECOGNITION, OD_CLASSIFICATION, OD_DETECTION, OD_DETECTION_NULL. - \param id The type of the detection. + /** \brief Set the type of the detection. This can be _RECOGNITION, _CLASSIFICATION, _DETECTION, _DETECTION_NULL. + \param type The type of the detection. */ void setType(const DetectionType & type); @@ -86,12 +86,12 @@ namespace od */ void setId(const std::string & id); - /** \brief Get the confidence of the detection. ODDetector can use this to provide confidence amnong several detections. + /** \brief Get the confidence of the detection. Detector can use this to provide confidence amnong several detections. \return The confidence level of the detection. */ double getConfidence() const; - /** \brief Set the confidence of the detection. ODDetector can use this to provide confidence amnong several detections. + /** \brief Set the confidence of the detection. Detector can use this to provide confidence amnong several detections. \param confidence The confidence level of the detection. */ void setConfidence(double confidence); @@ -109,18 +109,18 @@ namespace od * \author Kripasindhu Sarkar * */ - class ODDetection2D : public virtual ODDetection + class Detection2D : public virtual Detection { public: - virtual ~ODDetection2D(){} + virtual ~Detection2D(){} - /** \brief Constructor of the ODDetection2D class. + /** \brief Constructor of the Detection2D class. \param type The detection type. \param id The detection identifier. \param confidence The detection confidence. */ - ODDetection2D(const DetectionType & type = OD_DETECTION_NULL, const std::string & id = std::string(""), double confidence = 1.0); + Detection2D(const DetectionType & type = DETECTION_NULL, const std::string & id = std::string(""), double confidence = 1.0); /** \brief Get the 2D location of the detection. \return The 2D vector of the detection position. @@ -163,18 +163,18 @@ namespace od * \author Kripasindhu Sarkar * */ - class ODDetection3D : public virtual ODDetection + class Detection3D : public virtual Detection { public: - virtual ~ODDetection3D(){} + virtual ~Detection3D(){} - /** \brief Constructor of the ODDetection3D class. + /** \brief Constructor of the Detection3D class. \param type The detection type. \param id The detection identifier. \param confidence The detection confidence. */ - ODDetection3D(const DetectionType & type = OD_DETECTION_NULL, const std::string & id = std::string(""), double confidence = 1.0); + Detection3D(const DetectionType & type = DETECTION_NULL, const std::string & id = std::string(""), double confidence = 1.0); /** \brief Get the 3D location of the detection. @@ -183,7 +183,7 @@ namespace od const Eigen::Vector4d & getLocation() const; /** \brief Set the 3D location of the detection. - \param locationThe 3D location of the detection. + \param location 3D location of the detection. */ void setLocation(const Eigen::Vector4d & location); @@ -238,31 +238,31 @@ namespace od * \author Kripasindhu Sarkar * */ - class ODDetectionComplete: public ODDetection2D, public ODDetection3D + class DetectionComplete: public Detection2D, public Detection3D { }; - /** \brief The container class for ODDetection + /** \brief The container class for Detections * * \author Kripasindhu Sarkar * */ - class ODDetections + class Detections { public: - ODDetections(unsigned int n = 0); + Detections(unsigned int n = 0); - virtual ~ODDetections(); + virtual ~Detections(); unsigned int size() const; - void push_back(shared_ptr<ODDetection> detection); + void push_back(shared_ptr<Detection> detection); - void append(shared_ptr<ODDetections> detections); + void append(shared_ptr<Detections> detections); - shared_ptr<ODDetection> operator[](unsigned int i); - shared_ptr<ODDetection> at(unsigned int i); + shared_ptr<Detection> operator[](unsigned int i); + shared_ptr<Detection> at(unsigned int i); const cv::Mat & getMetainfoImage() const; void setMetainfoImage(const cv::Mat & metainfo_image_); @@ -272,7 +272,7 @@ namespace od protected: - std::vector<shared_ptr<ODDetection>> detections_; + std::vector<shared_ptr<Detection>> detections_; cv::Mat metainfo_image_; shared_ptr<pcl::PointCloud<pcl::PointXYZ> > metainfo_cluster_; @@ -281,35 +281,35 @@ namespace od -/** \brief The container class for ODDetection2D returned by ODDetector2D +/** \brief The container class for Detection2D returned by Detector2D * * \author Kripasindhu Sarkar * */ - class ODDetections2D: public ODDetections + class Detections2D: public Detections { public: /** \brief Draws rectangles over the input image using the bounding box information present in all the 2D detections. This is a quick function to render and verify the detections made. */ - ODSceneImage renderMetainfo(ODSceneImage & input); - ODSceneImage renderMetainfo(shared_ptr<ODSceneImage> input); + SceneImage renderMetainfo(SceneImage & input); + SceneImage renderMetainfo(shared_ptr<SceneImage> input); - shared_ptr<ODDetection2D> operator[](unsigned int i); - shared_ptr<ODDetection2D> at(unsigned int i); + shared_ptr<Detection2D> operator[](unsigned int i); + shared_ptr<Detection2D> at(unsigned int i); }; - /** \brief The container class for ODDetection3D returned by ODDetector3D + /** \brief The container class for Detection3D returned by Detector3D * * \author Kripasindhu Sarkar * */ - class ODDetections3D: public ODDetections + class Detections3D: public Detections { public: - /*ODSceneImage renderMetainfo(ODSceneImage input) + /*SceneImage renderMetainfo(SceneImage input) { //picking up random colors for different detection algorithm, if exist std::map<std::string, cv::Scalar> color_map; @@ -322,28 +322,28 @@ namespace od cv::Mat image = input.getCVImage().clone(); for(int i = 0; i < detections_.size(); i++) { - ODDetections3D * detection = dynamic_cast<ODDetections3D *>(detections_[i]); + Detections3D * detection = dynamic_cast<Detections3D *>(detections_[i]); cv::rectangle(image, detection->bounding_box_2d_, color_map[detections_[i]->getId()], 2); } - return ODSceneImage(image); + return SceneImage(image); }*/ - shared_ptr<ODDetection3D> operator[](unsigned int i); - shared_ptr<ODDetection3D> at(unsigned int i); + shared_ptr<Detection3D> operator[](unsigned int i); + shared_ptr<Detection3D> at(unsigned int i); }; - /** \brief The container class for ODDetectionComplete returned by ODDetector2DComplete + /** \brief The container class for DetectionComplete returned by Detector2DComplete * * \author Kripasindhu Sarkar * */ - class ODDetectionsComplete: public ODDetections + class DetectionsComplete: public Detections { public: - shared_ptr<ODDetectionComplete> operator[](unsigned int i); - shared_ptr<ODDetectionComplete> at(unsigned int i); + shared_ptr<DetectionComplete> operator[](unsigned int i); + shared_ptr<DetectionComplete> at(unsigned int i); }; } diff --git a/common/include/od/common/pipeline/ODDetector.h b/common/include/od/common/pipeline/Detector.h similarity index 62% rename from common/include/od/common/pipeline/ODDetector.h rename to common/include/od/common/pipeline/Detector.h index 3e97f0f6..16a1e436 100644 --- a/common/include/od/common/pipeline/ODDetector.h +++ b/common/include/od/common/pipeline/Detector.h @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -28,9 +28,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 08.06.15. // #pragma once -#include "od/common/pipeline/ODScene.h" -#include "od/common/pipeline/ODObjectDetector.h" -#include "od/common/utils/ODShared_pointers.h" +#include "od/common/pipeline/Scene.h" +#include "od/common/pipeline/ObjectDetector.h" +#include "od/common/utils/Shared_pointers.h" namespace od { @@ -42,15 +42,15 @@ namespace od * \author Kripasindhu Sarkar * */ - class ODDetector: public ODDetectorCommon + class Detector: public DetectorCommon { public: - ODDetector(const std::string & training_data_location) : ODDetectorCommon(training_data_location) {} - ODDetector() {} + Detector(const std::string & training_data_location) : DetectorCommon(training_data_location) {} + Detector() {} - virtual shared_ptr<ODDetections> detect(shared_ptr<ODScene> scene) = 0; - virtual shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene) = 0; + virtual shared_ptr<Detections> detect(shared_ptr<Scene> scene) = 0; + virtual shared_ptr<Detections> detectOmni(shared_ptr<Scene> scene) = 0; bool meta_info_; @@ -58,91 +58,91 @@ namespace od /** \brief The detector of 2D scene. * - * This class takes a 2D scene (ODSceneImage) as input and performs detection on them. All the 2D detectors should derive from this class and implement the detect and detectOmni functions. + * This class takes a 2D scene (SceneImage) as input and performs detection on them. All the 2D detectors should derive from this class and implement the detect and detectOmni functions. * * \author Kripasindhu Sarkar * */ - class ODDetector2D: public ODDetector + class Detector2D: public Detector { public: - ODDetector2D(const std::string & trained_data_location) : ODDetector(trained_data_location) {} - ODDetector2D() {} + Detector2D(const std::string & trained_data_location) : Detector(trained_data_location) {} + Detector2D() {} - shared_ptr<ODDetections> detect(shared_ptr<ODScene> scene) + shared_ptr<Detections> detect(shared_ptr<Scene> scene) { - return detect(dynamic_pointer_cast<ODSceneImage>(scene)); + return detect(dynamic_pointer_cast<SceneImage>(scene)); } /** \brief Function for performing detection on a segmented scene. * The purpose of this function is to perform detection on a segmented scene or an 'object candidate'. i.e. the entire scene is considered as an 'object' or an detection. It is possible for a scene to trigger multiple detections. * \param[in] scene An instance of 2D scene - * \return [out] detections A number of detections as an ODDetections instance. + * \return [out] detections A number of detections as an Detections instance. */ - virtual shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene) = 0; + virtual shared_ptr<Detections> detect(shared_ptr<SceneImage> scene) = 0; /** \brief Function for performing detection on an entire scene. * The purpose of this function is to detect an object in an entire scene. Thus, other than the type of detection we also have information about the location of the detection w.r.t. the scene. * \param[in] scene An instance of 2D scene - * \return [out] detections A number of detections as an ODDetections2D instance containing information about the detection and its 2D location. + * \return [out] detections A number of detections as an Detections2D instance containing information about the detection and its 2D location. */ - virtual shared_ptr<ODDetections2D> detectOmni(shared_ptr<ODSceneImage> scene) = 0; + virtual shared_ptr<Detections2D> detectOmni(shared_ptr<SceneImage> scene) = 0; }; /** \brief The detector of 3D scene. * - * This class takes a 2D scene (ODSceneImage) as input and performs detection on them. All the 3D detectors should derive from this class and implement the detect and detectOmni functions. + * This class takes a 2D scene (SceneImage) as input and performs detection on them. All the 3D detectors should derive from this class and implement the detect and detectOmni functions. * * \author Kripasindhu Sarkar * */ template<typename PointT> - class ODDetector3D: public ODDetector + class Detector3D: public Detector { public: - ODDetector3D(const std::string & trained_data_location) : ODDetector(trained_data_location) {} + Detector3D(const std::string & trained_data_location) : Detector(trained_data_location) {} /** \brief Function for performing detection on a segmented scene. * The purpose of this function is to perform detection on a segmented scene or an 'object candidate'. i.e. the entire scene is considered as an 'object' or an detection. It is possible for a scene to trigger multiple detections. * \param[in] scene An instance of 3D scene - * \return A number of detections as an ODDetections instance. + * \return A number of detections as an Detections instance. */ - virtual shared_ptr<ODDetections> detect(shared_ptr<ODScenePointCloud<PointT> > scene) = 0; + virtual shared_ptr<Detections> detect(shared_ptr<ScenePointCloud<PointT> > scene) = 0; /** \brief Function for performing detection on an entire scene. * The purpose of this function is to detect an object in an entire scene. Thus, other than the type of detection we also have information about the location of the detection w.r.t. the scene. * \param[in] scene An instance of 3D scene - * \return A number of detections as an ODDetections3D instance containing information about the detection and its 3D pose. + * \return A number of detections as an Detections3D instance containing information about the detection and its 3D pose. */ - virtual shared_ptr<ODDetections3D> detectOmni(shared_ptr<ODScenePointCloud<PointT> > scene) = 0; + virtual shared_ptr<Detections3D> detectOmni(shared_ptr<ScenePointCloud<PointT> > scene) = 0; }; /** \brief The detector of 2D scene performing a 'complete detection'. * - * This class takes a 2D scene (ODSceneImage) as input and performs complete detection on them. That is, other than finding the bounding box or location of the object in the image it + * This class takes a 2D scene (SceneImage) as input and performs complete detection on them. That is, other than finding the bounding box or location of the object in the image it * finds out the 3D location and orientation (in other words translation and rotation) of the object in the actual 3D scene as well. * * \author Kripasindhu Sarkar * */ - class ODDetector2DComplete: public ODDetector + class Detector2DComplete: public Detector { public: - ODDetector2DComplete(const std::string & trained_data_location) : ODDetector(trained_data_location) {} + Detector2DComplete(const std::string & trained_data_location) : Detector(trained_data_location) {} /** \brief Function for performing detection on a segmented scene. * The purpose of this function is to perform detection on a segmented scene or an 'object candidate'. i.e. the entire scene is considered as an 'object' or an detection. It is possible for a scene to trigger multiple detections. * \param[in] scene An instance of 2D scene - * \return A number of detections as an ODDetections instance. + * \return A number of detections as an Detections instance. */ - virtual shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene) = 0; + virtual shared_ptr<Detections> detect(shared_ptr<SceneImage> scene) = 0; /** \brief Function for performing detection on an entire scene. * The purpose of this function is to detect an object in an entire scene. Thus, other than the type of detection we also have information about the location of the detection w.r.t. the scene. * \param[in] scene An instance of 2D scene - * \return A number of detections as an ODDetections3D instance containing information about the detection and its pose in 3D. + * \return A number of detections as an Detections3D instance containing information about the detection and its pose in 3D. */ - virtual shared_ptr<ODDetections3D> detectOmni(shared_ptr<ODSceneImage> scene) = 0; + virtual shared_ptr<Detections3D> detectOmni(shared_ptr<SceneImage> scene) = 0; }; } diff --git a/common/include/od/common/pipeline/ODObjectDetector.h b/common/include/od/common/pipeline/ObjectDetector.h similarity index 90% rename from common/include/od/common/pipeline/ODObjectDetector.h rename to common/include/od/common/pipeline/ObjectDetector.h index b7363f73..8089a3c1 100644 --- a/common/include/od/common/pipeline/ODObjectDetector.h +++ b/common/include/od/common/pipeline/ObjectDetector.h @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -30,8 +30,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #pragma once #include <string> #include <vector> -#include "od/common/pipeline/ODDetection.h" -#include "od/common/pipeline/ODScene.h" +#include "od/common/pipeline/Detection.h" +#include "od/common/pipeline/Scene.h" namespace od { @@ -51,12 +51,12 @@ namespace od * */ - class ODDetectorCommon + class DetectorCommon { public: - ODDetectorCommon(const std::string & trained_data_location); - ODDetectorCommon() {} + DetectorCommon(const std::string & trained_data_location); + DetectorCommon() {} virtual void init() = 0; @@ -129,10 +129,10 @@ namespace od virtual int train() = 0; - virtual int detect(shared_ptr<ODScene> scene, const std::vector<shared_ptr<ODDetection> > & detections) = 0; + virtual int detect(shared_ptr<Scene> scene, const std::vector<shared_ptr<Detection> > & detections) = 0; - virtual shared_ptr<ODDetection> detect(shared_ptr<ODScene> scene) = 0; - virtual shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene) = 0; + virtual shared_ptr<Detection> detect(shared_ptr<Scene> scene) = 0; + virtual shared_ptr<Detections> detectOmni(shared_ptr<Scene> scene) = 0; protected: diff --git a/common/include/od/common/pipeline/ODScene.h b/common/include/od/common/pipeline/Scene.h similarity index 51% rename from common/include/od/common/pipeline/ODScene.h rename to common/include/od/common/pipeline/Scene.h index a797ba3a..f6257f03 100644 --- a/common/include/od/common/pipeline/ODScene.h +++ b/common/include/od/common/pipeline/Scene.h @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -34,7 +34,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <opencv2/features2d/features2d.hpp> #include <pcl/point_cloud.h> #include <pcl/io/pcd_io.h> -#include "od/common/utils/ODShared_pointers.h" +#include "od/common/utils/Shared_pointers.h" namespace od { @@ -43,7 +43,7 @@ namespace od * \author Kripasindhu Sarkar * */ - class ODScene + class Scene { public: @@ -62,12 +62,12 @@ namespace od * \author Kripasindhu Sarkar * */ - class ODSceneImage : public ODScene + class SceneImage : public Scene { public: - ODSceneImage(const cv::Mat & cvimage); - ODSceneImage(const std::string & path); + SceneImage(const cv::Mat & cvimage); + SceneImage(const std::string & path); const std::vector<cv::KeyPoint> & getKeypoints() const; void setKeypoints(const std::vector<cv::KeyPoint> & keypoints_); @@ -95,14 +95,14 @@ namespace od * */ template <typename PointType> - class ODScenePointCloud : public ODScene + class ScenePointCloud : public Scene { public: - ODScenePointCloud(const shared_ptr<pcl::PointCloud<PointType> > point_cloud); - ODScenePointCloud(const std::string & point_cloud_file); - ODScenePointCloud(): point_cloud_(new pcl::PointCloud<PointType>()){} + ScenePointCloud(const shared_ptr<pcl::PointCloud<PointType> > point_cloud); + ScenePointCloud(const std::string & point_cloud_file); + ScenePointCloud(): point_cloud_(new pcl::PointCloud<PointType>()){} shared_ptr<pcl::PointCloud<PointType> > getPointCloud() const; shared_ptr<pcl::PointCloud<PointType> > getPointCloudRef() const; @@ -116,46 +116,54 @@ namespace od }; - template <typename PointType> - ODScenePointCloud<PointType>::ODScenePointCloud(const shared_ptr<pcl::PointCloud<PointType> > point_cloud) - { - point_cloud_ = point_cloud; - } - - template <typename PointType> - ODScenePointCloud<PointType>::ODScenePointCloud(const std::string & point_cloud_file): - point_cloud_(new pcl::PointCloud<PointType>()) - { - if(pcl::io::loadPCDFile<PointType> (point_cloud_file, *point_cloud_ ) == -1) - { - std::cout << "ERROR: Couldn't read the file "<< point_cloud_file << std::endl; - } - path_ = point_cloud_file; - } - - template <typename PointType> - shared_ptr<pcl::PointCloud<PointType> > ODScenePointCloud<PointType>::getPointCloud() const - { - return point_cloud_; - } + #ifndef DOXYGEN_SHOULD_SKIP_THIS + + extern template + ScenePointCloud<pcl::PointXYZ>::ScenePointCloud(const shared_ptr<pcl::PointCloud<pcl::PointXYZ> > point_cloud); + extern template + ScenePointCloud<pcl::PointXYZRGB>::ScenePointCloud(const shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> > point_cloud); + extern template + ScenePointCloud<pcl::PointXYZRGBA>::ScenePointCloud(const shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA> > point_cloud); + + extern template + ScenePointCloud<pcl::PointXYZ>::ScenePointCloud(const std::string & point_cloud_file); + extern template + ScenePointCloud<pcl::PointXYZRGB>::ScenePointCloud(const std::string & point_cloud_file); + extern template + ScenePointCloud<pcl::PointXYZRGBA>::ScenePointCloud(const std::string & point_cloud_file); + + extern template + shared_ptr<pcl::PointCloud<pcl::PointXYZ> > ScenePointCloud<pcl::PointXYZ>::getPointCloud() const; + extern template + shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> > ScenePointCloud<pcl::PointXYZRGB>::getPointCloud() const; + extern template + shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA> > ScenePointCloud<pcl::PointXYZRGBA>::getPointCloud() const; + + extern template + shared_ptr<pcl::PointCloud<pcl::PointXYZ> > ScenePointCloud<pcl::PointXYZ>::getPointCloudRef() const; + extern template + shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> > ScenePointCloud<pcl::PointXYZRGB>::getPointCloudRef() const; + extern template + shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA> > ScenePointCloud<pcl::PointXYZRGBA>::getPointCloudRef() const; + + extern template + void ScenePointCloud<pcl::PointXYZ>::setPointCloud(const shared_ptr<pcl::PointCloud<pcl::PointXYZ> > point_cloud); + extern template + void ScenePointCloud<pcl::PointXYZRGB>::setPointCloud(const shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> > point_cloud); + extern template + void ScenePointCloud<pcl::PointXYZRGBA>::setPointCloud(const shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA> > point_cloud); + + extern template + void * ScenePointCloud<pcl::PointXYZ>::getData(); + extern template + void * ScenePointCloud<pcl::PointXYZRGB>::getData(); + extern template + void * ScenePointCloud<pcl::PointXYZRGBA>::getData(); + + #include "od/common/utils/Scene.hpp" + + #endif - template <typename PointType> - shared_ptr<pcl::PointCloud<PointType> > ODScenePointCloud<PointType>::getPointCloudRef() const - { - return point_cloud_; - } - - template <typename PointType> - void ODScenePointCloud<PointType>::setPointCloud(const shared_ptr<pcl::PointCloud<PointType> > point_cloud) - { - point_cloud_ = point_cloud; - } - - template <typename PointType> - void * ODScenePointCloud<PointType>::getData() - { - return (void *)point_cloud_.get(); - } } diff --git a/common/include/od/common/pipeline/ODTrainer.h b/common/include/od/common/pipeline/Trainer.h similarity index 83% rename from common/include/od/common/pipeline/ODTrainer.h rename to common/include/od/common/pipeline/Trainer.h index 5a84b977..6a0b13d7 100644 --- a/common/include/od/common/pipeline/ODTrainer.h +++ b/common/include/od/common/pipeline/Trainer.h @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -29,7 +29,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // #pragma once #include <boost/filesystem.hpp> -#include "od/common/pipeline/ODObjectDetector.h" +#include "od/common/pipeline/ObjectDetector.h" namespace od { @@ -38,12 +38,12 @@ namespace od * \author Kripasindhu Sarkar * */ - class ODTrainer: public ODDetectorCommon + class Trainer: public DetectorCommon { public: - ODTrainer(const std::string & training_input_location = std::string(""), const std::string & training_data_location = std::string("")) : - ODDetectorCommon(training_data_location) + Trainer(const std::string & training_input_location = std::string(""), const std::string & training_data_location = std::string("")) : + DetectorCommon(training_data_location) { training_input_location_ = training_data_location; } diff --git a/common/include/od/common/utils/FeatureDetector.h b/common/include/od/common/utils/FeatureDetector.h new file mode 100644 index 00000000..ad6ee32c --- /dev/null +++ b/common/include/od/common/utils/FeatureDetector.h @@ -0,0 +1,31 @@ +#pragma once +#include "od/common/utils/Shared_pointers.h" +#include "od/common/utils/FeatureDetectorInterface.h" +#include "od/common/utils/FeatureDetector2D.h" + + + +namespace od +{ + + class FeatureDetector + { + + public: + + FeatureDetector(FeatureType type); + FeatureDetector(const std::string & type); + + void computeKeypointsAndDescriptors(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints); + + void computeAndSave(const cv::Mat & image, const std::string & path); + + private: + + FeatureType mode_; + bool gpu_; + shared_ptr<FeatureDetectorIterface> feature_detector_; + + }; + +} \ No newline at end of file diff --git a/common/include/od/common/utils/ODFeatureDetector2D.h b/common/include/od/common/utils/FeatureDetector2D.h similarity index 67% rename from common/include/od/common/utils/ODFeatureDetector2D.h rename to common/include/od/common/utils/FeatureDetector2D.h index fa9763c5..4dd583ab 100644 --- a/common/include/od/common/utils/ODFeatureDetector2D.h +++ b/common/include/od/common/utils/FeatureDetector2D.h @@ -2,19 +2,19 @@ // Created by sarkar on 20.04.15. // #pragma once -#include "od/common/utils/ODFeatureDetectorInterface.h" +#include "od/common/utils/FeatureDetectorInterface.h" #include <opencv2/xfeatures2d.hpp> namespace od { - class ODFeatureDetector2D: public ODFeatureDetectorIterface + class FeatureDetector2D: public FeatureDetectorIterface { public: - ODFeatureDetector2D(FeatureType type); + FeatureDetector2D(FeatureType type); void computeKeypointsAndDescriptors(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints); diff --git a/common/include/od/common/utils/ODFeatureDetectorInterface.h b/common/include/od/common/utils/FeatureDetectorInterface.h similarity index 95% rename from common/include/od/common/utils/ODFeatureDetectorInterface.h rename to common/include/od/common/utils/FeatureDetectorInterface.h index 7eeda355..c3ea8211 100644 --- a/common/include/od/common/utils/ODFeatureDetectorInterface.h +++ b/common/include/od/common/utils/FeatureDetectorInterface.h @@ -17,7 +17,7 @@ namespace od FeatureType string2FeatureType(const std::string & name); - class ODFeatureDetectorIterface + class FeatureDetectorIterface { public: diff --git a/common/include/od/common/utils/ODFrameGenerator.h b/common/include/od/common/utils/FrameGenerator.h similarity index 78% rename from common/include/od/common/utils/ODFrameGenerator.h rename to common/include/od/common/utils/FrameGenerator.h index 4369b002..a94acff3 100644 --- a/common/include/od/common/utils/ODFrameGenerator.h +++ b/common/include/od/common/utils/FrameGenerator.h @@ -1,6 +1,6 @@ #pragma once -#include "od/common/pipeline/ODDetection.h" -#include "od/common/pipeline/ODScene.h" +#include "od/common/pipeline/Detection.h" +#include "od/common/pipeline/Scene.h" #include <iostream> #include <opencv2/videoio.hpp> #include <pcl/apps/3d_rec_framework/tools/openni_frame_source.h> @@ -18,19 +18,19 @@ namespace od * Templated with two parameters - SceneType identifying a Scene class, and TYPE identifying the type of input. After the instantiation with a correct TYPE, use the function * getNextFrame() to get an instance of next scene of SceneType. getNextFrame() returns valid scenes until all scenes matched are exhausted - the time when 'isValid()' is false. * - * \tparam SceneT One of the Scene classes - ODSceneImage or ODScenePointCloud + * \tparam SceneT One of the Scene classes - SceneImage or ScenePointCloud * \tparam TYPE TYPE can be GENERATOR_TYPE_FILE_LIST which means you provide the list of scene files to be returned by the FrameGenerator in the constructor (for eg. \/home/username/pics/\*.jpg) * Or it can be GENERATOR_TYPE_DEVICE which picks up the webcam or the kinect based on the SceneType. * \author Kripasindhu Sarkar * */ template<typename SceneT, GeneratorType TYPE> - class ODFrameGenerator + class FrameGenerator { public: - ODFrameGenerator(const std::string & input = std::string("")); - ODFrameGenerator(int input = 0){} + FrameGenerator(const std::string & input = std::string("")); + FrameGenerator(int input = 0){} shared_ptr<SceneT> getNextFrame(); @@ -48,10 +48,10 @@ namespace od template<typename SceneT> - class ODFrameGenerator<SceneT, GENERATOR_TYPE_FILE_LIST> + class FrameGenerator<SceneT, GENERATOR_TYPE_FILE_LIST> { public: - ODFrameGenerator(const std::string & input = std::string("")) + FrameGenerator(const std::string & input = std::string("")) { file_list_ = od_glob(input); curr_image_ = -1; @@ -98,11 +98,11 @@ namespace od /* template<typename SceneT, template<class, class> class TContainer, class TObject> - class ODFrameGenerator<SceneT, GENERATOR_TYPE_CONTAINER> + class FrameGenerator<SceneT, GENERATOR_TYPE_CONTAINER> { public: - explicit ODFrameGenerator(TContainer<TObject*, std::allocator<TObject*>> & container) :container_(container) + explicit FrameGenerator(TContainer<TObject*, std::allocator<TObject*>> & container) :container_(container) { curr_image_ = -1; exhausted_ = false; @@ -138,11 +138,11 @@ namespace od template<> - class ODFrameGenerator<ODSceneImage, GENERATOR_TYPE_DEVICE> + class FrameGenerator<SceneImage, GENERATOR_TYPE_DEVICE> { public: - ODFrameGenerator(const std::string & input = std::string("")) + FrameGenerator(const std::string & input = std::string("")) { std::cout << "Opening :" << input << std::endl; input_capture_.open(input); @@ -150,7 +150,7 @@ namespace od {std::cout << "FATAL: Cannot open video capture!" << std::endl;} } - ODFrameGenerator(int input = 0) + FrameGenerator(int input = 0) { std::cout << "Opening cam :" << input << std::endl; input_capture_.open(input); @@ -158,11 +158,11 @@ namespace od {std::cout << "FATAL: Cannot open video capture!" << std::endl;} } - shared_ptr<ODSceneImage> getNextFrame() + shared_ptr<SceneImage> getNextFrame() { cv::Mat frame; input_capture_.read(frame); - return make_shared<ODSceneImage>(frame); + return make_shared<SceneImage>(frame); } bool isValid() @@ -175,12 +175,12 @@ namespace od /* template<> - class ODFrameGenerator<ODScenePointCloud<pcl::PointXYZRGBA> , GENERATOR_TYPE_DEVICE> + class FrameGenerator<ScenePointCloud<pcl::PointXYZRGBA> , GENERATOR_TYPE_DEVICE> { public: - ODFrameGenerator(std::string input = "") + FrameGenerator(std::string input = "") { camera_ = new OpenNIFrameSource::OpenNIFrameSource(input); @@ -193,11 +193,11 @@ namespace od return keyboard_cb; } - ODScenePointCloud<pcl::PointXYZRGBA> * getNextFrame() + ScenePointCloud<pcl::PointXYZRGBA> * getNextFrame() { OpenNIFrameSource::PointCloudPtr frame = camera_->snap(); - return new ODScenePointCloud<pcl::PointXYZRGBA>(frame); + return new ScenePointCloud<pcl::PointXYZRGBA>(frame); } bool isValid() {return camera_->isActive();} @@ -208,7 +208,7 @@ namespace od template<typename PointT> - class ODFrameGenerator<ODScenePointCloud<PointT>, GENERATOR_TYPE_DEVICE> + class FrameGenerator<ScenePointCloud<PointT>, GENERATOR_TYPE_DEVICE> { public: @@ -217,24 +217,24 @@ namespace od typedef typename pcl::PointCloud<PointT>::ConstPtr PointCloudConstPtr; /* A simple class for capturing data from an OpenNI camera */ - ODFrameGenerator(const std::string & input = std::string("")) : grabber_(input), most_recent_frame_(), frame_counter_(0), active_(true) + FrameGenerator(const std::string & input = std::string("")) : grabber_(input), most_recent_frame_(), frame_counter_(0), active_(true) { - boost::function<void(const PointCloudConstPtr&)> frame_cb = boost::bind(&ODFrameGenerator<ODScenePointCloud<PointT> , GENERATOR_TYPE_DEVICE>::onNewFrame, this, _1); + boost::function<void(const PointCloudConstPtr&)> frame_cb = boost::bind(&FrameGenerator<ScenePointCloud<PointT> , GENERATOR_TYPE_DEVICE>::onNewFrame, this, _1); grabber_.registerCallback(frame_cb); grabber_.start (); boost::this_thread::sleep(boost::posix_time::seconds(5)); } - ~ODFrameGenerator() + ~FrameGenerator() { grabber_.stop (); } - shared_ptr<ODScenePointCloud<PointT>> getNextFrame() + shared_ptr<ScenePointCloud<PointT>> getNextFrame() { OpenNIFrameSource::PointCloudPtr frame = snap(); - return make_shared<ODScenePointCloud<PointT>>(frame); + return make_shared<ScenePointCloud<PointT>>(frame); } bool isValid() diff --git a/common/include/od/common/utils/ODFeatureDetector.h b/common/include/od/common/utils/ODFeatureDetector.h deleted file mode 100644 index c82bb6d5..00000000 --- a/common/include/od/common/utils/ODFeatureDetector.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once -#include "od/common/utils/ODShared_pointers.h" -#include "od/common/utils/ODFeatureDetectorInterface.h" -#include "od/common/utils/ODFeatureDetector2D.h" - - - -namespace od -{ - - class ODFeatureDetector - { - - public: - - ODFeatureDetector(FeatureType type); - ODFeatureDetector(const std::string & type); - - void computeKeypointsAndDescriptors(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints); - - void computeAndSave(const cv::Mat & image, const std::string & path); - - private: - - FeatureType mode_; - bool gpu_; - shared_ptr<ODFeatureDetectorIterface> feature_detector_; - - }; - -} \ No newline at end of file diff --git a/common/include/od/common/utils/ODViewer.h b/common/include/od/common/utils/ODViewer.h deleted file mode 100644 index 7a53b280..00000000 --- a/common/include/od/common/utils/ODViewer.h +++ /dev/null @@ -1,141 +0,0 @@ -#pragma once -#include <opencv2/core/core.hpp> -#include <opencv2/highgui/highgui.hpp> -#include <opencv2/imgproc/imgproc.hpp> - -#include <pcl/visualization/cloud_viewer.h> -#include <pcl/point_cloud.h> -#include <pcl/point_types.h> - -#include <iostream> -#include <string> - -#include <vtkRenderWindow.h> - -#include "od/common/utils/ODShared_pointers.h" -#include "od/common/pipeline/ODScene.h" - - -namespace od { - - /** \brief The viewer class. - * - * This class is used to visualize all types handled by the OpenDetection Library. For now it is possible to display cv::Mat and pcl::PointCloud<PointT> >. - * - * \author Giacomo Dabisias - * - */ - class ODViewer { - - enum odViewType { - UNDEFINED, - POINTCLOUD, - CVMAT - }; - - public: - - ODViewer(); - - template<typename PointT> - void render(shared_ptr<pcl::PointCloud<PointT> > to_display, const std::string & cloud_name, bool colored = true); - - template<typename PointT> - void render(shared_ptr<ODScenePointCloud<PointT> > to_display, const std::string & cloud_name, bool colored = true); - - template<typename PointT> - void render(const ODScenePointCloud<PointT> & to_display, const std::string & cloud_name, bool colored = true); - - template<typename PointT> - void render(shared_ptr<pcl::PointCloud<PointT> > to_display, - pcl::visualization::PointCloudColorHandlerRandom<PointT> & random_handler, const std::string & cloud_name); - - void render(const cv::Mat & to_display, const std::string & window_name); - void render(shared_ptr<ODSceneImage> to_display, const std::string & window_name); - void render(const ODSceneImage & to_display, const std::string & window_name); - - void initCVWindow(const std::string & window_name); - void initPCLWindow(const std::string & window_name); - - template<typename PointT> - void update(shared_ptr<pcl::PointCloud<PointT> > to_display, const std::string & cloud_name, bool colored = true); - - template<typename PointT> - void update(shared_ptr<ODScenePointCloud<PointT> > to_display, const std::string & cloud_name, bool colored = true); - - template<typename PointT> - void update(const ODScenePointCloud<PointT> & to_display, const std::string & cloud_name, bool colored = true); - - void update(const cv::Mat & to_display, const std::string & window_name); - void update(const ODSceneImage & to_display, const std::string & window_name); - void update(shared_ptr<ODSceneImage> to_display, const std::string & window_name); - - void setBackGround(const cv::Scalar & color); - void setBackGround(unsigned int r, unsigned int g, unsigned int b); - - void registerCallback(void(*callback)(const pcl::visualization::KeyboardEvent &, void *), void * data = nullptr); - void registerCallback(const std::string & window_name, CvMouseCallback on_mouse, void * data = nullptr); - - void spin(); - bool toStop(); - - void addText(const std::string & text, pcl::PointXYZ pos, double textScale, cv::Scalar color); - void addText(const std::string & text, cv::Point3f pos, double textScale, cv::Scalar color); - - void removeText(const std::string & text); - - void remove(const std::string & name); - void removeAll(); - - void removeShape(const std::string & text); - void removeAllShapes(); - - unsigned int wait(unsigned int time) const; - - shared_ptr<pcl::visualization::PCLVisualizer> getViewer(); - - private: - - odViewType status_; - shared_ptr<pcl::visualization::PCLVisualizer> viewer_; - std::string mat_window_name_, pcl_window_name_; - std::vector<std::string> clouds_; - shared_ptr<cv::Mat> mat_; - - }; - - extern template void ODViewer::render<pcl::PointXYZ>(shared_ptr<pcl::PointCloud<pcl::PointXYZ> >, const std::string &, bool); - extern template void ODViewer::render<pcl::PointXYZRGB>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> >, const std::string &, bool); - extern template void ODViewer::render<pcl::PointXYZRGBA>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA> >, const std::string &, bool); - - extern template void ODViewer::render<pcl::PointXYZ>(shared_ptr<pcl::PointCloud<pcl::PointXYZ> > to_display, - pcl::visualization::PointCloudColorHandlerRandom<pcl::PointXYZ> & random_handler, const std::string & cloud_name); - extern template void ODViewer::render<pcl::PointXYZRGB>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> > to_display, - pcl::visualization::PointCloudColorHandlerRandom<pcl::PointXYZRGB> & random_handler, const std::string & cloud_name); - extern template void ODViewer::render<pcl::PointXYZRGBA>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA> > to_display, - pcl::visualization::PointCloudColorHandlerRandom<pcl::PointXYZRGBA> & random_handler, const std::string & cloud_name); - - extern template void ODViewer::render<pcl::PointXYZ>(shared_ptr<ODScenePointCloud<pcl::PointXYZ> >, const std::string &, bool); - extern template void ODViewer::render<pcl::PointXYZRGB>(shared_ptr<ODScenePointCloud<pcl::PointXYZRGB> >, const std::string &, bool); - extern template void ODViewer::render<pcl::PointXYZRGBA>(shared_ptr<ODScenePointCloud<pcl::PointXYZRGBA> >, const std::string &, bool); - - extern template void ODViewer::render<pcl::PointXYZ>(const ODScenePointCloud<pcl::PointXYZ> &, const std::string &, bool); - extern template void ODViewer::render<pcl::PointXYZRGB>(const ODScenePointCloud<pcl::PointXYZRGB> &, const std::string &, bool); - extern template void ODViewer::render<pcl::PointXYZRGBA>(const ODScenePointCloud<pcl::PointXYZRGBA> &, const std::string &, bool); - - extern template void ODViewer::update<pcl::PointXYZ>(shared_ptr<pcl::PointCloud<pcl::PointXYZ> >, const std::string &, bool); - extern template void ODViewer::update<pcl::PointXYZRGB>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> >, const std::string &, bool); - extern template void ODViewer::update<pcl::PointXYZRGBA>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA> >, const std::string &, bool); - - extern template void ODViewer::update<pcl::PointXYZ>(shared_ptr<ODScenePointCloud<pcl::PointXYZ> >, const std::string &, bool); - extern template void ODViewer::update<pcl::PointXYZRGB>(shared_ptr<ODScenePointCloud<pcl::PointXYZRGB> >, const std::string &, bool); - extern template void ODViewer::update<pcl::PointXYZRGBA>(shared_ptr<ODScenePointCloud<pcl::PointXYZRGBA> >, const std::string &, bool); - - extern template void ODViewer::update<pcl::PointXYZ>(const ODScenePointCloud<pcl::PointXYZ> &, const std::string &, bool); - extern template void ODViewer::update<pcl::PointXYZRGB>(const ODScenePointCloud<pcl::PointXYZRGB> &, const std::string &, bool); - extern template void ODViewer::update<pcl::PointXYZRGBA>(const ODScenePointCloud<pcl::PointXYZRGBA> &, const std::string &, bool); - - -} - -#include "od/common/utils/ODViewer.hpp" diff --git a/common/include/od/common/utils/ODShared_pointers.h b/common/include/od/common/utils/Shared_pointers.h similarity index 100% rename from common/include/od/common/utils/ODShared_pointers.h rename to common/include/od/common/utils/Shared_pointers.h diff --git a/common/include/od/common/utils/ODUtils.h b/common/include/od/common/utils/Utils.h similarity index 95% rename from common/include/od/common/utils/ODUtils.h rename to common/include/od/common/utils/Utils.h index 6e1de376..e7ff573f 100644 --- a/common/include/od/common/utils/ODUtils.h +++ b/common/include/od/common/utils/Utils.h @@ -17,7 +17,7 @@ #define X_DEFINE_ENUM_WITH_STRING_CONVERSIONS_TOSTRING_CASE(r, data, elem) \ case elem : return BOOST_PP_STRINGIZE(elem); -#define OD_DEFINE_ENUM_WITH_STRING_CONVERSIONS(name, enumerators) \ +#define _DEFINE_ENUM_WITH_STRING_CONVERSIONS(name, enumerators) \ enum name { \ BOOST_PP_SEQ_ENUM(enumerators) \ }; \ @@ -39,7 +39,7 @@ namespace od { - //TODO REMOVE AND SUBSTITUTE USING BOOST FILESYSTEM + //TO REMOVE AND SUBSTITUTE USING BOOST FILESYSTEM std::vector<std::string> od_glob(const std::string & pat); void normL2(cv::Mat &descriptors); @@ -71,7 +71,7 @@ namespace od void getFilesInDirectory(const boost::filesystem::path & dir, const std::string & rel_path_so_far, std::vector<std::string> & relative_paths, const std::string & ext); void createTrainingDir(const std::string & training_dir); - //TODO REMOVE + //TO REMOVE void getArgvArgc(std::string const & commandline, char ***argv, int & argc); } } diff --git a/common/include/od/common/utils/Viewer.h b/common/include/od/common/utils/Viewer.h new file mode 100644 index 00000000..b2989c09 --- /dev/null +++ b/common/include/od/common/utils/Viewer.h @@ -0,0 +1,143 @@ +#pragma once +#include <opencv2/core/core.hpp> +#include <opencv2/highgui/highgui.hpp> +#include <opencv2/imgproc/imgproc.hpp> + +#include <pcl/visualization/cloud_viewer.h> +#include <pcl/point_cloud.h> +#include <pcl/point_types.h> + +#include <iostream> +#include <string> + +#include <vtkRenderWindow.h> + +#include "od/common/utils/Shared_pointers.h" +#include "od/common/pipeline/Scene.h" + + +namespace od { + + /** \brief The viewer class. + * + * This class is used to visualize all types handled by the OpenDetection Library. For now it is possible to display cv::Mat and pcl::PointCloud<PointT> >. + * + * \author Giacomo Dabisias + * + */ + class Viewer { + + enum odViewType { + UNDEFINED, + POINTCLOUD, + CVMAT + }; + + public: + + Viewer(); + + template<typename PointT> + void render(shared_ptr<pcl::PointCloud<PointT> > to_display, const std::string & cloud_name, bool colored = true); + + template<typename PointT> + void render(shared_ptr<ScenePointCloud<PointT> > to_display, const std::string & cloud_name, bool colored = true); + + template<typename PointT> + void render(const ScenePointCloud<PointT> & to_display, const std::string & cloud_name, bool colored = true); + + template<typename PointT> + void render(shared_ptr<pcl::PointCloud<PointT> > to_display, + pcl::visualization::PointCloudColorHandlerRandom<PointT> & random_handler, const std::string & cloud_name); + + void render(const cv::Mat & to_display, const std::string & window_name); + void render(shared_ptr<SceneImage> to_display, const std::string & window_name); + void render(const SceneImage & to_display, const std::string & window_name); + + void initCVWindow(const std::string & window_name); + void initPCLWindow(const std::string & window_name); + + template<typename PointT> + void update(shared_ptr<pcl::PointCloud<PointT> > to_display, const std::string & cloud_name, bool colored = true); + + template<typename PointT> + void update(shared_ptr<ScenePointCloud<PointT> > to_display, const std::string & cloud_name, bool colored = true); + + template<typename PointT> + void update(const ScenePointCloud<PointT> & to_display, const std::string & cloud_name, bool colored = true); + + void update(const cv::Mat & to_display, const std::string & window_name); + void update(const SceneImage & to_display, const std::string & window_name); + void update(shared_ptr<SceneImage> to_display, const std::string & window_name); + + void setBackGround(const cv::Scalar & color); + void setBackGround(unsigned int r, unsigned int g, unsigned int b); + + void registerCallback(void(*callback)(const pcl::visualization::KeyboardEvent &, void *), void * data = nullptr); + void registerCallback(const std::string & window_name, CvMouseCallback on_mouse, void * data = nullptr); + + void spin(); + bool toStop(); + + void addText(const std::string & text, pcl::PointXYZ pos, double textScale, cv::Scalar color); + void addText(const std::string & text, cv::Point3f pos, double textScale, cv::Scalar color); + + void removeText(const std::string & text); + + void remove(const std::string & name); + void removeAll(); + + void removeShape(const std::string & text); + void removeAllShapes(); + + unsigned int wait(unsigned int time) const; + + shared_ptr<pcl::visualization::PCLVisualizer> getViewer(); + + private: + + odViewType status_; + shared_ptr<pcl::visualization::PCLVisualizer> viewer_; + std::string mat_window_name_, pcl_window_name_; + std::vector<std::string> clouds_; + shared_ptr<cv::Mat> mat_; + + }; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + + extern template void Viewer::render<pcl::PointXYZ>(shared_ptr<pcl::PointCloud<pcl::PointXYZ> >, const std::string &, bool); + extern template void Viewer::render<pcl::PointXYZRGB>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> >, const std::string &, bool); + extern template void Viewer::render<pcl::PointXYZRGBA>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA> >, const std::string &, bool); + + extern template void Viewer::render<pcl::PointXYZ>(shared_ptr<pcl::PointCloud<pcl::PointXYZ> > to_display, + pcl::visualization::PointCloudColorHandlerRandom<pcl::PointXYZ> & random_handler, const std::string & cloud_name); + extern template void Viewer::render<pcl::PointXYZRGB>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> > to_display, + pcl::visualization::PointCloudColorHandlerRandom<pcl::PointXYZRGB> & random_handler, const std::string & cloud_name); + extern template void Viewer::render<pcl::PointXYZRGBA>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA> > to_display, + pcl::visualization::PointCloudColorHandlerRandom<pcl::PointXYZRGBA> & random_handler, const std::string & cloud_name); + + extern template void Viewer::render<pcl::PointXYZ>(shared_ptr<ScenePointCloud<pcl::PointXYZ> >, const std::string &, bool); + extern template void Viewer::render<pcl::PointXYZRGB>(shared_ptr<ScenePointCloud<pcl::PointXYZRGB> >, const std::string &, bool); + extern template void Viewer::render<pcl::PointXYZRGBA>(shared_ptr<ScenePointCloud<pcl::PointXYZRGBA> >, const std::string &, bool); + + extern template void Viewer::render<pcl::PointXYZ>(const ScenePointCloud<pcl::PointXYZ> &, const std::string &, bool); + extern template void Viewer::render<pcl::PointXYZRGB>(const ScenePointCloud<pcl::PointXYZRGB> &, const std::string &, bool); + extern template void Viewer::render<pcl::PointXYZRGBA>(const ScenePointCloud<pcl::PointXYZRGBA> &, const std::string &, bool); + + extern template void Viewer::update<pcl::PointXYZ>(shared_ptr<pcl::PointCloud<pcl::PointXYZ> >, const std::string &, bool); + extern template void Viewer::update<pcl::PointXYZRGB>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> >, const std::string &, bool); + extern template void Viewer::update<pcl::PointXYZRGBA>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA> >, const std::string &, bool); + + extern template void Viewer::update<pcl::PointXYZ>(shared_ptr<ScenePointCloud<pcl::PointXYZ> >, const std::string &, bool); + extern template void Viewer::update<pcl::PointXYZRGB>(shared_ptr<ScenePointCloud<pcl::PointXYZRGB> >, const std::string &, bool); + extern template void Viewer::update<pcl::PointXYZRGBA>(shared_ptr<ScenePointCloud<pcl::PointXYZRGBA> >, const std::string &, bool); + + extern template void Viewer::update<pcl::PointXYZ>(const ScenePointCloud<pcl::PointXYZ> &, const std::string &, bool); + extern template void Viewer::update<pcl::PointXYZRGB>(const ScenePointCloud<pcl::PointXYZRGB> &, const std::string &, bool); + extern template void Viewer::update<pcl::PointXYZRGBA>(const ScenePointCloud<pcl::PointXYZRGBA> &, const std::string &, bool); + +#endif +} + +#include "od/common/utils/Viewer.hpp" diff --git a/common/src/bindings/ODSvmlight.cpp b/common/src/bindings/Svmlight.cpp similarity index 98% rename from common/src/bindings/ODSvmlight.cpp rename to common/src/bindings/Svmlight.cpp index 82411ccc..f541020a 100644 --- a/common/src/bindings/ODSvmlight.cpp +++ b/common/src/bindings/Svmlight.cpp @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -49,7 +49,7 @@ Unless required by applicable law or agreed to in writing, software distributed * */ -#include "od/common/bindings/ODSvmlight.h" +#include "od/common/bindings/Svmlight.h" SVMlight::SVMlight(){ // Init variables diff --git a/common/src/pipeline/Detection.cpp b/common/src/pipeline/Detection.cpp new file mode 100644 index 00000000..1118d976 --- /dev/null +++ b/common/src/pipeline/Detection.cpp @@ -0,0 +1,290 @@ +#include "od/common/pipeline/Detection.h" + +namespace od +{ + + Detection::Detection(const DetectionType & type, const std::string & id, double confidence) : + type_(type), id_(id), confidence_(confidence) {} + + void Detection::printSelf() + { + std::cout << "--Detection-- \nType: " << enumToString(type_) << std::endl; + std::cout << "ID: " << id_ << std::endl; + } + + const Detection::DetectionType & Detection::getType() const + { + return type_; + } + + void Detection::setType(const DetectionType & type) + { + type_ = type; + } + + const std::string & Detection::getId() const + { + return id_; + } + + void Detection::setId(const std::string & id) + { + id_ = id_; + } + + double Detection::getConfidence() const + { + return confidence_; + } + + + void Detection::setConfidence(double confidence) + { + confidence_ = confidence; + } + + + + + Detection2D::Detection2D(const DetectionType & type, const std::string & id , double confidence) : Detection(type, id, confidence) + { + location_2d_ = Eigen::Vector3d::UnitZ(); + } + + const Eigen::Vector3d & Detection2D::getLocation() const + { + return location_2d_; + } + + void Detection2D::setLocation(const Eigen::Vector3d & location) + { + location_2d_ = location; + } + + const cv::Rect & Detection2D::getBoundingBox() const + { + return bounding_box_2d_; + } + + void Detection2D::setBoundingBox(const cv::Rect & bounding_box) + { + bounding_box_2d_ = bounding_box; + } + + const cv::Mat & Detection2D::getMetainfoImage() const + { + return metainfo_image_; + } + + void Detection2D::setMetainfoImage(const cv::Mat & metainfo_image) + { + metainfo_image_ = metainfo_image; + } + + + + Detection3D::Detection3D(const DetectionType & type, const std::string & id, double confidence) : Detection(type, id, confidence) + { + location_3d_ = Eigen::Vector4d::UnitW(); + orientation_.setIdentity(); + scale_ = 1; + } + + const Eigen::Vector4d & Detection3D::getLocation() const + { + return location_3d_; + } + + void Detection3D::setLocation(const Eigen::Vector4d & location) + { + location_3d_ = location; + } + void Detection3D::setLocation(const cv::Mat & location) + { + cv::cv2eigen(location, location_3d_); + } + + const Eigen::Matrix3Xd & Detection3D::getPose() const + { + return orientation_; + } + + void Detection3D::setPose(const Eigen::Matrix3Xd & pose) + { + orientation_ = pose; + } + //TO why clone? + void Detection3D::setPose(const cv::Mat & pose_cv) + { + orientation_ = Eigen::Map<Eigen::Matrix3d>(pose_cv.clone().ptr<double>()); + } + + double Detection3D::getScale() const + { + return scale_; + } + + void Detection3D::setScale(double scale) + { + Detection3D::scale_ = scale; + } + + const cv::Mat & Detection3D::getMetainfoImage() const + { + return metainfo_image_; + } + + void Detection3D::setMetainfoImage(const cv::Mat & metainfo_image) + { + metainfo_image_ = metainfo_image; + } + + const pcl::PointCloud<pcl::PointXYZ>::Ptr & Detection3D::getMetainfoCluster() const + { + return metainfo_cluster_; + } + + void Detection3D::setMetainfoCluster(const pcl::PointCloud<pcl::PointXYZ>::Ptr & metainfo_cluster) + { + metainfo_cluster_ = metainfo_cluster; + } + + void Detection3D::printSelf() + { + Detection::printSelf(); + std::cout << "Location: " << location_3d_ << std::endl; + std::cout << "Pose: " << orientation_ << std::endl; + std::cout << "Scale: " << scale_ << std::endl; + } + + + Detections::Detections(unsigned int n): detections_(n) {} + + Detections::~Detections() + { + detections_.resize(0); + } + + unsigned int Detections::size() const + { + return detections_.size(); + } + + void Detections::push_back(shared_ptr<Detection> detection) + { + detections_.push_back(detection); + } + + void Detections::append(shared_ptr<Detections> detections) + { + detections_.insert(detections_.end(), detections->detections_.begin(), detections->detections_.end()); + } + + shared_ptr<Detection> Detections::operator[](unsigned int i) + { + return detections_[i]; + } + + shared_ptr<Detection> Detections::at(unsigned int i) + { + return detections_[i]; + } + + const cv::Mat & Detections::getMetainfoImage() const + { + return metainfo_image_; + } + + void Detections::setMetainfoImage(const cv::Mat & metainfo_image) + { + metainfo_image_ = metainfo_image.clone(); + } + + const pcl::PointCloud<pcl::PointXYZ>::Ptr & Detections::getMetainfoCluster() const + { + return metainfo_cluster_; + } + + void Detections::setMetainfoCluster(const pcl::PointCloud<pcl::PointXYZ>::Ptr & metainfo_cluster) + { + metainfo_cluster_ = metainfo_cluster; + } + + + SceneImage Detections2D::renderMetainfo(SceneImage & input) + { + + //picking up random colors for different detection algorithm, if exist + /*std::map<std::string, cv::Scalar> color_map; + for(int i = 0; i < detections_.size(); i++) + { + if(color_map.find(detections_[i]->getId()) == color_map.end()) + color_map[detections_[i]->getId()] = CV_RGB(rand()%255, rand()%255, rand()%255); + }*/ + + cv::Mat image = input.getCVImage(); + for(size_t i = 0; i < detections_.size(); ++i) + { + shared_ptr<Detection2D> detection = dynamic_pointer_cast<Detection2D>(detections_[i]); + if(detection) + cv::rectangle(image, detection->bounding_box_2d_, getHashedColor(detections_[i]->getId(), 100), 2); + } + return SceneImage(image); + } + + SceneImage Detections2D::renderMetainfo(shared_ptr<SceneImage> input) + { + + //picking up random colors for different detection algorithm, if exist + /*std::map<std::string, cv::Scalar> color_map; + for(int i = 0; i < detections_.size(); i++) + { + if(color_map.find(detections_[i]->getId()) == color_map.end()) + color_map[detections_[i]->getId()] = CV_RGB(rand()%255, rand()%255, rand()%255); + }*/ + + cv::Mat image = input->getCVImage().clone(); + for(size_t i = 0; i < detections_.size(); ++i) + { + shared_ptr<Detection2D> detection = dynamic_pointer_cast<Detection2D>(detections_[i]); + if(detection) + cv::rectangle(image, detection->bounding_box_2d_, getHashedColor(detections_[i]->getId(), 100), 2); + } + return SceneImage(image); + } + + + shared_ptr<Detection2D> Detections2D::operator[](unsigned int i) + { + return dynamic_pointer_cast<Detection2D>(detections_[i]); + } + + shared_ptr<Detection2D> Detections2D::at(unsigned int i) + { + return dynamic_pointer_cast<Detection2D>(detections_[i]); + } + + + shared_ptr<Detection3D> Detections3D::operator[](unsigned int i) + { + return dynamic_pointer_cast<Detection3D>(detections_[i]); + } + + shared_ptr<Detection3D> Detections3D::at(unsigned int i) + { + return dynamic_pointer_cast<Detection3D>(detections_[i]); + } + + + shared_ptr<DetectionComplete> DetectionsComplete::operator[](unsigned int i) + { + return dynamic_pointer_cast<DetectionComplete>(detections_[i]); + } + + shared_ptr<DetectionComplete> DetectionsComplete::at(unsigned int i) + { + return dynamic_pointer_cast<DetectionComplete>(detections_[i]); + } + + +} \ No newline at end of file diff --git a/common/src/pipeline/ODDetection.cpp b/common/src/pipeline/ODDetection.cpp deleted file mode 100644 index 616d8150..00000000 --- a/common/src/pipeline/ODDetection.cpp +++ /dev/null @@ -1,291 +0,0 @@ -#include "od/common/pipeline/ODDetection.h" - -namespace od -{ - - ODDetection::ODDetection(const DetectionType & type, const std::string & id, double confidence) : - type_(type), id_(id), confidence_(confidence) {} - - void ODDetection::printSelf() - { - std::cout << "--Detection-- \nType: " << enumToString(type_) << std::endl; - std::cout << "ID: " << id_ << std::endl; - } - - const ODDetection::DetectionType & ODDetection::getType() const - { - return type_; - } - - void ODDetection::setType(const DetectionType & type) - { - type_ = type; - } - - const std::string & ODDetection::getId() const - { - return id_; - } - - void ODDetection::setId(const std::string & id) - { - id_ = id_; - } - - double ODDetection::getConfidence() const - { - return confidence_; - } - - - void ODDetection::setConfidence(double confidence) - { - confidence_ = confidence; - } - - - - - ODDetection2D::ODDetection2D(const DetectionType & type, const std::string & id , double confidence) : - ODDetection(type, id, confidence) - { - location_2d_ = Eigen::Vector3d::UnitZ(); - } - - const Eigen::Vector3d & ODDetection2D::getLocation() const - { - return location_2d_; - } - - void ODDetection2D::setLocation(const Eigen::Vector3d & location) - { - location_2d_ = location; - } - - const cv::Rect & ODDetection2D::getBoundingBox() const - { - return bounding_box_2d_; - } - - void ODDetection2D::setBoundingBox(const cv::Rect & bounding_box) - { - bounding_box_2d_ = bounding_box; - } - - const cv::Mat & ODDetection2D::getMetainfoImage() const - { - return metainfo_image_; - } - - void ODDetection2D::setMetainfoImage(const cv::Mat & metainfo_image) - { - metainfo_image_ = metainfo_image; - } - - - - ODDetection3D::ODDetection3D(const DetectionType & type, const std::string & id, double confidence) : - ODDetection(type, id, confidence) - { - location_3d_ = Eigen::Vector4d::UnitW(); - orientation_.setIdentity(); - scale_ = 1; - } - - const Eigen::Vector4d & ODDetection3D::getLocation() const - { - return location_3d_; - } - - void ODDetection3D::setLocation(const Eigen::Vector4d & location) - { - location_3d_ = location; - } - void ODDetection3D::setLocation(const cv::Mat & location) - { - cv::cv2eigen(location, location_3d_); - } - - const Eigen::Matrix3Xd & ODDetection3D::getPose() const - { - return orientation_; - } - - void ODDetection3D::setPose(const Eigen::Matrix3Xd & pose) - { - orientation_ = pose; - } - void ODDetection3D::setPose(const cv::Mat & pose_cv) - { - orientation_ = Eigen::Map<Eigen::Matrix3d>(pose_cv.clone().ptr<double>()); - } - - double ODDetection3D::getScale() const - { - return scale_; - } - - void ODDetection3D::setScale(double scale) - { - ODDetection3D::scale_ = scale; - } - - const cv::Mat & ODDetection3D::getMetainfoImage() const - { - return metainfo_image_; - } - - void ODDetection3D::setMetainfoImage(const cv::Mat & metainfo_image) - { - metainfo_image_ = metainfo_image; - } - - const pcl::PointCloud<pcl::PointXYZ>::Ptr & ODDetection3D::getMetainfoCluster() const - { - return metainfo_cluster_; - } - - void ODDetection3D::setMetainfoCluster(const pcl::PointCloud<pcl::PointXYZ>::Ptr & metainfo_cluster) - { - metainfo_cluster_ = metainfo_cluster; - } - - void ODDetection3D::printSelf() - { - ODDetection::printSelf(); - std::cout << "Location: " << location_3d_ << std::endl; - std::cout << "Pose: " << orientation_ << std::endl; - std::cout << "Scale: " << scale_ << std::endl; - } - - - ODDetections::ODDetections(unsigned int n): detections_(n) {} - - ODDetections::~ODDetections() - { - detections_.resize(0); - } - - unsigned int ODDetections::size() const - { - return detections_.size(); - } - - void ODDetections::push_back(shared_ptr<ODDetection> detection) - { - detections_.push_back(detection); - } - - void ODDetections::append(shared_ptr<ODDetections> detections) - { - detections_.insert(detections_.end(), detections->detections_.begin(), detections->detections_.end()); - } - - shared_ptr<ODDetection> ODDetections::operator[](unsigned int i) - { - return detections_[i]; - } - - shared_ptr<ODDetection> ODDetections::at(unsigned int i) - { - return detections_[i]; - } - - const cv::Mat & ODDetections::getMetainfoImage() const - { - return metainfo_image_; - } - - void ODDetections::setMetainfoImage(const cv::Mat & metainfo_image) - { - metainfo_image_ = metainfo_image.clone(); - } - - const pcl::PointCloud<pcl::PointXYZ>::Ptr & ODDetections::getMetainfoCluster() const - { - return metainfo_cluster_; - } - - void ODDetections::setMetainfoCluster(const pcl::PointCloud<pcl::PointXYZ>::Ptr & metainfo_cluster) - { - metainfo_cluster_ = metainfo_cluster; - } - - - ODSceneImage ODDetections2D::renderMetainfo(ODSceneImage & input) - { - - //picking up random colors for different detection algorithm, if exist - /*std::map<std::string, cv::Scalar> color_map; - for(int i = 0; i < detections_.size(); i++) - { - if(color_map.find(detections_[i]->getId()) == color_map.end()) - color_map[detections_[i]->getId()] = CV_RGB(rand()%255, rand()%255, rand()%255); - }*/ - - cv::Mat image = input.getCVImage(); - for(size_t i = 0; i < detections_.size(); ++i) - { - shared_ptr<ODDetection2D> detection = dynamic_pointer_cast<ODDetection2D>(detections_[i]); - if(detection) - cv::rectangle(image, detection->bounding_box_2d_, getHashedColor(detections_[i]->getId(), 100), 2); - } - return ODSceneImage(image); - } - - ODSceneImage ODDetections2D::renderMetainfo(shared_ptr<ODSceneImage> input) - { - - //picking up random colors for different detection algorithm, if exist - /*std::map<std::string, cv::Scalar> color_map; - for(int i = 0; i < detections_.size(); i++) - { - if(color_map.find(detections_[i]->getId()) == color_map.end()) - color_map[detections_[i]->getId()] = CV_RGB(rand()%255, rand()%255, rand()%255); - }*/ - - cv::Mat image = input->getCVImage().clone(); - for(size_t i = 0; i < detections_.size(); ++i) - { - shared_ptr<ODDetection2D> detection = dynamic_pointer_cast<ODDetection2D>(detections_[i]); - if(detection) - cv::rectangle(image, detection->bounding_box_2d_, getHashedColor(detections_[i]->getId(), 100), 2); - } - return ODSceneImage(image); - } - - - shared_ptr<ODDetection2D> ODDetections2D::operator[](unsigned int i) - { - return dynamic_pointer_cast<ODDetection2D>(detections_[i]); - } - - shared_ptr<ODDetection2D> ODDetections2D::at(unsigned int i) - { - return dynamic_pointer_cast<ODDetection2D>(detections_[i]); - } - - - shared_ptr<ODDetection3D> ODDetections3D::operator[](unsigned int i) - { - return dynamic_pointer_cast<ODDetection3D>(detections_[i]); - } - - shared_ptr<ODDetection3D> ODDetections3D::at(unsigned int i) - { - return dynamic_pointer_cast<ODDetection3D>(detections_[i]); - } - - - shared_ptr<ODDetectionComplete> ODDetectionsComplete::operator[](unsigned int i) - { - return dynamic_pointer_cast<ODDetectionComplete>(detections_[i]); - } - - shared_ptr<ODDetectionComplete> ODDetectionsComplete::at(unsigned int i) - { - return dynamic_pointer_cast<ODDetectionComplete>(detections_[i]); - } - - -} \ No newline at end of file diff --git a/common/src/pipeline/ODScene.cpp b/common/src/pipeline/ODScene.cpp deleted file mode 100644 index 39f42c4f..00000000 --- a/common/src/pipeline/ODScene.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* -Copyright (c) 2015, Kripasindhu Sarkar -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions 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. - * Neither the name of the copyright holder(s) nor the - names of its contributors may 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 COPYRIGHT HOLDER 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. -*/ -// -// Created by sarkar on 10.06.15. -// - -#include "od/common/pipeline/ODScene.h" - -namespace od { - - std::string const & ODScene::getPath() const - { - return path_; - } - - - ODSceneImage::ODSceneImage(const cv::Mat & cvimage): is_trained_(false) - { - cvimage_ = cvimage.clone(); - } - - ODSceneImage::ODSceneImage(const std::string & path): is_trained_(false) - { - cvimage_ = cv::imread(path); - path_ = path; - } - - const std::vector<cv::KeyPoint> & ODSceneImage::getKeypoints() const - { - return keypoints_; - } - - void ODSceneImage::setKeypoints(const std::vector<cv::KeyPoint> & keypoints) - { - keypoints_ = keypoints; - } - - const cv::Mat & ODSceneImage::getDescriptors() const - { - return descriptors_; - } - - void ODSceneImage::setDescriptors(const cv::Mat & descriptors) - { - descriptors_ = descriptors; - is_trained_ = true; - } - - cv::Mat ODSceneImage::getCVImage() const - { - return cvimage_; - } - - void * ODSceneImage::getData() - { - return &cvimage_; - } - -} \ No newline at end of file diff --git a/common/src/pipeline/ODObjectDetector.cpp b/common/src/pipeline/ObjectDetector.cpp similarity index 79% rename from common/src/pipeline/ODObjectDetector.cpp rename to common/src/pipeline/ObjectDetector.cpp index 9f5ce999..37094315 100644 --- a/common/src/pipeline/ODObjectDetector.cpp +++ b/common/src/pipeline/ObjectDetector.cpp @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -28,11 +28,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 03.06.15. // -#include "od/common/pipeline/ODObjectDetector.h" +#include "od/common/pipeline/ObjectDetector.h" namespace od { - ODDetectorCommon::ODDetectorCommon(const std::string & trained_data_location) : trained_data_location_(trained_data_location) + DetectorCommon::DetectorCommon(const std::string & trained_data_location) : trained_data_location_(trained_data_location) { std::string classname = typeid(this).name(); trained_data_id_ = classname; @@ -40,42 +40,42 @@ namespace od { trained_location_identifier_ = classname; } - std::string ODDetectorCommon::getTrainingInputLocation() const + std::string DetectorCommon::getTrainingInputLocation() const { return training_input_location_; } - void ODDetectorCommon::setTrainingInputLocation(const std::string & training_input_location) + void DetectorCommon::setTrainingInputLocation(const std::string & training_input_location) { training_input_location_ = training_input_location; } - std::string ODDetectorCommon::getTrainedDataLocation() const + std::string DetectorCommon::getTrainedDataLocation() const { return trained_data_location_; } - void ODDetectorCommon::setTrainedDataLocation(const std::string & trained_data_location) + void DetectorCommon::setTrainedDataLocation(const std::string & trained_data_location) { trained_data_location_ = trained_data_location; } - std::string ODDetectorCommon::getSpecificTrainingDataLocation() + std::string DetectorCommon::getSpecificTrainingDataLocation() { return trained_data_location_ + "/" + "TD_" + trained_location_identifier_; } - std::string ODDetectorCommon::getSpecificTrainingData() + std::string DetectorCommon::getSpecificTrainingData() { return getSpecificTrainingDataLocation() + "/" + trained_data_id_; } - const std::string & ODDetectorCommon::getTrainedDataID() const + const std::string & DetectorCommon::getTrainedDataID() const { return trained_data_id_; } - void ODDetectorCommon::setTrainedDataID(const std::string & trainedDataID) + void DetectorCommon::setTrainedDataID(const std::string & trainedDataID) { trained_data_id_ = trainedDataID; } diff --git a/common/src/pipeline/Scene.cpp b/common/src/pipeline/Scene.cpp new file mode 100644 index 00000000..2bb6d886 --- /dev/null +++ b/common/src/pipeline/Scene.cpp @@ -0,0 +1,127 @@ +/* +Copyright (c) 2015, Kripasindhu Sarkar +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions 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. + * Neither the name of the copyright holder(s) nor the + names of its contributors may 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 COPYRIGHT HOLDER BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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. +*/ +// +// Created by sarkar on 10.06.15. +// + +#include "od/common/pipeline/Scene.h" + +namespace od { + + std::string const & Scene::getPath() const + { + return path_; + } + + + SceneImage::SceneImage(const cv::Mat & cvimage): is_trained_(false) + { + cvimage_ = cvimage.clone(); + } + + SceneImage::SceneImage(const std::string & path): is_trained_(false) + { + cvimage_ = cv::imread(path); + path_ = path; + } + + const std::vector<cv::KeyPoint> & SceneImage::getKeypoints() const + { + return keypoints_; + } + + void SceneImage::setKeypoints(const std::vector<cv::KeyPoint> & keypoints) + { + keypoints_ = keypoints; + } + + const cv::Mat & SceneImage::getDescriptors() const + { + return descriptors_; + } + + void SceneImage::setDescriptors(const cv::Mat & descriptors) + { + descriptors_ = descriptors; + is_trained_ = true; + } + + cv::Mat SceneImage::getCVImage() const + { + return cvimage_; + } + + void * SceneImage::getData() + { + return &cvimage_; + } + + + template + ScenePointCloud<pcl::PointXYZ>::ScenePointCloud(const shared_ptr<pcl::PointCloud<pcl::PointXYZ> > point_cloud); + template + ScenePointCloud<pcl::PointXYZRGB>::ScenePointCloud(const shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> > point_cloud); + template + ScenePointCloud<pcl::PointXYZRGBA>::ScenePointCloud(const shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA> > point_cloud); + + template + ScenePointCloud<pcl::PointXYZ>::ScenePointCloud(const std::string & point_cloud_file); + template + ScenePointCloud<pcl::PointXYZRGB>::ScenePointCloud(const std::string & point_cloud_file); + template + ScenePointCloud<pcl::PointXYZRGBA>::ScenePointCloud(const std::string & point_cloud_file); + + template + shared_ptr<pcl::PointCloud<pcl::PointXYZ> > ScenePointCloud<pcl::PointXYZ>::getPointCloud() const; + template + shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> > ScenePointCloud<pcl::PointXYZRGB>::getPointCloud() const; + template + shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA> > ScenePointCloud<pcl::PointXYZRGBA>::getPointCloud() const; + + template + shared_ptr<pcl::PointCloud<pcl::PointXYZ> > ScenePointCloud<pcl::PointXYZ>::getPointCloudRef() const; + template + shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> > ScenePointCloud<pcl::PointXYZRGB>::getPointCloudRef() const; + template + shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA> > ScenePointCloud<pcl::PointXYZRGBA>::getPointCloudRef() const; + + template + void ScenePointCloud<pcl::PointXYZ>::setPointCloud(const shared_ptr<pcl::PointCloud<pcl::PointXYZ> > point_cloud); + template + void ScenePointCloud<pcl::PointXYZRGB>::setPointCloud(const shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> > point_cloud); + template + void ScenePointCloud<pcl::PointXYZRGBA>::setPointCloud(const shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA> > point_cloud); + + template + void * ScenePointCloud<pcl::PointXYZ>::getData(); + template + void * ScenePointCloud<pcl::PointXYZRGB>::getData(); + template + void * ScenePointCloud<pcl::PointXYZRGBA>::getData(); + + +} \ No newline at end of file diff --git a/common/src/utils/ODFeatureDetector.cpp b/common/src/utils/FeatureDetector.cpp similarity index 53% rename from common/src/utils/ODFeatureDetector.cpp rename to common/src/utils/FeatureDetector.cpp index 7f49d7e2..179d403f 100644 --- a/common/src/utils/ODFeatureDetector.cpp +++ b/common/src/utils/FeatureDetector.cpp @@ -1,9 +1,9 @@ -#include "od/common/utils/ODFeatureDetector.h" +#include "od/common/utils/FeatureDetector.h" namespace od { - ODFeatureDetector::ODFeatureDetector(FeatureType type) + FeatureDetector::FeatureDetector(FeatureType type) { mode_ = type; @@ -13,13 +13,13 @@ namespace od case(SIFT) : case(ORB) : case(SURF) : - feature_detector_ = make_shared<ODFeatureDetector2D>(mode_); + feature_detector_ = make_shared<FeatureDetector2D>(mode_); gpu_ = false; break; case(SIFT_GPU): case(ORB_GPU): #if WIHT_GPU - feature_detector_ = make_shared<od::gpu::ODFeatureDetector2D>(mode_); + feature_detector_ = make_shared<od::gpu::FeatureDetector2D>(mode_); gpu_ = true; #else std::cout << "FATAL ERROR, gpu type is not compile. Recompile with WITH_GPU to enable GPU support." << std::endl; @@ -33,17 +33,17 @@ namespace od } } - ODFeatureDetector::ODFeatureDetector(const std::string & type) - { - ODFeatureDetector(string2FeatureType(type)); - } + FeatureDetector::FeatureDetector(const std::string & type) + { + FeatureDetector(string2FeatureType(type)); + } - void ODFeatureDetector::computeKeypointsAndDescriptors(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) + void FeatureDetector::computeKeypointsAndDescriptors(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) { feature_detector_->computeKeypointsAndDescriptors(image, descriptors,keypoints); } - void ODFeatureDetector::computeAndSave(const cv::Mat & image, const std::string & path) + void FeatureDetector::computeAndSave(const cv::Mat & image, const std::string & path) { feature_detector_->computeAndSave(image, path); } diff --git a/common/src/utils/ODFeatureDetector2D.cpp b/common/src/utils/FeatureDetector2D.cpp similarity index 67% rename from common/src/utils/ODFeatureDetector2D.cpp rename to common/src/utils/FeatureDetector2D.cpp index cb3bedec..94965760 100644 --- a/common/src/utils/ODFeatureDetector2D.cpp +++ b/common/src/utils/FeatureDetector2D.cpp @@ -2,12 +2,12 @@ // Created by sarkar on 20.04.15. // -#include "od/common/utils/ODFeatureDetector2D.h" +#include "od/common/utils/FeatureDetector2D.h" namespace od { - ODFeatureDetector2D::ODFeatureDetector2D(FeatureType type) + FeatureDetector2D::FeatureDetector2D(FeatureType type) { mode_ = type; @@ -36,7 +36,7 @@ namespace od } } - void ODFeatureDetector2D::computeKeypointsAndDescriptors(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) + void FeatureDetector2D::computeKeypointsAndDescriptors(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) { feature_detector_->detect(image, keypoints); @@ -44,12 +44,12 @@ namespace od } - void ODFeatureDetector2D::computeAndSave(const cv::Mat & image, const std::string & path) + void FeatureDetector2D::computeAndSave(const cv::Mat & image, const std::string & path) { cv::Mat descriptors; std::vector<cv::KeyPoint> keypoints; - //TODO implementation + //TO implementation } } diff --git a/common/src/utils/ODFeatureDetectorInterface.cpp b/common/src/utils/FeatureDetectorInterface.cpp similarity index 82% rename from common/src/utils/ODFeatureDetectorInterface.cpp rename to common/src/utils/FeatureDetectorInterface.cpp index 03caf360..b1d562c2 100644 --- a/common/src/utils/ODFeatureDetectorInterface.cpp +++ b/common/src/utils/FeatureDetectorInterface.cpp @@ -1,4 +1,4 @@ -#include "od/common/utils/ODFeatureDetectorInterface.h" +#include "od/common/utils/FeatureDetectorInterface.h" namespace od { diff --git a/common/src/utils/FrameGenerator.cpp b/common/src/utils/FrameGenerator.cpp new file mode 100644 index 00000000..e69de29b diff --git a/common/src/utils/ODUtils.cpp b/common/src/utils/Utils.cpp similarity index 99% rename from common/src/utils/ODUtils.cpp rename to common/src/utils/Utils.cpp index a045316c..d412f6fc 100644 --- a/common/src/utils/ODUtils.cpp +++ b/common/src/utils/Utils.cpp @@ -1,7 +1,7 @@ // // Created by sarkar on 19.06.15. // -#include "od/common/utils/ODUtils.h" +#include "od/common/utils/Utils.h" namespace od diff --git a/common/src/utils/ODViewer.cpp b/common/src/utils/Viewer.cpp similarity index 58% rename from common/src/utils/ODViewer.cpp rename to common/src/utils/Viewer.cpp index 7bc722a4..e3c8ae1d 100644 --- a/common/src/utils/ODViewer.cpp +++ b/common/src/utils/Viewer.cpp @@ -1,13 +1,13 @@ -#include "od/common/utils/ODViewer.h" +#include "od/common/utils/Viewer.h" #if(WIN32) #include <Windows.h> #endif namespace od { - ODViewer::ODViewer() : status_(UNDEFINED) {} + Viewer::Viewer() : status_(UNDEFINED) {} - void ODViewer::render(const cv::Mat & to_display, const std::string & window_name) + void Viewer::render(const cv::Mat & to_display, const std::string & window_name) { if(status_ != CVMAT){ std::cout << "Switching viewer to cv::Mat mode" << std::endl; @@ -33,17 +33,17 @@ namespace od { cvNamedWindow(mat_window_name_.c_str(), CV_WINDOW_AUTOSIZE); } - void ODViewer::render(const ODSceneImage & to_display, const std::string & window_name) + void Viewer::render(const SceneImage & to_display, const std::string & window_name) { render(to_display.getCVImage(), window_name); } - void ODViewer::render(shared_ptr<ODSceneImage> to_display, const std::string & window_name) + void Viewer::render(shared_ptr<SceneImage> to_display, const std::string & window_name) { render(to_display->getCVImage(), window_name); } - void ODViewer::initCVWindow(const std::string & window_name) + void Viewer::initCVWindow(const std::string & window_name) { if(status_ != CVMAT){ std::cout << "Switching viewer to cv::Mat mode" << std::endl; @@ -69,7 +69,7 @@ namespace od { cvNamedWindow(mat_window_name_.c_str(), CV_WINDOW_AUTOSIZE); } - void ODViewer::initPCLWindow(const std::string & window_name) + void Viewer::initPCLWindow(const std::string & window_name) { if(status_ != POINTCLOUD){ std::cout << "Switching viewer to PointCloud mode" << std::endl; @@ -82,7 +82,7 @@ namespace od { if(!viewer_ || pcl_window_name_ != window_name) { -#ifdef WITH_BOOST_SHARED_PTR +#ifndef WITH_BOOST_SHARED_PTR viewer_ = shared_ptr<pcl::visualization::PCLVisualizer>(new pcl::visualization::PCLVisualizer(window_name)); #else viewer_ = make_shared<pcl::visualization::PCLVisualizer>(window_name); @@ -93,7 +93,7 @@ namespace od { pcl_window_name_ = window_name; } - void ODViewer::update(const cv::Mat & to_display, const std::string & window_name) + void Viewer::update(const cv::Mat & to_display, const std::string & window_name) { if(status_ != CVMAT) { @@ -110,16 +110,16 @@ namespace od { } - void ODViewer::update(const ODSceneImage & to_display, const std::string & window_name) + void Viewer::update(const SceneImage & to_display, const std::string & window_name) { update(to_display.getCVImage(), window_name); } - void ODViewer::update(shared_ptr<ODSceneImage> to_display, const std::string & window_name) + void Viewer::update(shared_ptr<SceneImage> to_display, const std::string & window_name) { update(to_display->getCVImage(), window_name); } - void ODViewer::setBackGround(const cv::Scalar & color) + void Viewer::setBackGround(const cv::Scalar & color) { switch(status_){ case POINTCLOUD : @@ -135,7 +135,7 @@ namespace od { } - bool ODViewer::toStop() + bool Viewer::toStop() { switch(status_){ @@ -152,12 +152,12 @@ namespace od { } - void ODViewer::setBackGround(unsigned int r, unsigned int g, unsigned int b) + void Viewer::setBackGround(unsigned int r, unsigned int g, unsigned int b) { setBackGround(cv::Scalar(b, g, r)); } - void ODViewer::spin() + void Viewer::spin() { switch(status_){ case POINTCLOUD : @@ -174,12 +174,12 @@ namespace od { } } - unsigned int ODViewer::wait(unsigned int time) const + unsigned int Viewer::wait(unsigned int time) const { return cv::waitKey(time); } - void ODViewer::addText(const std::string & text, pcl::PointXYZ pos, double textScale, cv::Scalar color) + void Viewer::addText(const std::string & text, pcl::PointXYZ pos, double textScale, cv::Scalar color) { if(status_ == CVMAT) { @@ -192,7 +192,7 @@ namespace od { } } - void ODViewer::addText(const std::string & text, cv::Point3f pos, double textScale, cv::Scalar color) + void Viewer::addText(const std::string & text, cv::Point3f pos, double textScale, cv::Scalar color) { if(status_ == CVMAT) { @@ -205,7 +205,7 @@ namespace od { } } - void ODViewer::remove(const std::string & text) + void Viewer::remove(const std::string & text) { if(status_ == CVMAT) { @@ -228,7 +228,7 @@ namespace od { } } - void ODViewer::removeAll() + void Viewer::removeAll() { if(status_ == CVMAT) { @@ -241,7 +241,7 @@ namespace od { } } - void ODViewer::removeAllShapes() + void Viewer::removeAllShapes() { if(status_ == CVMAT) { @@ -255,7 +255,7 @@ namespace od { } - void ODViewer::removeText(const std::string & text) + void Viewer::removeText(const std::string & text) { if(status_ == CVMAT) { @@ -268,7 +268,7 @@ namespace od { } - void ODViewer::removeShape(const std::string & text) + void Viewer::removeShape(const std::string & text) { if(status_ == CVMAT) { @@ -281,7 +281,7 @@ namespace od { } - shared_ptr<pcl::visualization::PCLVisualizer> ODViewer::getViewer() + shared_ptr<pcl::visualization::PCLVisualizer> Viewer::getViewer() { return viewer_; } @@ -289,35 +289,35 @@ namespace od { // Explicit template function instantiation - template void ODViewer::render<pcl::PointXYZ>(shared_ptr<pcl::PointCloud<pcl::PointXYZ> >, const std::string &, bool); - template void ODViewer::render<pcl::PointXYZRGB>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> >, const std::string &, bool); - template void ODViewer::render<pcl::PointXYZRGBA>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA> >, const std::string &, bool); + template void Viewer::render<pcl::PointXYZ>(shared_ptr<pcl::PointCloud<pcl::PointXYZ> >, const std::string &, bool); + template void Viewer::render<pcl::PointXYZRGB>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> >, const std::string &, bool); + template void Viewer::render<pcl::PointXYZRGBA>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA> >, const std::string &, bool); - template void ODViewer::render<pcl::PointXYZ>(shared_ptr<pcl::PointCloud<pcl::PointXYZ> > to_display, + template void Viewer::render<pcl::PointXYZ>(shared_ptr<pcl::PointCloud<pcl::PointXYZ> > to_display, pcl::visualization::PointCloudColorHandlerRandom<pcl::PointXYZ> & random_handler, const std::string & cloud_name); - template void ODViewer::render<pcl::PointXYZRGB>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> > to_display, + template void Viewer::render<pcl::PointXYZRGB>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> > to_display, pcl::visualization::PointCloudColorHandlerRandom<pcl::PointXYZRGB> & random_handler, const std::string & cloud_name); - template void ODViewer::render<pcl::PointXYZRGBA>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA> > to_display, + template void Viewer::render<pcl::PointXYZRGBA>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA> > to_display, pcl::visualization::PointCloudColorHandlerRandom<pcl::PointXYZRGBA> & random_handler, const std::string & cloud_name); - template void ODViewer::render<pcl::PointXYZ>(shared_ptr<ODScenePointCloud<pcl::PointXYZ> >, const std::string &, bool); - template void ODViewer::render<pcl::PointXYZRGB>(shared_ptr<ODScenePointCloud<pcl::PointXYZRGB> >, const std::string &, bool); - template void ODViewer::render<pcl::PointXYZRGBA>(shared_ptr<ODScenePointCloud<pcl::PointXYZRGBA> >, const std::string &, bool); + template void Viewer::render<pcl::PointXYZ>(shared_ptr<ScenePointCloud<pcl::PointXYZ> >, const std::string &, bool); + template void Viewer::render<pcl::PointXYZRGB>(shared_ptr<ScenePointCloud<pcl::PointXYZRGB> >, const std::string &, bool); + template void Viewer::render<pcl::PointXYZRGBA>(shared_ptr<ScenePointCloud<pcl::PointXYZRGBA> >, const std::string &, bool); - template void ODViewer::render<pcl::PointXYZ>(const ODScenePointCloud<pcl::PointXYZ> &, const std::string &, bool); - template void ODViewer::render<pcl::PointXYZRGB>(const ODScenePointCloud<pcl::PointXYZRGB> &, const std::string &, bool); - template void ODViewer::render<pcl::PointXYZRGBA>(const ODScenePointCloud<pcl::PointXYZRGBA> &, const std::string &, bool); + template void Viewer::render<pcl::PointXYZ>(const ScenePointCloud<pcl::PointXYZ> &, const std::string &, bool); + template void Viewer::render<pcl::PointXYZRGB>(const ScenePointCloud<pcl::PointXYZRGB> &, const std::string &, bool); + template void Viewer::render<pcl::PointXYZRGBA>(const ScenePointCloud<pcl::PointXYZRGBA> &, const std::string &, bool); - template void ODViewer::update<pcl::PointXYZ>(shared_ptr<pcl::PointCloud<pcl::PointXYZ> >, const std::string &, bool); - template void ODViewer::update<pcl::PointXYZRGB>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> >, const std::string &, bool); - template void ODViewer::update<pcl::PointXYZRGBA>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA> >, const std::string &, bool); + template void Viewer::update<pcl::PointXYZ>(shared_ptr<pcl::PointCloud<pcl::PointXYZ> >, const std::string &, bool); + template void Viewer::update<pcl::PointXYZRGB>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> >, const std::string &, bool); + template void Viewer::update<pcl::PointXYZRGBA>(shared_ptr<pcl::PointCloud<pcl::PointXYZRGBA> >, const std::string &, bool); - template void ODViewer::update<pcl::PointXYZ>(shared_ptr<ODScenePointCloud<pcl::PointXYZ> >, const std::string &, bool); - template void ODViewer::update<pcl::PointXYZRGB>(shared_ptr<ODScenePointCloud<pcl::PointXYZRGB> >, const std::string &, bool); - template void ODViewer::update<pcl::PointXYZRGBA>(shared_ptr<ODScenePointCloud<pcl::PointXYZRGBA> >, const std::string &, bool); + template void Viewer::update<pcl::PointXYZ>(shared_ptr<ScenePointCloud<pcl::PointXYZ> >, const std::string &, bool); + template void Viewer::update<pcl::PointXYZRGB>(shared_ptr<ScenePointCloud<pcl::PointXYZRGB> >, const std::string &, bool); + template void Viewer::update<pcl::PointXYZRGBA>(shared_ptr<ScenePointCloud<pcl::PointXYZRGBA> >, const std::string &, bool); - template void ODViewer::update<pcl::PointXYZ>(const ODScenePointCloud<pcl::PointXYZ> &, const std::string &, bool); - template void ODViewer::update<pcl::PointXYZRGB>(const ODScenePointCloud<pcl::PointXYZRGB> &, const std::string &, bool); - template void ODViewer::update<pcl::PointXYZRGBA>(const ODScenePointCloud<pcl::PointXYZRGBA> &, const std::string &, bool); + template void Viewer::update<pcl::PointXYZ>(const ScenePointCloud<pcl::PointXYZ> &, const std::string &, bool); + template void Viewer::update<pcl::PointXYZRGB>(const ScenePointCloud<pcl::PointXYZRGB> &, const std::string &, bool); + template void Viewer::update<pcl::PointXYZRGBA>(const ScenePointCloud<pcl::PointXYZRGBA> &, const std::string &, bool); } \ No newline at end of file diff --git a/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp b/detectors/impl/od/detectors/global3D/detection/CADDetector3DGlobal.hpp similarity index 69% rename from detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp rename to detectors/impl/od/detectors/global3D/detection/CADDetector3DGlobal.hpp index 27d2bf95..4582357c 100644 --- a/detectors/impl/od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp +++ b/detectors/impl/od/detectors/global3D/detection/CADDetector3DGlobal.hpp @@ -3,7 +3,7 @@ // #pragma once -#include "od/common/pipeline/ODDetector.h" +#include "od/common/pipeline/Detector.h" #include <pcl/apps/3d_rec_framework/pipeline/global_nn_classifier.h> #include <pcl/apps/3d_rec_framework/pc_source/mesh_source.h> #include <pcl/apps/3d_rec_framework/feature_wrapper/global/vfh_estimator.h> @@ -18,13 +18,13 @@ namespace od { template<typename PointT> - class ODCADDetector3DGlobal : public ODDetector3D<PointT> + class CADDetector3DGlobal : public Detector3D<PointT> { public: - ODCADDetector3DGlobal(const std::string & training_data_location = std::string(""), + CADDetector3DGlobal(const std::string & training_data_location = std::string(""), const std::string & training_input_location = std::string("")) : - ODDetector3D<PointT>(training_data_location), NN_(2), desc_name_("esf") + Detector3D<PointT>(training_data_location), NN_(2), desc_name_("esf") { this->trained_location_identifier_ = std::string("GLOBAL3DVFH"); this->training_input_location_ = training_input_location; @@ -52,11 +52,11 @@ namespace od desc_name_ = desc_name; } - shared_ptr<ODDetections> detect(shared_ptr<ODScene> scene); - shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene); + shared_ptr<Detections> detect(shared_ptr<Scene> scene); + shared_ptr<Detections> detectOmni(shared_ptr<Scene> scene); - shared_ptr<ODDetections> detect(shared_ptr<ODScenePointCloud<PointT> > scene); - shared_ptr<ODDetections3D> detectOmni(shared_ptr<ODScenePointCloud<PointT> > scene); + shared_ptr<Detections> detect(shared_ptr<ScenePointCloud<PointT> > scene); + shared_ptr<Detections3D> detectOmni(shared_ptr<ScenePointCloud<PointT> > scene); protected: @@ -67,21 +67,21 @@ namespace od }; template<typename PointT> - shared_ptr<ODDetections> ODCADDetector3DGlobal<PointT>::detect(shared_ptr<ODScene> scene) + shared_ptr<Detections> CADDetector3DGlobal<PointT>::detect(shared_ptr<Scene> scene) { - std::cout << "not implemented, use with shared_ptr<ODScenePointCloud<PointT>>" << std::endl; + std::cout << "not implemented, use with shared_ptr<ScenePointCloud<PointT>>" << std::endl; return nullptr; }; template<typename PointT> - shared_ptr<ODDetections> ODCADDetector3DGlobal<PointT>::detectOmni(shared_ptr<ODScene> scene) + shared_ptr<Detections> CADDetector3DGlobal<PointT>::detectOmni(shared_ptr<Scene> scene) { - std::cout << "not implemented, use with shared_ptr<ODScenePointCloud<PointT>>" << std::endl; + std::cout << "not implemented, use with shared_ptr<ScenePointCloud<PointT>>" << std::endl; return nullptr; }; template<typename PointT> - void ODCADDetector3DGlobal<PointT>::init() + void CADDetector3DGlobal<PointT>::init() { shared_ptr<pcl::rec_3d_framework::MeshSource<pcl::PointXYZ> > mesh_source(new pcl::rec_3d_framework::MeshSource<pcl::PointXYZ>); @@ -165,9 +165,9 @@ namespace od } template<typename PointT> - shared_ptr<ODDetections3D> ODCADDetector3DGlobal<PointT>::detectOmni(shared_ptr<ODScenePointCloud<PointT> > scene) + shared_ptr<Detections3D> CADDetector3DGlobal<PointT>::detectOmni(shared_ptr<ScenePointCloud<PointT> > scene) { - shared_ptr<ODDetections3D> detections = make_shared<ODDetections3D>(); + shared_ptr<Detections3D> detections = make_shared<Detections3D>(); typename pcl::PointCloud<PointT>::Ptr frame = scene->getPointCloud(); pcl::PointCloud<pcl::PointXYZ>::Ptr xyz_points(new pcl::PointCloud<pcl::PointXYZ>); @@ -209,8 +209,8 @@ namespace od //position at 3D identified! //now fill up the detection: - shared_ptr<ODDetection3D> detection = make_shared<ODDetection3D>(); - detection->setType(ODDetection::OD_CLASSIFICATION); + shared_ptr<Detection3D> detection = make_shared<Detection3D>(); + detection->setType(Detection::CLASSIFICATION); detection->setId(categories[0]); detection->setLocation(centroid); detection->setMetainfoCluster(clusters[i]); @@ -221,9 +221,9 @@ namespace od } template<typename PointT> - shared_ptr<ODDetections> ODCADDetector3DGlobal<PointT>::detect(shared_ptr<ODScenePointCloud<PointT> > scene) + shared_ptr<Detections> CADDetector3DGlobal<PointT>::detect(shared_ptr<ScenePointCloud<PointT> > scene) { - shared_ptr<ODDetections> detections = make_shared<ODDetections>(); + shared_ptr<Detections> detections = make_shared<Detections>(); typename pcl::PointCloud<PointT>::Ptr frame = scene->getPointCloud(); pcl::PointCloud<pcl::PointXYZ>::Ptr xyz_points(new pcl::PointCloud<pcl::PointXYZ>); @@ -240,7 +240,7 @@ namespace od //detection done! //now fill up the detection: - shared_ptr<ODDetection> detection(new ODDetection(ODDetection::OD_CLASSIFICATION, categories[0], conf[0])); + shared_ptr<Detection> detection(new Detection(Detection::CLASSIFICATION, categories[0], conf[0])); detections->push_back(detection); @@ -250,71 +250,71 @@ namespace od #ifndef DOXYGEN_SHOULD_SKIP_THIS extern template - shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZ>::detect(shared_ptr<ODScene> scene); + shared_ptr<Detections> CADDetector3DGlobal<pcl::PointXYZ>::detect(shared_ptr<Scene> scene); extern template - shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZ>::detectOmni(shared_ptr<ODScene> scene); + shared_ptr<Detections> CADDetector3DGlobal<pcl::PointXYZ>::detectOmni(shared_ptr<Scene> scene); extern template - void ODCADDetector3DGlobal<pcl::PointXYZ>::init(); + void CADDetector3DGlobal<pcl::PointXYZ>::init(); extern template - shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZ>::detect(shared_ptr<ODScene> scene); + shared_ptr<Detections> CADDetector3DGlobal<pcl::PointXYZ>::detect(shared_ptr<Scene> scene); extern template - shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZ>::detectOmni(shared_ptr<ODScene> scene); + shared_ptr<Detections> CADDetector3DGlobal<pcl::PointXYZ>::detectOmni(shared_ptr<Scene> scene); extern template - shared_ptr<ODDetections3D> ODCADDetector3DGlobal<pcl::PointXYZ>::detectOmni(shared_ptr<ODScenePointCloud<pcl::PointXYZ> > scene); + shared_ptr<Detections3D> CADDetector3DGlobal<pcl::PointXYZ>::detectOmni(shared_ptr<ScenePointCloud<pcl::PointXYZ> > scene); extern template - shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZ>::detect(shared_ptr<ODScenePointCloud<pcl::PointXYZ> > scene); + shared_ptr<Detections> CADDetector3DGlobal<pcl::PointXYZ>::detect(shared_ptr<ScenePointCloud<pcl::PointXYZ> > scene); extern template - shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZRGB>::detect(shared_ptr<ODScene> scene); + shared_ptr<Detections> CADDetector3DGlobal<pcl::PointXYZRGB>::detect(shared_ptr<Scene> scene); extern template - shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZRGB>::detectOmni(shared_ptr<ODScene> scene); + shared_ptr<Detections> CADDetector3DGlobal<pcl::PointXYZRGB>::detectOmni(shared_ptr<Scene> scene); extern template - void ODCADDetector3DGlobal<pcl::PointXYZRGB>::init(); + void CADDetector3DGlobal<pcl::PointXYZRGB>::init(); extern template - shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZRGB>::detect(shared_ptr<ODScene> scene); + shared_ptr<Detections> CADDetector3DGlobal<pcl::PointXYZRGB>::detect(shared_ptr<Scene> scene); extern template - shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZRGB>::detectOmni(shared_ptr<ODScene> scene); + shared_ptr<Detections> CADDetector3DGlobal<pcl::PointXYZRGB>::detectOmni(shared_ptr<Scene> scene); extern template - shared_ptr<ODDetections3D> ODCADDetector3DGlobal<pcl::PointXYZRGB>::detectOmni(shared_ptr<ODScenePointCloud<pcl::PointXYZRGB> > scene); + shared_ptr<Detections3D> CADDetector3DGlobal<pcl::PointXYZRGB>::detectOmni(shared_ptr<ScenePointCloud<pcl::PointXYZRGB> > scene); extern template - shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZRGB>::detect(shared_ptr<ODScenePointCloud<pcl::PointXYZRGB> > scene); + shared_ptr<Detections> CADDetector3DGlobal<pcl::PointXYZRGB>::detect(shared_ptr<ScenePointCloud<pcl::PointXYZRGB> > scene); extern template - shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZRGBA>::detect(shared_ptr<ODScene> scene); + shared_ptr<Detections> CADDetector3DGlobal<pcl::PointXYZRGBA>::detect(shared_ptr<Scene> scene); extern template - shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZRGBA>::detectOmni(shared_ptr<ODScene> scene); + shared_ptr<Detections> CADDetector3DGlobal<pcl::PointXYZRGBA>::detectOmni(shared_ptr<Scene> scene); extern template - void ODCADDetector3DGlobal<pcl::PointXYZRGBA>::init(); + void CADDetector3DGlobal<pcl::PointXYZRGBA>::init(); extern template - shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZRGBA>::detect(shared_ptr<ODScene> scene); + shared_ptr<Detections> CADDetector3DGlobal<pcl::PointXYZRGBA>::detect(shared_ptr<Scene> scene); extern template - shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZRGBA>::detectOmni(shared_ptr<ODScene> scene); + shared_ptr<Detections> CADDetector3DGlobal<pcl::PointXYZRGBA>::detectOmni(shared_ptr<Scene> scene); extern template - shared_ptr<ODDetections3D> ODCADDetector3DGlobal<pcl::PointXYZRGBA>::detectOmni(shared_ptr<ODScenePointCloud<pcl::PointXYZRGBA> > scene); + shared_ptr<Detections3D> CADDetector3DGlobal<pcl::PointXYZRGBA>::detectOmni(shared_ptr<ScenePointCloud<pcl::PointXYZRGBA> > scene); extern template - shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZRGBA>::detect(shared_ptr<ODScenePointCloud<pcl::PointXYZRGBA> > scene); + shared_ptr<Detections> CADDetector3DGlobal<pcl::PointXYZRGBA>::detect(shared_ptr<ScenePointCloud<pcl::PointXYZRGBA> > scene); #endif } diff --git a/detectors/impl/od/detectors/misc/detection/DetectorMultiAlgo.hpp b/detectors/impl/od/detectors/misc/detection/DetectorMultiAlgo.hpp new file mode 100644 index 00000000..45806813 --- /dev/null +++ b/detectors/impl/od/detectors/misc/detection/DetectorMultiAlgo.hpp @@ -0,0 +1,321 @@ +/* +Copyright (c) 2015, Kripasindhu Sarkar +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions 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. + * Neither the name of the copyright holder(s) nor the + names of its contributors may 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 COPYRIGHT HOLDER BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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. +*/// +// Created by sarkar on 06.08.15. +// +#pragma once +#include "od/detectors/global2D/detection/CascadeDetector.h" +#include "od/detectors/global2D/detection/HOGDetector.h" +#include "od/detectors/global3D/detection/CADDetector3DGlobal.hpp" + +namespace od +{ + + template<typename PointT> + class DetectorMultiAlgo2D : public Detector2D + { + public: + DetectorMultiAlgo2D(const std::string & training_data_location_) : Detector2D(training_data_location_){} + + shared_ptr<Detections> detect(shared_ptr<SceneImage> scene); + shared_ptr<Detections2D> detectOmni(shared_ptr<SceneImage> scene); + + shared_ptr<Detections> detect(shared_ptr<ScenePointCloud<PointT> > scene); + shared_ptr<Detections3D> detectOmni(shared_ptr<ScenePointCloud<PointT> > scene); + + shared_ptr<Detections> detectOmni(shared_ptr<Scene> scene); + shared_ptr<Detections> detect(shared_ptr<Scene> scene); + + void init(); + + private: + + std::vector<shared_ptr<Detector2D> > detectors_2d_; + std::vector<shared_ptr<Detector3D<PointT> > > detectors_3d_; + + }; + + template<typename PointT> + shared_ptr<Detections> DetectorMultiAlgo2D<PointT>::detectOmni(shared_ptr<Scene> scene) + { + std::cout << "not implemented, use with shared_ptr<SceneImage> or shared_ptr<ScenePointCloud<PointT>>" << std::endl; + return nullptr; + } + + template<typename PointT> + shared_ptr<Detections> DetectorMultiAlgo2D<PointT>::detect(shared_ptr<Scene> scene) + { + std::cout << "not implemented, use with shared_ptr<SceneImage> or shared_ptr<ScenePointCloud<PointT>>" << std::endl; + return nullptr; + } + + //BASED ON 2D SCENE + template<typename PointT> + shared_ptr<Detections> DetectorMultiAlgo2D<PointT>::detect(shared_ptr<SceneImage> scene) + { + shared_ptr<Detections> detections_all = make_shared<Detections>(); + for(auto & d : detectors_2d_) + { + detections_all->append(d->detect(scene)); + } + + return detections_all; + } + + template<typename PointT> + shared_ptr<Detections2D> DetectorMultiAlgo2D<PointT>::detectOmni(shared_ptr<SceneImage > scene) + { + shared_ptr<Detections2D> detections_all = make_shared<Detections2D>(); + for(auto & d : detectors_2d_) + { + detections_all->append(d->detectOmni(scene)); + } + + return detections_all; + } + + template<typename PointT> + void DetectorMultiAlgo2D<PointT>::init() + { + //make a list of different algorithms + //vector<Detector *> detectors = {new CascadeDetector(trained_data_location_), new HOGDetector(trained_data_location_), new CADRecognizer2DLocal(trained_data_location_)}; + +#ifndef WITH_BOOST_SHARED_PTR + #if WITH_GPU + detectors_2d_.push_back(shared_ptr<g2d::CascadeDetector>(new g2d::CascadeDetector(true, trained_data_location_))); + #else + detectors_2d_.push_back(shared_ptr<g2d::CascadeDetector>(new g2d::CascadeDetector(false, trained_data_location_))); + #endif + detectors_2d_.push_back(shared_ptr<g2d::HOGDetector>(new g2d::HOGDetector(trained_data_location_))); +#else + #if WITH_GPU + detectors_2d_.push_back(make_shared<g2d::CascadeDetector>(true, trained_data_location_)); + #else + detectors_2d_.push_back(make_shared<g2d::CascadeDetector>(false, trained_data_location_)); + #endif + detectors_2d_.push_back(make_shared<g2d::HOGDetector>(trained_data_location_)); +#endif + + // detectors.push_back(new CADRecognizer2DLocal(trained_data_location_)); + + for(auto & d : detectors_2d_) + { + d->init(); + } + } + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + + extern template + shared_ptr<Detections> DetectorMultiAlgo2D<pcl::PointXYZ>::detectOmni(shared_ptr<Scene> scene); + + extern template + shared_ptr<Detections> DetectorMultiAlgo2D<pcl::PointXYZ>::detect(shared_ptr<Scene> scene); + + extern template + shared_ptr<Detections> DetectorMultiAlgo2D<pcl::PointXYZ>::detect(shared_ptr<SceneImage> scene); + + extern template + shared_ptr<Detections2D> DetectorMultiAlgo2D<pcl::PointXYZ>::detectOmni(shared_ptr<SceneImage > scene); + + extern template + void DetectorMultiAlgo2D<pcl::PointXYZ>::init(); + + + + extern template + shared_ptr<Detections> DetectorMultiAlgo2D<pcl::PointXYZRGB>::detectOmni(shared_ptr<Scene> scene); + + extern template + shared_ptr<Detections> DetectorMultiAlgo2D<pcl::PointXYZRGB>::detect(shared_ptr<Scene> scene); + + extern template + shared_ptr<Detections> DetectorMultiAlgo2D<pcl::PointXYZRGB>::detect(shared_ptr<SceneImage> scene); + + extern template + shared_ptr<Detections2D> DetectorMultiAlgo2D<pcl::PointXYZRGB>::detectOmni(shared_ptr<SceneImage > scene); + + extern template + void DetectorMultiAlgo2D<pcl::PointXYZRGB>::init(); + + + + + extern template + shared_ptr<Detections> DetectorMultiAlgo2D<pcl::PointXYZRGBA>::detectOmni(shared_ptr<Scene> scene); + + extern template + shared_ptr<Detections> DetectorMultiAlgo2D<pcl::PointXYZRGBA>::detect(shared_ptr<Scene> scene); + + extern template + shared_ptr<Detections> DetectorMultiAlgo2D<pcl::PointXYZRGBA>::detect(shared_ptr<SceneImage> scene); + + extern template + shared_ptr<Detections2D> DetectorMultiAlgo2D<pcl::PointXYZRGBA>::detectOmni(shared_ptr<SceneImage > scene); + + extern template + void DetectorMultiAlgo2D<pcl::PointXYZRGBA>::init(); + +#endif + + template<typename PointT> + class DetectorMultiAlgo : public Detector + { + + public: + + DetectorMultiAlgo(const std::string & training_data_location_) : Detector(training_data_location_){} + + shared_ptr<Detections> detect(shared_ptr<SceneImage> scene); + shared_ptr<Detections2D> detectOmni(shared_ptr<SceneImage> scene); + + shared_ptr<Detections> detect(shared_ptr<ScenePointCloud<PointT> > scene); + shared_ptr<Detections3D> detectOmni(shared_ptr<ScenePointCloud<PointT> > scene); + + shared_ptr<Detections> detectOmni(shared_ptr<Scene> scene); + shared_ptr<Detections> detect(shared_ptr<Scene> scene); + + void init(); + + private: + + std::vector<shared_ptr<Detector2D> > detectors_2d_; + std::vector<shared_ptr<Detector3D<PointT> > > detectors_3d_; + + }; + + template<typename PointT> + shared_ptr<Detections> DetectorMultiAlgo<PointT>::detectOmni(shared_ptr<Scene> scene) + { + std::cout << "not implemented, use with shared_ptr<SceneImage> or shared_ptr<ScenePointCloud<PointT>>" << std::endl; + return nullptr; + } + + template<typename PointT> + shared_ptr<Detections> DetectorMultiAlgo<PointT>::detect(shared_ptr<Scene> scene) + { + std::cout << "not implemented, use with shared_ptr<SceneImage> or shared_ptr<ScenePointCloud<PointT>>" << std::endl; + return nullptr; + } + + + /////############BASED ON 3D SCENE##################### + template<typename PointT> + void DetectorMultiAlgo<PointT>::init() + { + //3D +#ifndef WITH_BOOST_SHARED_PTR + detectors_3d_.push_back(shared_ptr<g3d::CADDetector3DGlobal<PointT> >(new g3d::CADDetector3DGlobal<PointT>(trained_data_location_, training_input_location_))); +#else + detectors_3d_.push_back(make_shared<g3d::CADDetector3DGlobal<PointT> >(trained_data_location_, training_input_location_)); +#endif + for(auto & d : detectors_3d_) + { + d->init(); + } + } + + template<typename PointT> + shared_ptr<Detections> DetectorMultiAlgo<PointT>::detect(shared_ptr<ScenePointCloud<PointT> > scene) + { + shared_ptr<Detections> detections_all = make_shared<Detections>(); + for(auto & d : detectors_3d_) + { + detections_all->append(d->detect(scene)); + } + + return detections_all; + } + + template<typename PointT> + shared_ptr<Detections3D> DetectorMultiAlgo<PointT>::detectOmni(shared_ptr<ScenePointCloud<PointT> > scene) + { + shared_ptr<Detections3D> detections_all = make_shared<Detections3D>(); + for(auto & d : detectors_3d_) + { + detections_all->append(d->detectOmni(scene)); + } + + return detections_all; + } + + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + + + extern template + shared_ptr<Detections> DetectorMultiAlgo<pcl::PointXYZ>::detectOmni(shared_ptr<Scene> scene); + + extern template + shared_ptr<Detections> DetectorMultiAlgo<pcl::PointXYZ>::detect(shared_ptr<Scene> scene); + + extern template + void DetectorMultiAlgo<pcl::PointXYZ>::init(); + + extern template + shared_ptr<Detections> DetectorMultiAlgo<pcl::PointXYZ>::detect(shared_ptr<ScenePointCloud<pcl::PointXYZ> > scene); + + extern template + shared_ptr<Detections3D> DetectorMultiAlgo<pcl::PointXYZ>::detectOmni(shared_ptr<ScenePointCloud<pcl::PointXYZ> > scene); + + + + extern template + shared_ptr<Detections> DetectorMultiAlgo<pcl::PointXYZRGB>::detectOmni(shared_ptr<Scene> scene); + + extern template + shared_ptr<Detections> DetectorMultiAlgo<pcl::PointXYZRGB>::detect(shared_ptr<Scene> scene); + + extern template + void DetectorMultiAlgo<pcl::PointXYZRGB>::init(); + + extern template + shared_ptr<Detections> DetectorMultiAlgo<pcl::PointXYZRGB>::detect(shared_ptr<ScenePointCloud<pcl::PointXYZRGB> > scene); + + extern template + shared_ptr<Detections3D> DetectorMultiAlgo<pcl::PointXYZRGB>::detectOmni(shared_ptr<ScenePointCloud<pcl::PointXYZRGB> > scene); + + + + extern template + shared_ptr<Detections> DetectorMultiAlgo<pcl::PointXYZRGBA>::detectOmni(shared_ptr<Scene> scene); + + extern template + shared_ptr<Detections> DetectorMultiAlgo<pcl::PointXYZRGBA>::detect(shared_ptr<Scene> scene); + + extern template + void DetectorMultiAlgo<pcl::PointXYZRGBA>::init(); + + extern template + shared_ptr<Detections> DetectorMultiAlgo<pcl::PointXYZRGBA>::detect(shared_ptr<ScenePointCloud<pcl::PointXYZRGBA> > scene); + + extern template + shared_ptr<Detections3D> DetectorMultiAlgo<pcl::PointXYZRGBA>::detectOmni(shared_ptr<ScenePointCloud<pcl::PointXYZRGBA> > scene); + + +#endif + +} \ No newline at end of file diff --git a/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp b/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp deleted file mode 100644 index 9d33a8f2..00000000 --- a/detectors/impl/od/detectors/misc/detection/ODDetectorMultiAlgo.hpp +++ /dev/null @@ -1,321 +0,0 @@ -/* -Copyright (c) 2015, Kripasindhu Sarkar -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions 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. - * Neither the name of the copyright holder(s) nor the - names of its contributors may 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 COPYRIGHT HOLDER 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. -*/// -// Created by sarkar on 06.08.15. -// -#pragma once -#include "od/detectors/global2D/detection/ODCascadeDetector.h" -#include "od/detectors/global2D/detection/ODHOGDetector.h" -#include "od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp" - -namespace od -{ - - template<typename PointT> - class ODDetectorMultiAlgo2D : public ODDetector2D - { - public: - ODDetectorMultiAlgo2D(const std::string & training_data_location_) : ODDetector2D(training_data_location_){} - - shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene); - shared_ptr<ODDetections2D> detectOmni(shared_ptr<ODSceneImage> scene); - - shared_ptr<ODDetections> detect(shared_ptr<ODScenePointCloud<PointT> > scene); - shared_ptr<ODDetections3D> detectOmni(shared_ptr<ODScenePointCloud<PointT> > scene); - - shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene); - shared_ptr<ODDetections> detect(shared_ptr<ODScene> scene); - - void init(); - - private: - - std::vector<shared_ptr<ODDetector2D> > detectors_2d_; - std::vector<shared_ptr<ODDetector3D<PointT> > > detectors_3d_; - - }; - - template<typename PointT> - shared_ptr<ODDetections> ODDetectorMultiAlgo2D<PointT>::detectOmni(shared_ptr<ODScene> scene) - { - std::cout << "not implemented, use with shared_ptr<ODSceneImage> or shared_ptr<ODScenePointCloud<PointT>>" << std::endl; - return nullptr; - } - - template<typename PointT> - shared_ptr<ODDetections> ODDetectorMultiAlgo2D<PointT>::detect(shared_ptr<ODScene> scene) - { - std::cout << "not implemented, use with shared_ptr<ODSceneImage> or shared_ptr<ODScenePointCloud<PointT>>" << std::endl; - return nullptr; - } - - //BASED ON 2D SCENE - template<typename PointT> - shared_ptr<ODDetections> ODDetectorMultiAlgo2D<PointT>::detect(shared_ptr<ODSceneImage> scene) - { - shared_ptr<ODDetections> detections_all = make_shared<ODDetections>(); - for(auto & d : detectors_2d_) - { - detections_all->append(d->detect(scene)); - } - - return detections_all; - } - - template<typename PointT> - shared_ptr<ODDetections2D> ODDetectorMultiAlgo2D<PointT>::detectOmni(shared_ptr<ODSceneImage > scene) - { - shared_ptr<ODDetections2D> detections_all = make_shared<ODDetections2D>(); - for(auto & d : detectors_2d_) - { - detections_all->append(d->detectOmni(scene)); - } - - return detections_all; - } - - template<typename PointT> - void ODDetectorMultiAlgo2D<PointT>::init() - { - //make a list of different algorithms - //vector<ODDetector *> detectors = {new ODCascadeDetector(trained_data_location_), new ODHOGDetector(trained_data_location_), new ODCADRecognizer2DLocal(trained_data_location_)}; - -#ifdef WITH_BOOST_SHARED_PTR - #if WITH_GPU - detectors_2d_.push_back(shared_ptr<g2d::ODCascadeDetector>(new g2d::ODCascadeDetector(true, trained_data_location_))); - #else - detectors_2d_.push_back(shared_ptr<g2d::ODCascadeDetector>(new g2d::ODCascadeDetector(false, trained_data_location_))); - #endif - detectors_2d_.push_back(shared_ptr<g2d::ODHOGDetector>(new g2d::ODHOGDetector(trained_data_location_))); -#else - #if WITH_GPU - detectors_2d_.push_back(make_shared<g2d::ODCascadeDetector>(true, trained_data_location_)); - #else - detectors_2d_.push_back(make_shared<g2d::ODCascadeDetector>(false, trained_data_location_)); - #endif - detectors_2d_.push_back(make_shared<g2d::ODHOGDetector>(trained_data_location_)); -#endif - - // detectors.push_back(new ODCADRecognizer2DLocal(trained_data_location_)); - - for(auto & d : detectors_2d_) - { - d->init(); - } - } - -#ifndef DOXYGEN_SHOULD_SKIP_THIS - - extern template - shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZ>::detectOmni(shared_ptr<ODScene> scene); - - extern template - shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZ>::detect(shared_ptr<ODScene> scene); - - extern template - shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZ>::detect(shared_ptr<ODSceneImage> scene); - - extern template - shared_ptr<ODDetections2D> ODDetectorMultiAlgo2D<pcl::PointXYZ>::detectOmni(shared_ptr<ODSceneImage > scene); - - extern template - void ODDetectorMultiAlgo2D<pcl::PointXYZ>::init(); - - - - extern template - shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZRGB>::detectOmni(shared_ptr<ODScene> scene); - - extern template - shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZRGB>::detect(shared_ptr<ODScene> scene); - - extern template - shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZRGB>::detect(shared_ptr<ODSceneImage> scene); - - extern template - shared_ptr<ODDetections2D> ODDetectorMultiAlgo2D<pcl::PointXYZRGB>::detectOmni(shared_ptr<ODSceneImage > scene); - - extern template - void ODDetectorMultiAlgo2D<pcl::PointXYZRGB>::init(); - - - - - extern template - shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZRGBA>::detectOmni(shared_ptr<ODScene> scene); - - extern template - shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZRGBA>::detect(shared_ptr<ODScene> scene); - - extern template - shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZRGBA>::detect(shared_ptr<ODSceneImage> scene); - - extern template - shared_ptr<ODDetections2D> ODDetectorMultiAlgo2D<pcl::PointXYZRGBA>::detectOmni(shared_ptr<ODSceneImage > scene); - - extern template - void ODDetectorMultiAlgo2D<pcl::PointXYZRGBA>::init(); - -#endif - - template<typename PointT> - class ODDetectorMultiAlgo : public ODDetector - { - - public: - - ODDetectorMultiAlgo(const std::string & training_data_location_) : ODDetector(training_data_location_){} - - shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene); - shared_ptr<ODDetections2D> detectOmni(shared_ptr<ODSceneImage> scene); - - shared_ptr<ODDetections> detect(shared_ptr<ODScenePointCloud<PointT> > scene); - shared_ptr<ODDetections3D> detectOmni(shared_ptr<ODScenePointCloud<PointT> > scene); - - shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene); - shared_ptr<ODDetections> detect(shared_ptr<ODScene> scene); - - void init(); - - private: - - std::vector<shared_ptr<ODDetector2D> > detectors_2d_; - std::vector<shared_ptr<ODDetector3D<PointT> > > detectors_3d_; - - }; - - template<typename PointT> - shared_ptr<ODDetections> ODDetectorMultiAlgo<PointT>::detectOmni(shared_ptr<ODScene> scene) - { - std::cout << "not implemented, use with shared_ptr<ODSceneImage> or shared_ptr<ODScenePointCloud<PointT>>" << std::endl; - return nullptr; - } - - template<typename PointT> - shared_ptr<ODDetections> ODDetectorMultiAlgo<PointT>::detect(shared_ptr<ODScene> scene) - { - std::cout << "not implemented, use with shared_ptr<ODSceneImage> or shared_ptr<ODScenePointCloud<PointT>>" << std::endl; - return nullptr; - } - - - /////############BASED ON 3D SCENE##################### - template<typename PointT> - void ODDetectorMultiAlgo<PointT>::init() - { - //3D -#ifdef WITH_BOOST_SHARED_PTR - detectors_3d_.push_back(shared_ptr<g3d::ODCADDetector3DGlobal<PointT> >(new g3d::ODCADDetector3DGlobal<PointT>(trained_data_location_, training_input_location_))); -#else - detectors_3d_.push_back(make_shared<g3d::ODCADDetector3DGlobal<PointT> >(trained_data_location_, training_input_location_)); -#endif - for(auto & d : detectors_3d_) - { - d->init(); - } - } - - template<typename PointT> - shared_ptr<ODDetections> ODDetectorMultiAlgo<PointT>::detect(shared_ptr<ODScenePointCloud<PointT> > scene) - { - shared_ptr<ODDetections> detections_all = make_shared<ODDetections>(); - for(auto & d : detectors_3d_) - { - detections_all->append(d->detect(scene)); - } - - return detections_all; - } - - template<typename PointT> - shared_ptr<ODDetections3D> ODDetectorMultiAlgo<PointT>::detectOmni(shared_ptr<ODScenePointCloud<PointT> > scene) - { - shared_ptr<ODDetections3D> detections_all = make_shared<ODDetections3D>(); - for(auto & d : detectors_3d_) - { - detections_all->append(d->detectOmni(scene)); - } - - return detections_all; - } - - -#ifndef DOXYGEN_SHOULD_SKIP_THIS - - - extern template - shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZ>::detectOmni(shared_ptr<ODScene> scene); - - extern template - shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZ>::detect(shared_ptr<ODScene> scene); - - extern template - void ODDetectorMultiAlgo<pcl::PointXYZ>::init(); - - extern template - shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZ>::detect(shared_ptr<ODScenePointCloud<pcl::PointXYZ> > scene); - - extern template - shared_ptr<ODDetections3D> ODDetectorMultiAlgo<pcl::PointXYZ>::detectOmni(shared_ptr<ODScenePointCloud<pcl::PointXYZ> > scene); - - - - extern template - shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZRGB>::detectOmni(shared_ptr<ODScene> scene); - - extern template - shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZRGB>::detect(shared_ptr<ODScene> scene); - - extern template - void ODDetectorMultiAlgo<pcl::PointXYZRGB>::init(); - - extern template - shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZRGB>::detect(shared_ptr<ODScenePointCloud<pcl::PointXYZRGB> > scene); - - extern template - shared_ptr<ODDetections3D> ODDetectorMultiAlgo<pcl::PointXYZRGB>::detectOmni(shared_ptr<ODScenePointCloud<pcl::PointXYZRGB> > scene); - - - - extern template - shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZRGBA>::detectOmni(shared_ptr<ODScene> scene); - - extern template - shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZRGBA>::detect(shared_ptr<ODScene> scene); - - extern template - void ODDetectorMultiAlgo<pcl::PointXYZRGBA>::init(); - - extern template - shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZRGBA>::detect(shared_ptr<ODScenePointCloud<pcl::PointXYZRGBA> > scene); - - extern template - shared_ptr<ODDetections3D> ODDetectorMultiAlgo<pcl::PointXYZRGBA>::detectOmni(shared_ptr<ODScenePointCloud<pcl::PointXYZRGBA> > scene); - - -#endif - -} \ No newline at end of file diff --git a/detectors/include/od/detectors/global2D/ODFaceRecognizer.h b/detectors/include/od/detectors/global2D/FaceRecognizer.h similarity index 73% rename from detectors/include/od/detectors/global2D/ODFaceRecognizer.h rename to detectors/include/od/detectors/global2D/FaceRecognizer.h index 98f83ebf..37998509 100644 --- a/detectors/include/od/detectors/global2D/ODFaceRecognizer.h +++ b/detectors/include/od/detectors/global2D/FaceRecognizer.h @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -27,7 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 16.06.15. // #pragma once -#include "od/common/pipeline/ODDetector.h" +#include "od/common/pipeline/Detector.h" #include <opencv2/face.hpp> namespace od @@ -39,20 +39,20 @@ namespace od /** \brief A facerecognizer based on EigenFace and FischerFace algorithms. * * Currently it just supports detection on fixed scene (detect()) and does not support multiscale detection. This is due to the fact that class for cascade classifier - - * ODCascadeDetector supports multiscale detection and can be easily integrated with this recognizer - first by finding face using the Cascade and then applying this recognizer on that detected window. + * CascadeDetector supports multiscale detection and can be easily integrated with this recognizer - first by finding face using the Cascade and then applying this recognizer on that detected window. * This is faster than trying to perform recognition on each multiscale window. * * \author Kripasindhu Sarkar * */ - class ODFaceRecognizer : public ObjectDetector + class FaceRecognizer : public ObjectDetector { public: - OD_DEFINE_ENUM_WITH_STRING_CONVERSIONS(FaceRecogType, (OD_FACE_FISCHER)(OD_FACE_EIGEN)) + _DEFINE_ENUM_WITH_STRING_CONVERSIONS(FaceRecogType, (_FACE_FISCHER)(_FACE_EIGEN)) - ODFaceRecognizer(FaceRecogType recog_type = OD_FACE_EIGEN, int num_components = 0, double threshold = DBL_MAX); + FaceRecognizer(FaceRecogType recog_type = _FACE_EIGEN, int num_components = 0, double threshold = DBL_MAX); void init(); @@ -62,13 +62,13 @@ namespace od int train(); - shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene); - shared_ptr<ODDetections> detectOmni(shared_ptr<ODSceneImage> scene); + shared_ptr<Detections> detect(shared_ptr<SceneImage> scene); + shared_ptr<Detections> detectOmni(shared_ptr<SceneImage> scene); - virtual shared_ptr<ODDetection> detect(shared_ptr<ODScene> scene); - virtual shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene); + virtual shared_ptr<Detection> detect(shared_ptr<Scene> scene); + virtual shared_ptr<Detections> detectOmni(shared_ptr<Scene> scene); - virtual int detect(shared_ptr<ODScene> scene, const std::vector<shared_ptr<ODDetection> > & detections); + virtual int detect(shared_ptr<Scene> scene, const std::vector<shared_ptr<Detection> > & detections); const FaceRecogType & getRecogtype() const; void setRecogtype(const FaceRecogType & recog_type); diff --git a/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h b/detectors/include/od/detectors/global2D/detection/CascadeDetector.h similarity index 72% rename from detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h rename to detectors/include/od/detectors/global2D/detection/CascadeDetector.h index 5d1a7d2c..42a4516a 100644 --- a/detectors/include/od/detectors/global2D/detection/ODCascadeDetector.h +++ b/detectors/include/od/detectors/global2D/detection/CascadeDetector.h @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -27,12 +27,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 17.07.15. // #pragma once -#include "od/common/pipeline/ODDetector.h" -#include "od/common/pipeline/ODScene.h" -#include "od/common/utils/ODUtils.h" -#include "od/detectors/global2D/detection/ODCascadeDetectorInterface.h" -#include "od/detectors/global2D/detection/ODCascadeDetectorImpl.h" -#include "od/gpu/detectors/global2D/detection/ODCascadeDetectorImpl.h" +#include "od/common/pipeline/Detector.h" +#include "od/common/pipeline/Scene.h" +#include "od/common/utils/Utils.h" +#include "od/detectors/global2D/detection/CascadeDetectorInterface.h" +#include "od/detectors/global2D/detection/CascadeDetectorImpl.h" +#include "od/gpu/detectors/global2D/detection/CascadeDetectorImpl.h" #include <opencv2/opencv.hpp> @@ -41,7 +41,7 @@ namespace od namespace g2d { /** \brief A class for detection using Cascade classifiers. - * Given a scene and a cascade classifier, this class performs a classification and returns detections. The training is not supported in OD currently but is compatible to the cascade training of OpenCV. + * Given a scene and a cascade classifier, this class performs a classification and returns detections. The training is not supported in currently but is compatible to the cascade training of OpenCV. * Train your cascade classifiers using OpenCV's *opencv_traincascade* utility (http://docs.opencv.org/master/dc/d88/tutorial_traincascade.html#gsc.tab=0). It is a great tool for traning your cascade. * Paste the generated xml in trained_data_location_/TD_CASCADE/\*.cascade.xml to use your trained cascade. * @@ -49,20 +49,20 @@ namespace od * */ - class ODCascadeDetector : public ODDetector2D + class CascadeDetector : public Detector2D { public: - ODCascadeDetector(bool gpu = false, const std::string & trainer = std::string("cascade.xml"), const std::string & trained_data_location = std::string(""), double scale_factor = 1.1, int min_neighbors = 3, + CascadeDetector(bool gpu = false, const std::string & trainer = std::string("cascade.xml"), const std::string & trained_data_location = std::string(""), double scale_factor = 1.1, int min_neighbors = 3, int flags = 0, const cv::Size & min_size = cv::Size(), const cv::Size & max_size = cv::Size()); void init(); - shared_ptr<ODDetections2D> detectOmni(shared_ptr<ODSceneImage> scene); - shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene); + shared_ptr<Detections2D> detectOmni(shared_ptr<SceneImage> scene); + shared_ptr<Detections> detect(shared_ptr<SceneImage> scene); - shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene); + shared_ptr<Detections> detectOmni(shared_ptr<Scene> scene); void setScale(const float scale); float getScale() const; @@ -78,7 +78,7 @@ namespace od private: - shared_ptr<ODCascadeDetectorInterface> cascade_detector_; + shared_ptr<CascadeDetectorInterface> cascade_detector_; }; diff --git a/detectors/include/od/detectors/global2D/detection/ODCascadeDetectorImpl.h b/detectors/include/od/detectors/global2D/detection/CascadeDetectorImpl.h similarity index 78% rename from detectors/include/od/detectors/global2D/detection/ODCascadeDetectorImpl.h rename to detectors/include/od/detectors/global2D/detection/CascadeDetectorImpl.h index 29570185..c57b887d 100644 --- a/detectors/include/od/detectors/global2D/detection/ODCascadeDetectorImpl.h +++ b/detectors/include/od/detectors/global2D/detection/CascadeDetectorImpl.h @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -27,10 +27,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 17.07.15. // #pragma once -#include "od/common/pipeline/ODDetector.h" -#include "od/common/pipeline/ODScene.h" -#include "od/common/utils/ODUtils.h" -#include "od/detectors/global2D/detection/ODCascadeDetectorInterface.h" +#include "od/common/pipeline/Detector.h" +#include "od/common/pipeline/Scene.h" +#include "od/common/utils/Utils.h" +#include "od/detectors/global2D/detection/CascadeDetectorInterface.h" #include <opencv2/opencv.hpp> namespace od @@ -38,26 +38,26 @@ namespace od namespace g2d { /** \brief A class for detection using Cascade classifiers. - * Given a scene and a cascade classifier, this class performs a classification and returns detections. The training is not supported in OD currently but is compatible to the cascade training of OpenCV. + * Given a scene and a cascade classifier, this class performs a classification and returns detections. The training is not supported in currently but is compatible to the cascade training of OpenCV. * Train your cascade classifiers using OpenCV's *opencv_traincascade* utility (http://docs.opencv.org/master/dc/d88/tutorial_traincascade.html#gsc.tab=0). It is a great tool for traning your cascade. * Paste the generated xml in trained_data_location_/TD_CASCADE/\*.cascade.xml to use your trained cascade. * * \author Kripasindhu Sarkar * */ - class ODCascadeDetectorImpl : public ODCascadeDetectorInterface + class CascadeDetectorImpl : public CascadeDetectorInterface { public: - ODCascadeDetectorImpl(const std::string & trainer = std::string("cascade.xml"), const std::string & trained_data_location = std::string(""), double scale_factor = 1.1, int min_neighbors = 3, + CascadeDetectorImpl(const std::string & trainer = std::string("cascade.xml"), const std::string & trained_data_location = std::string(""), double scale_factor = 1.1, int min_neighbors = 3, int flags = 0, const cv::Size & min_size = cv::Size(), const cv::Size & max_size = cv::Size()); void init(); - shared_ptr<ODDetections2D> detectOmni(shared_ptr<ODSceneImage> scene); - shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene); + shared_ptr<Detections2D> detectOmni(shared_ptr<SceneImage> scene); + shared_ptr<Detections> detect(shared_ptr<SceneImage> scene); - shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene); + shared_ptr<Detections> detectOmni(shared_ptr<Scene> scene); void setScale(const float scale); float getScale() const; diff --git a/detectors/include/od/detectors/global2D/detection/ODCascadeDetectorInterface.h b/detectors/include/od/detectors/global2D/detection/CascadeDetectorInterface.h similarity index 83% rename from detectors/include/od/detectors/global2D/detection/ODCascadeDetectorInterface.h rename to detectors/include/od/detectors/global2D/detection/CascadeDetectorInterface.h index 98e5b43e..b87bb04b 100644 --- a/detectors/include/od/detectors/global2D/detection/ODCascadeDetectorInterface.h +++ b/detectors/include/od/detectors/global2D/detection/CascadeDetectorInterface.h @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -28,17 +28,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // #pragma once #include <opencv2/opencv.hpp> -#include "od/common/utils/ODShared_pointers.h" +#include "od/common/utils/Shared_pointers.h" namespace od { - class ODCascadeDetectorInterface + class CascadeDetectorInterface { public: - virtual shared_ptr<ODDetections2D> detectOmni(shared_ptr<ODSceneImage> scene) = 0; - virtual shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene) = 0; + virtual shared_ptr<Detections2D> detectOmni(shared_ptr<SceneImage> scene) = 0; + virtual shared_ptr<Detections> detect(shared_ptr<SceneImage> scene) = 0; - virtual shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene) = 0; + virtual shared_ptr<Detections> detectOmni(shared_ptr<Scene> scene) = 0; virtual void init() = 0; diff --git a/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h b/detectors/include/od/detectors/global2D/detection/HOGDetector.h similarity index 76% rename from detectors/include/od/detectors/global2D/detection/ODHOGDetector.h rename to detectors/include/od/detectors/global2D/detection/HOGDetector.h index b2dbc420..230a83e1 100644 --- a/detectors/include/od/detectors/global2D/detection/ODHOGDetector.h +++ b/detectors/include/od/detectors/global2D/detection/HOGDetector.h @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -27,12 +27,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 15.07.15. // #pragma once -#include "od/common/pipeline/ODDetector.h" -#include "od/common/pipeline/ODScene.h" -#include "od/common/utils/ODUtils.h" -#include "od/common/utils/ODFeatureDetector2D.h" -#include "od/common/utils/ODShared_pointers.h" -#include "od/common/bindings/ODSvmlight.h" +#include "od/common/pipeline/Detector.h" +#include "od/common/pipeline/Scene.h" +#include "od/common/utils/Utils.h" +#include "od/common/utils/FeatureDetector2D.h" +#include "od/common/utils/Shared_pointers.h" +#include "od/common/bindings/Svmlight.h" #include <iostream> #include <opencv2/objdetect.hpp> @@ -47,7 +47,7 @@ namespace od * This class takes an image as an input, finds its HOG features using the parameters containing in the trained data, runs the linear classifier (present in the trained data) on the computed HOG features * and finally produces the classification output. Covers both multiscale detection (detectOmni()) and detection on a fixed scene (detect() - the scene is resized to the HOG window size). * - * Use ODHOGTrainer for the training of new objects. By default this class provides two people detector: OD_DEFAULT_PEOPLE, the people detector from OpenCV and OD_DAIMLER_PEOPLE, the second detector available from OpenCV. + * Use HOGTrainer for the training of new objects. By default this class provides two people detector: _DEFAULT_PEOPLE, the people detector from OpenCV and _DAIMLER_PEOPLE, the second detector available from OpenCV. * One can set any linear classifier, the linear weight vector through setSVMDetector() function but it is highly not recommended as the HOG parameters and the weight vector can be out of sync. * One must set appropriate HOG parameters after using setSVMDetector() function with which the weight vector was trained. * @@ -56,15 +56,15 @@ namespace od */ - class ODHOGDetector : public ODDetector2D + class HOGDetector : public Detector2D { public: - ODHOGDetector(const std::string & trained_data_location_ = std::string(""), const cv::Size & win_size = cv::Size(64,128), + HOGDetector(const std::string & trained_data_location_ = std::string(""), const cv::Size & win_size = cv::Size(64,128), const cv::Size & block_size = cv::Size(16,16), const cv::Size & block_stride = cv::Size(8,8), const cv::Size & cell_size = cv::Size(8,8), float hit_threshold = 0.0); - OD_DEFINE_ENUM_WITH_STRING_CONVERSIONS(SVMType, (OD_DEFAULT_PEOPLE)(OD_DAIMLER_PEOPLE)(OD_FILE)) + _DEFINE_ENUM_WITH_STRING_CONVERSIONS(SVMType, (_DEFAULT_PEOPLE)(_DAIMLER_PEOPLE)(_FILE)) void init(); void load(const std::string & file_name); @@ -73,10 +73,10 @@ namespace od void setSVMDetector(std::vector<float> svm_detector); - shared_ptr<ODDetections2D> detectOmni(shared_ptr<ODSceneImage> scene); - shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene); + shared_ptr<Detections2D> detectOmni(shared_ptr<SceneImage> scene); + shared_ptr<Detections> detect(shared_ptr<SceneImage> scene); - int detect(shared_ptr<ODScene> scene, std::vector<shared_ptr<ODDetection> > & detections); + int detect(shared_ptr<Scene> scene, std::vector<shared_ptr<Detection> > & detections); void setTrainedDataLocation(const std::string & trained_data_location); @@ -100,7 +100,7 @@ namespace od void printParameters(); - shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene); + shared_ptr<Detections> detectOmni(shared_ptr<Scene> scene); protected: //properteis @@ -119,7 +119,7 @@ namespace od /** \examples objectdetector/od_image_hog.cpp * \examples objectdetector/od_image_hog_files.cpp * \examples apps/global2D/od_multihog_app.cpp - * This is an example of how to use the ODHOGDetector class. + * This is an example of how to use the HOGDetector class. * * More details about this example. */ diff --git a/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h b/detectors/include/od/detectors/global2D/training/HOGTrainer.h similarity index 89% rename from detectors/include/od/detectors/global2D/training/ODHOGTrainer.h rename to detectors/include/od/detectors/global2D/training/HOGTrainer.h index d93c2645..3a00c973 100644 --- a/detectors/include/od/detectors/global2D/training/ODHOGTrainer.h +++ b/detectors/include/od/detectors/global2D/training/HOGTrainer.h @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -30,8 +30,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <opencv2/objdetect.hpp> #include <opencv2/opencv.hpp> #include <fstream> -#include "od/common/pipeline/ODTrainer.h" -#include "od/common/bindings/ODSvmlight.h" +#include "od/common/pipeline/Trainer.h" +#include "od/common/bindings/Svmlight.h" namespace od { @@ -39,19 +39,19 @@ namespace od { /** \brief Class for training HOG based detector. * - * Use ODHOGDetector after training with this class. This is the training class for training HOG based detector. SVMlight is used here to train linear SVM on the HOG features. It supports the usage of multiple random windows in negetive training images + * Use HOGDetector after training with this class. This is the training class for training HOG based detector. SVMlight is used here to train linear SVM on the HOG features. It supports the usage of multiple random windows in negetive training images * to increase the number of negetive features by the function 'setNOFeaturesNeg'. It also supports "Hard negetive" training which collects all the false positive * windows after initial training to retrain and obtain a new feature vector. Use the function 'setTrainHardNegetive' to enable this feature. * * \author Kripasindhu Sarkar */ - class ODHOGTrainer : public ODTrainer + class HOGTrainer : public Trainer { public: - ODHOGTrainer(const std::string & training_input_location_ = std::string(""), const std::string & trained_data_location_ = std::string(""), + HOGTrainer(const std::string & training_input_location_ = std::string(""), const std::string & trained_data_location_ = std::string(""), const cv::Size & win_size = cv::Size(64,128), const cv::Size & block_size = cv::Size(16,16), const cv::Size & block_stride = cv::Size(8,8), const cv::Size & cell_size = cv::Size(8,8), float hits_threshold = 0.0); diff --git a/detectors/include/od/detectors/global3D/ODPointCloudGlobalMatching.h b/detectors/include/od/detectors/global3D/PointCloudGlobalMatching.h similarity index 74% rename from detectors/include/od/detectors/global3D/ODPointCloudGlobalMatching.h rename to detectors/include/od/detectors/global3D/PointCloudGlobalMatching.h index 3704a37d..23891d84 100644 --- a/detectors/include/od/detectors/global3D/ODPointCloudGlobalMatching.h +++ b/detectors/include/od/detectors/global3D/PointCloudGlobalMatching.h @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -27,34 +27,34 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 16.06.15. // #pragma once -#include "od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h" -#include "od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp" +#include "od/detectors/global3D/training/CADDetectTrainer3DGlobal.h" +#include "od/detectors/global3D/detection/CADDetector3DGlobal.hpp" namespace od { namespace g3d { - /** \brief ODPointCloudGlobalMatching: global feature based object detection in point cloud + /** \brief PointCloudGlobalMatching: global feature based object detection in point cloud * * \author Kripasindhu Sarkar * */ - class ODPointCloudGlobalMatching : ObjectDetector + class PointCloudGlobalMatching : ObjectDetector { void init(){} int train(); - int detect(shared_ptr<ODScene> scene, std::vector<shared_ptr<ODDetection>> & detections); + int detect(shared_ptr<Scene> scene, std::vector<shared_ptr<Detection>> & detections); protected: std::string desc_name_; - shared_ptr<ODCADDetectTrainer3DGlobal> trainer_; - shared_ptr<ODCADDetector3DGlobal<pcl::PointXYZ> > detector_; + shared_ptr<CADDetectTrainer3DGlobal> trainer_; + shared_ptr<CADDetector3DGlobal<pcl::PointXYZ> > detector_; }; } diff --git a/detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h b/detectors/include/od/detectors/global3D/training/CADDetectTrainer3DGlobal.h similarity index 84% rename from detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h rename to detectors/include/od/detectors/global3D/training/CADDetectTrainer3DGlobal.h index 7c859835..3c3668cc 100644 --- a/detectors/include/od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h +++ b/detectors/include/od/detectors/global3D/training/CADDetectTrainer3DGlobal.h @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -28,15 +28,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // #pragma once #include <pcl/apps/3d_rec_framework/pc_source/mesh_source.h> -#include "od/common/pipeline/ODTrainer.h" +#include "od/common/pipeline/Trainer.h" namespace od { namespace g3d { -/** \brief Training class for the detector ODCADDetector3DGlobal. +/** \brief Training class for the detector CADDetector3DGlobal. * - * This class uses PCL 3d_recognition_framework in the background for the training of 3D CAD models (in PLY format) and should be used with ODCADDetector3DGlobal for their detection in a pointcloud. + * This class uses PCL 3d_recognition_framework in the background for the training of 3D CAD models (in PLY format) and should be used with CADDetector3DGlobal for their detection in a pointcloud. * In the training_input_location_ the CAD models should be arranged in the following structure: * * - \<training_input_location_\> @@ -59,17 +59,17 @@ namespace od - /home/user/DB/fruit/banana.ply - After the training use the detector class ODCADDetector3DGlobal. + After the training use the detector class CADDetector3DGlobal. * * \author Kripasindhu Sarkar * */ - class ODCADDetectTrainer3DGlobal : public ODTrainer + class CADDetectTrainer3DGlobal : public Trainer { public: - ODCADDetectTrainer3DGlobal(const std::string & training_input_location_ = std::string(""), + CADDetectTrainer3DGlobal(const std::string & training_input_location_ = std::string(""), const std::string & training_data_location_ = std::string("")); int train(); diff --git a/detectors/include/od/detectors/local2D/ODImageLocalMatching.h b/detectors/include/od/detectors/local2D/ImageLocalMatching.h similarity index 59% rename from detectors/include/od/detectors/local2D/ODImageLocalMatching.h rename to detectors/include/od/detectors/local2D/ImageLocalMatching.h index 96fbee21..a13d9d12 100644 --- a/detectors/include/od/detectors/local2D/ODImageLocalMatching.h +++ b/detectors/include/od/detectors/local2D/ImageLocalMatching.h @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -28,74 +28,74 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 05.06.15. // #pragma once -#include "od/common/pipeline/ODScene.h" -#include "od/common/pipeline/ODTrainer.h" -#include "od/common/pipeline/ODDetector.h" +#include "od/common/pipeline/Scene.h" +#include "od/common/pipeline/Trainer.h" +#include "od/common/pipeline/Detector.h" namespace od { namespace l2d { - /** \brief ODImageLocalMatchingTrainer + /** \brief ImageLocalMatchingTrainer * * \author Kripasindhu Sarkar * */ - class ODImageLocalMatchingTrainer : public ODTrainer + class ImageLocalMatchingTrainer : public Trainer { public: - ODImageLocalMatchingTrainer(const std::string & training_input_location, const std::string & training_data_location); + ImageLocalMatchingTrainer(const std::string & training_input_location, const std::string & training_data_location); }; - /** \brief ODImageLocalMatchingDetector + /** \brief ImageLocalMatchingDetector * * \author Kripasindhu Sarkar * */ - class ODImageLocalMatchingDetector : public ODDetector2DComplete + class ImageLocalMatchingDetector : public Detector2DComplete { public: - ODImageLocalMatchingDetector(const std::string & training_data_location); + ImageLocalMatchingDetector(const std::string & training_data_location); }; - /** \brief ODImageLocalMatching + /** \brief ImageLocalMatching * * \author Kripasindhu Sarkar * */ - class ODImageLocalMatching : public ObjectDetector + class ImageLocalMatching : public ObjectDetector { public: - shared_ptr<ODImageLocalMatchingTrainer> getTrainer() const; + shared_ptr<ImageLocalMatchingTrainer> getTrainer() const; - void setTrainer(shared_ptr<ODImageLocalMatchingTrainer> trainer); + void setTrainer(shared_ptr<ImageLocalMatchingTrainer> trainer); - shared_ptr<ODImageLocalMatchingDetector> getDetector() const; + shared_ptr<ImageLocalMatchingDetector> getDetector() const; - void setDetector(shared_ptr<ODImageLocalMatchingDetector> detector); + void setDetector(shared_ptr<ImageLocalMatchingDetector> detector); - ODImageLocalMatching(); + ImageLocalMatching(); void init() {} int train(); - int detect(shared_ptr<ODScene> scene, const std::vector<shared_ptr<ODDetection> > & detections); + int detect(shared_ptr<Scene> scene, const std::vector<shared_ptr<Detection> > & detections); protected: - shared_ptr<ODImageLocalMatchingTrainer> trainer_; - shared_ptr<ODImageLocalMatchingDetector> detector_; + shared_ptr<ImageLocalMatchingTrainer> trainer_; + shared_ptr<ImageLocalMatchingDetector> detector_; }; diff --git a/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h b/detectors/include/od/detectors/local2D/detection/CADRecognizer2DLocal.h similarity index 78% rename from detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h rename to detectors/include/od/detectors/local2D/detection/CADRecognizer2DLocal.h index 1a43c95b..e074438f 100644 --- a/detectors/include/od/detectors/local2D/detection/ODCADRecognizer2DLocal.h +++ b/detectors/include/od/detectors/local2D/detection/CADRecognizer2DLocal.h @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -28,11 +28,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 08.06.15. // #pragma once -#include "od/detectors/local2D/ODImageLocalMatching.h" -#include "od/common/pipeline/ODScene.h" -#include "od/common/utils/ODFeatureDetector.h" -#include "od/detectors/local2D/simple_ransac_detection/ODMesh.h" -#include "od/detectors/local2D/simple_ransac_detection/ODRobustMatcher.h" +#include "od/detectors/local2D/ImageLocalMatching.h" +#include "od/common/pipeline/Scene.h" +#include "od/common/utils/FeatureDetector.h" +#include "od/detectors/local2D/simple_ransac_detection/Mesh.h" +#include "od/detectors/local2D/simple_ransac_detection/RobustMatcher.h" #include <iostream> @@ -45,17 +45,17 @@ namespace od /** \brief Simple RANSAC based 3D object recognizer. * * A recognizer which uses local features like SIFT/SURF to perform object recognition. - * Given a 'trained model' trained by ODCADRecogTrainerSnapshotBased or trained externally (manually augmenting features in 3D cad models), this class performs a complete detection + * Given a 'trained model' trained by CADRecogTrainerSnapshotBased or trained externally (manually augmenting features in 3D cad models), this class performs a complete detection * in an image. It first extracts 2D features from the scene, matches them with all the feature augmented models (the trained data) and in the end solves PnP under RANSAC. * * \author Kripasindhu Sarkar * */ - class ODCADRecognizer2DLocal : public ODImageLocalMatchingDetector + class CADRecognizer2DLocal : public ImageLocalMatchingDetector { public: - ODCADRecognizer2DLocal(const std::string & trained_data_location_ = std::string()); + CADRecognizer2DLocal(const std::string & trained_data_location_ = std::string()); const std::string & getCameraIntrinsicFile() const; @@ -98,15 +98,15 @@ namespace od void init(); - shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene); - shared_ptr<ODDetections3D> detectOmni(shared_ptr<ODSceneImage> scene); + shared_ptr<Detections> detect(shared_ptr<SceneImage> scene); + shared_ptr<Detections3D> detectOmni(shared_ptr<SceneImage> scene); - shared_ptr<ODDetections> detect(shared_ptr<ODScene> scene); - shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene); + shared_ptr<Detections> detect(shared_ptr<Scene> scene); + shared_ptr<Detections> detectOmni(shared_ptr<Scene> scene); protected: - bool detectSingleModel(shared_ptr<ODSceneImage> scene, const Model & model, shared_ptr<ODDetection3D> & detection3D, + bool detectSingleModel(shared_ptr<SceneImage> scene, const Model & model, shared_ptr<Detection3D> & detection3D, const cv::Mat & frame_viz); std::string camera_intrinsic_file_; @@ -139,7 +139,7 @@ namespace od std::vector<Model> models_; PnPProblem pnp_detection_; FeatureType f_type_default_; - shared_ptr<ODFeatureDetector> feature_detector_; + shared_ptr<FeatureDetector> feature_detector_; }; diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/ODCsvReader.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvReader.h similarity index 94% rename from detectors/include/od/detectors/local2D/simple_ransac_detection/ODCsvReader.h rename to detectors/include/od/detectors/local2D/simple_ransac_detection/CsvReader.h index 574c2bf0..cf530ce9 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/ODCsvReader.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvReader.h @@ -4,7 +4,7 @@ #include <opencv2/core/core.hpp> #include <string> #include <stdlib.h> -#include "od/detectors/local2D/simple_ransac_detection/ODUtils.h" +#include "od/detectors/local2D/simple_ransac_detection/Utils.h" namespace od { diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/ODCsvWriter.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvWriter.h similarity index 89% rename from detectors/include/od/detectors/local2D/simple_ransac_detection/ODCsvWriter.h rename to detectors/include/od/detectors/local2D/simple_ransac_detection/CsvWriter.h index 94dc5fc6..ad9ad194 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/ODCsvWriter.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/CsvWriter.h @@ -2,7 +2,7 @@ #include <iostream> #include <fstream> #include <opencv2/core/core.hpp> -#include "od/detectors/local2D/simple_ransac_detection/ODUtils.h" +#include "od/detectors/local2D/simple_ransac_detection/Utils.h" namespace od { diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/ODMesh.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/Mesh.h similarity index 96% rename from detectors/include/od/detectors/local2D/simple_ransac_detection/ODMesh.h rename to detectors/include/od/detectors/local2D/simple_ransac_detection/Mesh.h index 148293a0..7a850f09 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/ODMesh.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/Mesh.h @@ -8,7 +8,7 @@ #pragma once #include <iostream> #include <opencv2/core/core.hpp> -#include "od/detectors/local2D/simple_ransac_detection/ODCsvReader.h" +#include "od/detectors/local2D/simple_ransac_detection/CsvReader.h" namespace od { diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/ODModel.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/Model.h similarity index 96% rename from detectors/include/od/detectors/local2D/simple_ransac_detection/ODModel.h rename to detectors/include/od/detectors/local2D/simple_ransac_detection/Model.h index b013dbf3..6d2d5098 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/ODModel.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/Model.h @@ -12,7 +12,7 @@ #include <pugixml.hpp> #include <sstream> #include <boost/algorithm/string.hpp> -#include "od/detectors/local2D/simple_ransac_detection/ODCsvWriter.h" +#include "od/detectors/local2D/simple_ransac_detection/CsvWriter.h" namespace od { diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/ODModelRegistration.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/ModelRegistration.h similarity index 100% rename from detectors/include/od/detectors/local2D/simple_ransac_detection/ODModelRegistration.h rename to detectors/include/od/detectors/local2D/simple_ransac_detection/ModelRegistration.h diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/ODPnPProblem.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/PnPProblem.h similarity index 92% rename from detectors/include/od/detectors/local2D/simple_ransac_detection/ODPnPProblem.h rename to detectors/include/od/detectors/local2D/simple_ransac_detection/PnPProblem.h index 90bd84f9..d4ed8596 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/ODPnPProblem.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/PnPProblem.h @@ -10,9 +10,9 @@ #include <opencv2/calib3d/calib3d.hpp> #include <iostream> #include <sstream> -#include "od/detectors/local2D/simple_ransac_detection/ODMesh.h" -#include "od/detectors/local2D/simple_ransac_detection/ODModelRegistration.h" -#include "od/common/utils/ODShared_pointers.h" +#include "od/detectors/local2D/simple_ransac_detection/Mesh.h" +#include "od/detectors/local2D/simple_ransac_detection/ModelRegistration.h" +#include "od/common/utils/Shared_pointers.h" class Mesh; class Ray; diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/ODRobustMatcher.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/RobustMatcher.h similarity index 92% rename from detectors/include/od/detectors/local2D/simple_ransac_detection/ODRobustMatcher.h rename to detectors/include/od/detectors/local2D/simple_ransac_detection/RobustMatcher.h index 03865120..0b159cad 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/ODRobustMatcher.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/RobustMatcher.h @@ -13,10 +13,10 @@ #include <opencv2/cudafeatures2d.hpp> #include <opencv2/xfeatures2d.hpp> #include <opencv2/ml.hpp> -#include "od/common/utils/ODFeatureDetector2D.h" -#include "od/common/utils/ODUtils.h" -#include "od/detectors/local2D/simple_ransac_detection/ODModel.h" -#include "od/detectors/local2D/simple_ransac_detection/ODUtils.h" +#include "od/common/utils/FeatureDetector2D.h" +#include "od/common/utils/Utils.h" +#include "od/detectors/local2D/simple_ransac_detection/Model.h" +#include "od/detectors/local2D/simple_ransac_detection/Utils.h" namespace od { @@ -86,7 +86,7 @@ namespace od { void instantiateMatcher1(const Model & model, bool use_gpu); // pointer to the feature point detector object - shared_ptr<od::ODFeatureDetector2D> featureDetector_; + shared_ptr<od::FeatureDetector2D> featureDetector_; shared_ptr<cv::FeatureDetector> detector_; // pointer to the feature descriptor extractor object diff --git a/detectors/include/od/detectors/local2D/simple_ransac_detection/ODUtils.h b/detectors/include/od/detectors/local2D/simple_ransac_detection/Utils.h similarity index 90% rename from detectors/include/od/detectors/local2D/simple_ransac_detection/ODUtils.h rename to detectors/include/od/detectors/local2D/simple_ransac_detection/Utils.h index 201479a2..d573dc5c 100644 --- a/detectors/include/od/detectors/local2D/simple_ransac_detection/ODUtils.h +++ b/detectors/include/od/detectors/local2D/simple_ransac_detection/Utils.h @@ -6,10 +6,10 @@ */ #pragma once #include <iostream> -#include "od/detectors/local2D/simple_ransac_detection/ODPnPProblem.h" -#include "od/detectors/local2D/simple_ransac_detection/ODModelRegistration.h" -#include "od/detectors/local2D/simple_ransac_detection/ODModel.h" -#include "od/detectors/local2D/simple_ransac_detection/ODMesh.h" +#include "od/detectors/local2D/simple_ransac_detection/PnPProblem.h" +#include "od/detectors/local2D/simple_ransac_detection/ModelRegistration.h" +#include "od/detectors/local2D/simple_ransac_detection/Model.h" +#include "od/detectors/local2D/simple_ransac_detection/Mesh.h" #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/calib3d/calib3d.hpp> diff --git a/detectors/include/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h b/detectors/include/od/detectors/local2D/training/CADRecogTrainerSnapshotBased.h similarity index 88% rename from detectors/include/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h rename to detectors/include/od/detectors/local2D/training/CADRecogTrainerSnapshotBased.h index 9fbb2e18..ef39cbd6 100644 --- a/detectors/include/od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h +++ b/detectors/include/od/detectors/local2D/training/CADRecogTrainerSnapshotBased.h @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -28,7 +28,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 17.03.15. // #pragma once -#include "od/detectors/local2D/ODImageLocalMatching.h" +#include "od/detectors/local2D/ImageLocalMatching.h" #include "pugixml.hpp" #include <vtkSphereSource.h> @@ -58,20 +58,20 @@ namespace od { namespace l2d { - /** \brief ODCADRecogTrainerSnapshotBased; One of the new algorithm; details will be explained later + /** \brief CADRecogTrainerSnapshotBased; One of the new algorithm; details will be explained later * * \author Kripasindhu Sarkar * */ - class ODCADRecogTrainerSnapshotBased : public ODImageLocalMatchingTrainer + class CADRecogTrainerSnapshotBased : public ImageLocalMatchingTrainer { public: - ODCADRecogTrainerSnapshotBased(const std::string & training_input_location = std::string(""), + CADRecogTrainerSnapshotBased(const std::string & training_input_location = std::string(""), const std::string & training_data_location = std::string("")) : - ODImageLocalMatchingTrainer(training_input_location, training_data_location){} + ImageLocalMatchingTrainer(training_input_location, training_data_location){} int train(); void init() {} diff --git a/detectors/src/global2D/CMakeLists.txt b/detectors/src/global2D/CMakeLists.txt index 41ab6898..aac239d9 100644 --- a/detectors/src/global2D/CMakeLists.txt +++ b/detectors/src/global2D/CMakeLists.txt @@ -11,14 +11,14 @@ if(BUILD_GLOBAL_2D_DETECTION) endif() set(SOURCES - "ODFaceRecognizer.cpp" - "detection/ODCascadeDetector.cpp" - "detection/ODCascadeDetectorImpl.cpp" + "FaceRecognizer.cpp" + "detection/CascadeDetector.cpp" + "detection/CascadeDetectorImpl.cpp" ) if(WITH_SVMLIGHT) - set(SOURCES ${SOURCES} "training/ODHOGTrainer.cpp" "detection/ODHOGDetector.cpp") + set(SOURCES ${SOURCES} "training/HOGTrainer.cpp" "detection/HOGDetector.cpp") include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) set(SUBSYS_DEPS ${SUBSYS_DEPS} svmlight) endif() @@ -26,6 +26,7 @@ if(BUILD_GLOBAL_2D_DETECTION) include_directories(${OpenCV_INCLUDE_DIRS}) include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) include_directories(${PCL_INCLUDE_DIRS}) OD_ADD_LIBRARY("${SUBSYS_NAME}" SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) diff --git a/detectors/src/global2D/ODFaceRecognizer.cpp b/detectors/src/global2D/FaceRecognizer.cpp similarity index 72% rename from detectors/src/global2D/ODFaceRecognizer.cpp rename to detectors/src/global2D/FaceRecognizer.cpp index b9f3c60f..ab5e7871 100644 --- a/detectors/src/global2D/ODFaceRecognizer.cpp +++ b/detectors/src/global2D/FaceRecognizer.cpp @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -27,14 +27,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 16.07.15. // -#include "od/detectors/global2D/ODFaceRecognizer.h" +#include "od/detectors/global2D/FaceRecognizer.h" namespace od { namespace g2d { - ODFaceRecognizer::ODFaceRecognizer(FaceRecogType recog_type, int num_components, double threshold): + FaceRecognizer::FaceRecognizer(FaceRecogType recog_type, int num_components, double threshold): recog_type_(recog_type), num_components_(num_components), threshold_(threshold), im_height_(0), im_width_(0) { @@ -42,14 +42,14 @@ namespace od trained_data_ext_ = std::string("facerec.xml"); } - void ODFaceRecognizer::init() + void FaceRecognizer::init() { switch(recog_type_) { - case OD_FACE_FISCHER: + case _FACE_FISCHER: cv_recognizer_ = cv::face::createFisherFaceRecognizer(num_components_, threshold_); break; - case OD_FACE_EIGEN: + case _FACE_EIGEN: cv_recognizer_ = cv::face::createEigenFaceRecognizer(num_components_, threshold_); break; default: @@ -57,12 +57,12 @@ namespace od } } - void ODFaceRecognizer::initTrainer() + void FaceRecognizer::initTrainer() { init(); } - void ODFaceRecognizer::initDetector() + void FaceRecognizer::initDetector() { if(!trained_) { @@ -83,7 +83,7 @@ namespace od } } - int ODFaceRecognizer::train() + int FaceRecognizer::train() { std::vector<cv::Mat> images; std::vector<int> labels; @@ -108,32 +108,32 @@ namespace od return 0; } - shared_ptr<ODDetection> ODFaceRecognizer::detect(shared_ptr<ODScene> scene) + shared_ptr<Detection> FaceRecognizer::detect(shared_ptr<Scene> scene) { - std::cout << "not implemented, use with shared_ptr<ODSceneImage>" <<std::endl; + std::cout << "not implemented, use with shared_ptr<SceneImage>" <<std::endl; return nullptr; }; - shared_ptr<ODDetections> ODFaceRecognizer::detectOmni(shared_ptr<ODScene> scene) + shared_ptr<Detections> FaceRecognizer::detectOmni(shared_ptr<Scene> scene) { - std::cout << "not implemented, use with shared_ptr<ODSceneImage>" <<std::endl; + std::cout << "not implemented, use with shared_ptr<SceneImage>" <<std::endl; return nullptr; }; - int ODFaceRecognizer::detect(shared_ptr<ODScene> scene, const std::vector<shared_ptr<ODDetection> > & detections) + int FaceRecognizer::detect(shared_ptr<Scene> scene, const std::vector<shared_ptr<Detection> > & detections) { - std::cout << "not implemented, use with shared_ptr<ODSceneImage>" <<std::endl; + std::cout << "not implemented, use with shared_ptr<SceneImage>" <<std::endl; return -1; } - shared_ptr<ODDetections> ODFaceRecognizer::detectOmni(shared_ptr<ODSceneImage> scene) + shared_ptr<Detections> FaceRecognizer::detectOmni(shared_ptr<SceneImage> scene) { std::cout << "not implemented, use detect()" <<std::endl; return nullptr; }; - shared_ptr<ODDetections> ODFaceRecognizer::detect(shared_ptr<ODSceneImage> scene) + shared_ptr<Detections> FaceRecognizer::detect(shared_ptr<SceneImage> scene) { cv::Mat face_edited; cv::cvtColor(scene->getCVImage(), face_edited, CV_BGR2GRAY); @@ -148,15 +148,15 @@ namespace od cv_recognizer_->predict(face_edited, label, confidence); //fill in the detection - shared_ptr<ODDetection2D> detection(new ODDetection2D(ODDetection::OD_CLASSIFICATION, std::to_string(label), confidence)); - shared_ptr<ODDetections2D> detections = make_shared<ODDetections2D>(); + shared_ptr<Detection2D> detection(new Detection2D(Detection::CLASSIFICATION, std::to_string(label), confidence)); + shared_ptr<Detections2D> detections = make_shared<Detections2D>(); detections->push_back(detection); return detections; } - void ODFaceRecognizer::read_csv(const std::string & filename, std::vector<cv::Mat> & images, std::vector<int> & labels, + void FaceRecognizer::read_csv(const std::string & filename, std::vector<cv::Mat> & images, std::vector<int> & labels, const std::string & separator) { std::ifstream file(filename.c_str(), std::ifstream::in); @@ -179,32 +179,32 @@ namespace od } } - const ODFaceRecognizer::FaceRecogType & ODFaceRecognizer::getRecogtype() const + const FaceRecognizer::FaceRecogType & FaceRecognizer::getRecogtype() const { return recog_type_; } - void ODFaceRecognizer::setRecogtype(const ODFaceRecognizer::FaceRecogType & recog_type) + void FaceRecognizer::setRecogtype(const FaceRecognizer::FaceRecogType & recog_type) { recog_type_ = recog_type; } - int ODFaceRecognizer::getThreshold() const + int FaceRecognizer::getThreshold() const { return threshold_; } - void ODFaceRecognizer::setThreshold(int threshold) + void FaceRecognizer::setThreshold(int threshold) { threshold_ = threshold; } - int ODFaceRecognizer::getNumComponents() const + int FaceRecognizer::getNumComponents() const { return num_components_; } - void ODFaceRecognizer::setNumComponents(int num_components) + void FaceRecognizer::setNumComponents(int num_components) { num_components_ = num_components; } diff --git a/detectors/src/global2D/detection/ODCascadeDetector.cpp b/detectors/src/global2D/detection/CascadeDetector.cpp similarity index 59% rename from detectors/src/global2D/detection/ODCascadeDetector.cpp rename to detectors/src/global2D/detection/CascadeDetector.cpp index 7030f8f5..602d16b3 100644 --- a/detectors/src/global2D/detection/ODCascadeDetector.cpp +++ b/detectors/src/global2D/detection/CascadeDetector.cpp @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -27,7 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 17.07.15. // -#include "od/detectors/global2D/detection/ODCascadeDetector.h" +#include "od/detectors/global2D/detection/CascadeDetector.h" namespace od { @@ -35,16 +35,16 @@ namespace od namespace g2d { - ODCascadeDetector::ODCascadeDetector(bool gpu, const std::string & trainer, const std::string & trained_data_location, double scale_factor, int min_neighbors, int flags, + CascadeDetector::CascadeDetector(bool gpu, const std::string & trainer, const std::string & trained_data_location, double scale_factor, int min_neighbors, int flags, const cv::Size & min_size, const cv::Size & max_size) { if(gpu){ std::cout << "creating gpu cascade detector" << std::endl; #if WITH_GPU - #ifdef WITH_BOOST_SHARED_PTR - cascade_detector_ = shared_ptr<od::gpu::g2d::ODCascadeDetectorImpl>(new od::gpu::g2d::ODCascadeDetectorImpl(trainer, trained_data_location, scale_factor, min_neighbors, flags, min_size, max_size)); + #ifndef WITH_BOOST_SHARED_PTR + cascade_detector_ = shared_ptr<od::gpu::g2d::CascadeDetectorImpl>(new od::gpu::g2d::CascadeDetectorImpl(trainer, trained_data_location, scale_factor, min_neighbors, flags, min_size, max_size)); #else - cascade_detector_ = make_shared<od::gpu::g2d::ODCascadeDetectorImpl>(trainer, trained_data_location, scale_factor, min_neighbors, flags, min_size, max_size); + cascade_detector_ = make_shared<od::gpu::g2d::CascadeDetectorImpl>(trainer, trained_data_location, scale_factor, min_neighbors, flags, min_size, max_size); #endif #else std::cout << "Error !! GPU CascadeDetector has not been compiled. Recompile with WITH_GPU." << std::endl; @@ -53,70 +53,70 @@ namespace od } else { std::cout << "creating cpu cascade detector" << std::endl; -#ifdef WITH_BOOST_SHARED_PTR - cascade_detector_ = shared_ptr<ODCascadeDetectorImpl>(new ODCascadeDetectorImpl(trainer, trained_data_location, scale_factor, min_neighbors, flags, min_size, max_size)); +#ifndef WITH_BOOST_SHARED_PTR + cascade_detector_ = shared_ptr<CascadeDetectorImpl>(new CascadeDetectorImpl(trainer, trained_data_location, scale_factor, min_neighbors, flags, min_size, max_size)); #else - cascade_detector_ = make_shared<ODCascadeDetectorImpl>(trainer, trained_data_location, scale_factor, min_neighbors, flags, min_size, max_size); + cascade_detector_ = make_shared<CascadeDetectorImpl>(trainer, trained_data_location, scale_factor, min_neighbors, flags, min_size, max_size); #endif } } - shared_ptr<ODDetections> ODCascadeDetector::detectOmni(shared_ptr<ODScene> scene) + shared_ptr<Detections> CascadeDetector::detectOmni(shared_ptr<Scene> scene) { return cascade_detector_->detectOmni(scene); }; - void ODCascadeDetector::init() + void CascadeDetector::init() { cascade_detector_->init(); } - shared_ptr<ODDetections2D> ODCascadeDetector::detectOmni(shared_ptr<ODSceneImage> scene) + shared_ptr<Detections2D> CascadeDetector::detectOmni(shared_ptr<SceneImage> scene) { return cascade_detector_->detectOmni(scene); } - shared_ptr<ODDetections> ODCascadeDetector::detect(shared_ptr<ODSceneImage> scene) + shared_ptr<Detections> CascadeDetector::detect(shared_ptr<SceneImage> scene) { return cascade_detector_->detect(scene); } - void ODCascadeDetector::setScale(const float scale) + void CascadeDetector::setScale(const float scale) { cascade_detector_->setScale(scale); } - float ODCascadeDetector::getScale() const + float CascadeDetector::getScale() const { return cascade_detector_->getScale(); } - void ODCascadeDetector::setMinNeighbors(const unsigned int min_neighbors) + void CascadeDetector::setMinNeighbors(const unsigned int min_neighbors) { cascade_detector_->setMinNeighbors(min_neighbors); } - unsigned int ODCascadeDetector::getMinNeighbors() const + unsigned int CascadeDetector::getMinNeighbors() const { return cascade_detector_->getMinNeighbors(); } - void ODCascadeDetector::setMinSize(const cv::Size & size) + void CascadeDetector::setMinSize(const cv::Size & size) { cascade_detector_->setMinSize(size); } - cv::Size ODCascadeDetector::getMinSize() const + cv::Size CascadeDetector::getMinSize() const { return cascade_detector_->getMinSize(); } - void ODCascadeDetector::setMaxSize(const cv::Size & size) + void CascadeDetector::setMaxSize(const cv::Size & size) { cascade_detector_->setMaxSize(size); } - cv::Size ODCascadeDetector::getMaxSize() const + cv::Size CascadeDetector::getMaxSize() const { return cascade_detector_->getMaxSize(); } diff --git a/detectors/src/global2D/detection/ODCascadeDetectorImpl.cpp b/detectors/src/global2D/detection/CascadeDetectorImpl.cpp similarity index 72% rename from detectors/src/global2D/detection/ODCascadeDetectorImpl.cpp rename to detectors/src/global2D/detection/CascadeDetectorImpl.cpp index 6ae93e40..2961d636 100644 --- a/detectors/src/global2D/detection/ODCascadeDetectorImpl.cpp +++ b/detectors/src/global2D/detection/CascadeDetectorImpl.cpp @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -27,7 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 17.07.15. // -#include "od/detectors/global2D/detection/ODCascadeDetectorImpl.h" +#include "od/detectors/global2D/detection/CascadeDetectorImpl.h" namespace od { @@ -35,7 +35,7 @@ namespace od namespace g2d { - ODCascadeDetectorImpl::ODCascadeDetectorImpl(const std::string & trainer, const std::string & trained_data_location, double scale_factor, int min_neighbors, int flags, + CascadeDetectorImpl::CascadeDetectorImpl(const std::string & trainer, const std::string & trained_data_location, double scale_factor, int min_neighbors, int flags, const cv::Size & min_size, const cv::Size & max_size): trained_data_location_(trained_data_location), scale_factor_(scale_factor), min_neighbors_(min_neighbors), min_size_(min_size), max_size_(max_size) @@ -45,22 +45,22 @@ namespace od meta_info_ = true; } - shared_ptr<ODDetections> ODCascadeDetectorImpl::detectOmni(shared_ptr<ODScene> scene) + shared_ptr<Detections> CascadeDetectorImpl::detectOmni(shared_ptr<Scene> scene) { std::cout << "not implemented, use detect()" <<std::endl; return nullptr; }; - void ODCascadeDetectorImpl::init() + void CascadeDetectorImpl::init() { -#ifdef WITH_BOOST_SHARED_PTR +#ifndef WITH_BOOST_SHARED_PTR haar_cascade_ = shared_ptr<cv::CascadeClassifier>(new cv::CascadeClassifier(trained_data_id_)); #else haar_cascade_ = make_shared<cv::CascadeClassifier>(trained_data_id_); #endif } - shared_ptr<ODDetections2D> ODCascadeDetectorImpl::detectOmni(shared_ptr<ODSceneImage> scene) + shared_ptr<Detections2D> CascadeDetectorImpl::detectOmni(shared_ptr<SceneImage> scene) { if(!haar_cascade_){ @@ -75,12 +75,12 @@ namespace od haar_cascade_->detectMultiScale(gray, objects, scale_factor_, min_neighbors_, 0, min_size_, max_size_); //always create detections - shared_ptr<ODDetections2D> detections = make_shared<ODDetections2D>(); + shared_ptr<Detections2D> detections = make_shared<Detections2D>(); cv::Mat viz = scene->getCVImage(); for(auto & o : objects) { // Process object by object: - shared_ptr<ODDetection2D> detection2D = make_shared<ODDetection2D>(ODDetection::OD_CLASSIFICATION, "OBJ", 1); + shared_ptr<Detection2D> detection2D = make_shared<Detection2D>(Detection::CLASSIFICATION, "OBJ", 1); detection2D->setBoundingBox(o); detections->push_back(detection2D); @@ -94,7 +94,7 @@ namespace od return detections; } - shared_ptr<ODDetections> ODCascadeDetectorImpl::detect(shared_ptr<ODSceneImage> scene) + shared_ptr<Detections> CascadeDetectorImpl::detect(shared_ptr<SceneImage> scene) { if(!haar_cascade_){ @@ -103,7 +103,7 @@ namespace od } //always create detections - shared_ptr<ODDetections> detections = make_shared<ODDetections>(); + shared_ptr<Detections> detections = make_shared<Detections>(); cv::Mat gray; cv::cvtColor(scene->getCVImage(), gray, CV_BGR2GRAY); @@ -116,47 +116,47 @@ namespace od haar_cascade_->detectMultiScale(gray, objects, 5, min_neighbors_, 0, gray.size(), gray.size()); if(objects.size() > 0) { - detections->push_back(make_shared<ODDetection>(ODDetection::OD_CLASSIFICATION, "OBJ", 1)); + detections->push_back(make_shared<Detection>(Detection::CLASSIFICATION, "OBJ", 1)); } return detections; } - void ODCascadeDetectorImpl::setScale(const float scale) + void CascadeDetectorImpl::setScale(const float scale) { scale_factor_ = scale; } - float ODCascadeDetectorImpl::getScale() const + float CascadeDetectorImpl::getScale() const { return scale_factor_; } - void ODCascadeDetectorImpl::setMinNeighbors(const unsigned int min_neighbors) + void CascadeDetectorImpl::setMinNeighbors(const unsigned int min_neighbors) { min_neighbors_ = min_neighbors; } - unsigned int ODCascadeDetectorImpl::getMinNeighbors() const + unsigned int CascadeDetectorImpl::getMinNeighbors() const { return min_neighbors_; } - void ODCascadeDetectorImpl::setMinSize(const cv::Size & size) + void CascadeDetectorImpl::setMinSize(const cv::Size & size) { min_size_ = size; } - cv::Size ODCascadeDetectorImpl::getMinSize() const + cv::Size CascadeDetectorImpl::getMinSize() const { return min_size_; } - void ODCascadeDetectorImpl::setMaxSize(const cv::Size & size) + void CascadeDetectorImpl::setMaxSize(const cv::Size & size) { max_size_ = size; } - cv::Size ODCascadeDetectorImpl::getMaxSize() const + cv::Size CascadeDetectorImpl::getMaxSize() const { return max_size_; } diff --git a/detectors/src/global2D/detection/ODHOGDetector.cpp b/detectors/src/global2D/detection/HOGDetector.cpp similarity index 71% rename from detectors/src/global2D/detection/ODHOGDetector.cpp rename to detectors/src/global2D/detection/HOGDetector.cpp index ac3da854..860eb00e 100644 --- a/detectors/src/global2D/detection/ODHOGDetector.cpp +++ b/detectors/src/global2D/detection/HOGDetector.cpp @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -27,46 +27,46 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 15.07.15. // -#include "od/detectors/global2D/detection/ODHOGDetector.h" +#include "od/detectors/global2D/detection/HOGDetector.h" namespace od { namespace g2d { - ODHOGDetector::ODHOGDetector(const std::string & trained_data_location_, const cv::Size & win_size, const cv::Size & block_size, + HOGDetector::HOGDetector(const std::string & trained_data_location_, const cv::Size & win_size, const cv::Size & block_size, const cv::Size & block_stride, const cv::Size & cell_size, float hit_threshold): - ODDetector2D(trained_data_location_), win_size_(win_size), block_size_(block_size), block_stride_(block_stride), + Detector2D(trained_data_location_), win_size_(win_size), block_size_(block_size), block_stride_(block_stride), cell_size_(cell_size), hit_threshold_(hit_threshold), hog_(win_size, block_size, block_stride, cell_size, 9, 1, -1, cv::HOGDescriptor::L2Hys, 0.2, false, cv::HOGDescriptor::DEFAULT_NLEVELS) { trained_location_identifier_ = std::string("HOG"); trained_data_id_ = std::string("hog.xml"); meta_info_ = true; - svm_type_ = OD_DEFAULT_PEOPLE; + svm_type_ = _DEFAULT_PEOPLE; if(trained_data_location_ != std::string("")) - svm_type_ = OD_FILE; + svm_type_ = _FILE; } - void ODHOGDetector::init() + void HOGDetector::init() { switch(svm_type_) { - case OD_DEFAULT_PEOPLE: + case _DEFAULT_PEOPLE: hog_.setSVMDetector(cv::HOGDescriptor::getDefaultPeopleDetector()); std::cout << "HOG TYPE: OpenCV Default People" << std::endl; //hog_.save(getSpecificTrainingDataLocation() + "/defaultpeople." + TRAINED_DATA_EXT_); break; - case OD_DAIMLER_PEOPLE: + case _DAIMLER_PEOPLE: hog_.winSize = cv::Size(48, 96); hit_threshold_ = 1.2; hog_.setSVMDetector(cv::HOGDescriptor::getDaimlerPeopleDetector()); std::cout << "HOG TYPE: OpenCV Daimler People" << std::endl; //hog_.save(getSpecificTrainingDataLocation() + "/daimlerpeople." + TRAINED_DATA_EXT_); break; - case OD_FILE: + case _FILE: std::string hogfile = fileutils::getFirstFile(getSpecificTrainingDataLocation(), trained_data_id_); load(hogfile); std::cout << "HOG TYPE: Custom HOG features loaded from: " << hogfile << std::endl; @@ -77,7 +77,7 @@ namespace od printParameters(); } - void ODHOGDetector::load(const std::string & file_name) + void HOGDetector::load(const std::string & file_name) { cv::FileStorage fs(file_name, cv::FileStorage::READ); fs["hitThreshold"] >> hit_threshold_; @@ -85,15 +85,15 @@ namespace od hog_.read(fn); } - void ODHOGDetector::setSVMDetector(std::vector<float> svm_detector) + void HOGDetector::setSVMDetector(std::vector<float> svm_detector) { hog_.setSVMDetector(svm_detector); } - shared_ptr<ODDetections2D> ODHOGDetector::detectOmni(shared_ptr<ODSceneImage> scene) + shared_ptr<Detections2D> HOGDetector::detectOmni(shared_ptr<SceneImage> scene) { //always create a detection - shared_ptr<ODDetections2D> detections = make_shared<ODDetections2D>(); + shared_ptr<Detections2D> detections = make_shared<Detections2D>(); std::vector<cv::Rect> found; hog_.detectMultiScale(scene->getCVImage(), found, hit_threshold_, cv::Size(8, 8), cv::Size(32, 32), 1.05, 2); @@ -101,10 +101,10 @@ namespace od cv::Mat viz = scene->getCVImage().clone(); for(size_t i = 0; i < found.size(); ++i) { - shared_ptr<ODDetection2D> detection2D = make_shared<ODDetection2D>(); + shared_ptr<Detection2D> detection2D = make_shared<Detection2D>(); detection2D->setBoundingBox(found[i]); detection2D->setId("PEOPLE"); - detection2D->setType(ODDetection::OD_CLASSIFICATION); + detection2D->setType(od::Detection::CLASSIFICATION); detections->push_back(detection2D); if(meta_info_) @@ -122,10 +122,10 @@ namespace od return detections; } - shared_ptr<ODDetections> ODHOGDetector::detect(shared_ptr<ODSceneImage> scene) + shared_ptr<Detections> HOGDetector::detect(shared_ptr<SceneImage> scene) { //always create a detection - shared_ptr<ODDetections> detections = make_shared<ODDetections>(); + shared_ptr<Detections> detections = make_shared<Detections>(); cv::Mat scaled_window; cv::resize(scene->getCVImage(), scaled_window, hog_.winSize); @@ -135,82 +135,82 @@ namespace od hog_.detect(scene->getCVImage(), found_locations, hit_threshold_); if(!found_locations.empty()) { - shared_ptr<ODDetection2D> detection2D = make_shared<ODDetection2D>(); + shared_ptr<Detection2D> detection2D = make_shared<Detection2D>(); detection2D->setId("PEOPLE"); - detection2D->setType(ODDetection::OD_CLASSIFICATION); + detection2D->setType(od::Detection::CLASSIFICATION); detections->push_back(detection2D); } return detections; } - void ODHOGDetector::setTrainedDataLocation(const std::string & trained_data_location) + void HOGDetector::setTrainedDataLocation(const std::string & trained_data_location) { trained_data_location_ = trained_data_location; - svm_type_ = OD_FILE; + svm_type_ = _FILE; } - const ODHOGDetector::SVMType & ODHOGDetector::getSvmtype() const + const HOGDetector::SVMType & HOGDetector::getSvmtype() const { return svm_type_; } - void ODHOGDetector::setSvmtype(const ODHOGDetector::SVMType & svm_type) + void HOGDetector::setSvmtype(const HOGDetector::SVMType & svm_type) { svm_type_ = svm_type; } - const cv::Size & ODHOGDetector::getWinSize() const + const cv::Size & HOGDetector::getWinSize() const { return win_size_; } - void ODHOGDetector::setWinSize(const cv::Size & win_size) + void HOGDetector::setWinSize(const cv::Size & win_size) { win_size_ = win_size; } - const cv::Size & ODHOGDetector::getBlockSize() const + const cv::Size & HOGDetector::getBlockSize() const { return block_size_; } - void ODHOGDetector::setBlockSize(const cv::Size & block_size) + void HOGDetector::setBlockSize(const cv::Size & block_size) { block_size_ = block_size; } - const cv::Size & ODHOGDetector::getBlockStride() const + const cv::Size & HOGDetector::getBlockStride() const { return block_stride_; } - void ODHOGDetector::setBlockStride(const cv::Size & block_stride) + void HOGDetector::setBlockStride(const cv::Size & block_stride) { block_stride_ = block_stride; } - const cv::Size & ODHOGDetector::getCellSize() const + const cv::Size & HOGDetector::getCellSize() const { return cell_size_; } - void ODHOGDetector::setCellSize(const cv::Size & cell_size) + void HOGDetector::setCellSize(const cv::Size & cell_size) { cell_size_ = cell_size; } - float ODHOGDetector::getHitThreshold() const + float HOGDetector::getHitThreshold() const { return hit_threshold_; } - void ODHOGDetector::setHitThreshold(float hit_threshold) + void HOGDetector::setHitThreshold(float hit_threshold) { hit_threshold_ = hit_threshold; } - void ODHOGDetector::setSVMFromFile(const std::string & file_name) + void HOGDetector::setSVMFromFile(const std::string & file_name) { std::vector<float> descriptor_vector; std::cout << "Reading descriptor vector from file " << file_name << std::endl;; @@ -231,7 +231,7 @@ namespace od hog_.setSVMDetector(descriptor_vector); } - void ODHOGDetector::printParameters() + void HOGDetector::printParameters() { std::cout << "winSize: " << win_size_ << std::endl; std::cout << "blockSize: " << block_size_ << std::endl; @@ -240,7 +240,7 @@ namespace od std::cout << "hitThreshold: " << hit_threshold_ << std::endl; } - shared_ptr<ODDetections> ODHOGDetector::detectOmni(shared_ptr<ODScene> scene) + shared_ptr<Detections> HOGDetector::detectOmni(shared_ptr<Scene> scene) { return nullptr; } diff --git a/detectors/src/global2D/training/ODHOGTrainer.cpp b/detectors/src/global2D/training/HOGTrainer.cpp similarity index 84% rename from detectors/src/global2D/training/ODHOGTrainer.cpp rename to detectors/src/global2D/training/HOGTrainer.cpp index c1bb848f..65294c6a 100644 --- a/detectors/src/global2D/training/ODHOGTrainer.cpp +++ b/detectors/src/global2D/training/HOGTrainer.cpp @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -28,16 +28,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -#include "od/detectors/global2D/training/ODHOGTrainer.h" +#include "od/detectors/global2D/training/HOGTrainer.h" namespace od { namespace g2d { - ODHOGTrainer::ODHOGTrainer(const std::string & training_input_location_, const std::string & trained_data_location_, const cv::Size & win_size, + HOGTrainer::HOGTrainer(const std::string & training_input_location_, const std::string & trained_data_location_, const cv::Size & win_size, const cv::Size & block_size, const cv::Size & block_stride, const cv::Size & cell_size, float hit_threshold): - ODTrainer(training_input_location_, trained_data_location_), win_size_(win_size), block_size_(block_size), + Trainer(training_input_location_, trained_data_location_), win_size_(win_size), block_size_(block_size), block_stride_(block_stride), cell_size_(cell_size), hog_(win_size, block_size, block_stride, cell_size, 9) { @@ -68,7 +68,7 @@ namespace od } - void ODHOGTrainer::saveDescriptorVectorToFile(const std::vector<float> & descriptor_vector, const std::string & file_name) + void HOGTrainer::saveDescriptorVectorToFile(const std::vector<float> & descriptor_vector, const std::string & file_name) { std::cout << "Saving descriptor vector to file " << file_name << std::endl; std::fstream file; @@ -95,7 +95,7 @@ namespace od } } - void ODHOGTrainer::calculateFeaturesFromInput(const std::string & image_file_name, std::vector<float> & feature_vector, cv::HOGDescriptor & hog) + void HOGTrainer::calculateFeaturesFromInput(const std::string & image_file_name, std::vector<float> & feature_vector, cv::HOGDescriptor & hog) { cv::Mat image_data_orig = cv::imread(image_file_name, 0); @@ -114,7 +114,7 @@ namespace od //cout << ": Expected size :" << hog.getDescriptorSize() << endl; } - void ODHOGTrainer::detectTrainingSetTest(const cv::HOGDescriptor & hog, double hit_threshold, const std::vector<std::string> & pos_file_names, + void HOGTrainer::detectTrainingSetTest(const cv::HOGDescriptor & hog, double hit_threshold, const std::vector<std::string> & pos_file_names, const std::vector<std::string> & neg_file_names) { unsigned int true_positives = 0; @@ -157,7 +157,7 @@ namespace od } - void ODHOGTrainer::calculateFeaturesFromImageLoc(const cv::Mat & image_data, std::vector<float> & feature_vector, const cv::HOGDescriptor & hog, + void HOGTrainer::calculateFeaturesFromImageLoc(const cv::Mat & image_data, std::vector<float> & feature_vector, const cv::HOGDescriptor & hog, const cv::Point & start_pos) { @@ -166,7 +166,7 @@ namespace od hog.compute(image_data, feature_vector, win_stride_, training_padding_, locations); } - void ODHOGTrainer::handleNegetivefile(const std::string & image_file_name, cv::HOGDescriptor & hog, std::fstream & file) + void HOGTrainer::handleNegetivefile(const std::string & image_file_name, cv::HOGDescriptor & hog, std::fstream & file) { cv::Mat image_data = cv::imread(image_file_name, 0); @@ -200,7 +200,7 @@ namespace od } - void ODHOGTrainer::createHardTrainingData(const cv::HOGDescriptor & hog, double hit_threshold, const std::vector<std::string> & neg_file_names) + void HOGTrainer::createHardTrainingData(const cv::HOGDescriptor & hog, double hit_threshold, const std::vector<std::string> & neg_file_names) { std::fstream file; file.open(features_file_.c_str(), std::fstream::app); @@ -252,7 +252,7 @@ namespace od file.close(); } - double ODHOGTrainer::trainWithSVMLight(const std::string & svm_model_file, const std::string & svm_descriptor_file, + double HOGTrainer::trainWithSVMLight(const std::string & svm_model_file, const std::string & svm_descriptor_file, std::vector<float> & descriptor_vector) { SVMlight * svmlight= SVMlight::getInstance(); @@ -275,7 +275,7 @@ namespace od return hit_threshold_; } - int ODHOGTrainer::train() + int HOGTrainer::train() { std::vector<std::string> positive_training_images; @@ -382,7 +382,7 @@ namespace od } - void ODHOGTrainer::readDescriptorsFromFile(const std::string & file_name, std::vector<float> & descriptor_vector) + void HOGTrainer::readDescriptorsFromFile(const std::string & file_name, std::vector<float> & descriptor_vector) { std::cout << "Reading descriptor vector from file " << file_name << std::endl; @@ -399,114 +399,114 @@ namespace od } } - void ODHOGTrainer::save(const std::string & file_name) + void HOGTrainer::save(const std::string & file_name) { cv::FileStorage fs(file_name, cv::FileStorage::WRITE); fs << "hitThreshold" << hit_threshold_; hog_.write(fs, cv::FileStorage::getDefaultObjectName(file_name)); } - const std::string & ODHOGTrainer::getPosSamplesDir() const + const std::string & HOGTrainer::getPosSamplesDir() const { return pos_samples_dir_; } - void ODHOGTrainer::setPosSamplesDir(const std::string & pos_samples_dir) + void HOGTrainer::setPosSamplesDir(const std::string & pos_samples_dir) { pos_samples_dir_ = pos_samples_dir; } - const std::string & ODHOGTrainer::getNegSamplesDir() const + const std::string & HOGTrainer::getNegSamplesDir() const { return neg_samples_dir_; } - void ODHOGTrainer::setNegSamplesDir(const std::string & neg_samples_dir) + void HOGTrainer::setNegSamplesDir(const std::string & neg_samples_dir) { neg_samples_dir_ = neg_samples_dir; } - int ODHOGTrainer::getNOFeaturesNeg() const + int HOGTrainer::getNOFeaturesNeg() const { return no_features_neg_; } - void ODHOGTrainer::setNOFeaturesNeg(int featno) + void HOGTrainer::setNOFeaturesNeg(int featno) { no_features_neg_ = featno; } - const cv::Point & ODHOGTrainer::getStartHogPos() const + const cv::Point & HOGTrainer::getStartHogPos() const { return start_hog_pos_; } - void ODHOGTrainer::setStartHogPos(const cv::Point & start_hog_pos) + void HOGTrainer::setStartHogPos(const cv::Point & start_hog_pos) { start_hog_pos_ = start_hog_pos; } - const cv::Size & ODHOGTrainer::getWinSize() const + const cv::Size & HOGTrainer::getWinSize() const { return win_size_; } - void ODHOGTrainer::setWinSize(const cv::Size & win_size) + void HOGTrainer::setWinSize(const cv::Size & win_size) { win_size_ = win_size; } - const cv::Size & ODHOGTrainer::getBlockSize() const + const cv::Size & HOGTrainer::getBlockSize() const { return block_size_; } - void ODHOGTrainer::setBlockSize(const cv::Size & block_size) + void HOGTrainer::setBlockSize(const cv::Size & block_size) { block_size_ = block_size; } - const cv::Size & ODHOGTrainer::getBlockStride() const + const cv::Size & HOGTrainer::getBlockStride() const { return block_stride_; } - void ODHOGTrainer::setBlockStride(const cv::Size & block_stride) + void HOGTrainer::setBlockStride(const cv::Size & block_stride) { block_stride_ = block_stride; } - const cv::Size & ODHOGTrainer::getCellSize() const + const cv::Size & HOGTrainer::getCellSize() const { return cell_size_; } - void ODHOGTrainer::setCellSize(const cv::Size & cell_size) + void HOGTrainer::setCellSize(const cv::Size & cell_size) { cell_size_ = cell_size; } - const cv::Size & ODHOGTrainer::getTrainingPadding() const + const cv::Size & HOGTrainer::getTrainingPadding() const { return training_padding_; } - void ODHOGTrainer::setTrainingPadding(const cv::Size & training_padding) + void HOGTrainer::setTrainingPadding(const cv::Size & training_padding) { training_padding_ = training_padding; } - bool ODHOGTrainer::isTrainHardNegetive() const + bool HOGTrainer::isTrainHardNegetive() const { return train_hard_negative_; } - void ODHOGTrainer::setTrainHardNegetive(bool train_hard_negative) + void HOGTrainer::setTrainHardNegetive(bool train_hard_negative) { train_hard_negative_ = train_hard_negative; } - double ODHOGTrainer::getHitThreshold() const + double HOGTrainer::getHitThreshold() const { return hit_threshold_; } diff --git a/detectors/src/global3D/CMakeLists.txt b/detectors/src/global3D/CMakeLists.txt index 9322d82c..7f7bc98a 100644 --- a/detectors/src/global3D/CMakeLists.txt +++ b/detectors/src/global3D/CMakeLists.txt @@ -10,15 +10,16 @@ if(BUILD_GLOBAL_3D_DETECTION) endif() set(SOURCES - "ODPointCloudGlobalMatching.cpp" - "training/ODCADDetectTrainer3DGlobal.cpp" - "detection/ODCADDetector3DGlobal.cpp" + "PointCloudGlobalMatching.cpp" + "training/CADDetectTrainer3DGlobal.cpp" + "detection/CADDetector3DGlobal.cpp" ) include_directories(${OpenCV_INCLUDE_DIRS}) include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${DETECTORS_IMPL_DIR}) include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) include_directories(${PCL_INCLUDE_DIRS}) OD_ADD_LIBRARY("${SUBSYS_NAME}" SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) diff --git a/detectors/src/global3D/ODPointCloudGlobalMatching.cpp b/detectors/src/global3D/PointCloudGlobalMatching.cpp similarity index 84% rename from detectors/src/global3D/ODPointCloudGlobalMatching.cpp rename to detectors/src/global3D/PointCloudGlobalMatching.cpp index aa796769..e53ff289 100644 --- a/detectors/src/global3D/ODPointCloudGlobalMatching.cpp +++ b/detectors/src/global3D/PointCloudGlobalMatching.cpp @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -27,18 +27,18 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 16.06.15. // -#include "od/detectors/global3D/ODPointCloudGlobalMatching.h" +#include "od/detectors/global3D/PointCloudGlobalMatching.h" namespace od { namespace g3d { - int ODPointCloudGlobalMatching::train() + int PointCloudGlobalMatching::train() { return trainer_->train(); } - int ODPointCloudGlobalMatching::detect(shared_ptr<ODScene> scene, std::vector<shared_ptr<ODDetection>> & detections) + int PointCloudGlobalMatching::detect(shared_ptr<Scene> scene, std::vector<shared_ptr<Detection>> & detections) { //detector_->detect(scene, detections); return 0; diff --git a/detectors/src/global3D/detection/CADDetector3DGlobal.cpp b/detectors/src/global3D/detection/CADDetector3DGlobal.cpp new file mode 100644 index 00000000..525240d1 --- /dev/null +++ b/detectors/src/global3D/detection/CADDetector3DGlobal.cpp @@ -0,0 +1,59 @@ +#include "od/detectors/global3D/detection/CADDetector3DGlobal.hpp" +#include "od/common/pipeline/Detection.h" + +namespace od { + + namespace g3d { + + template + shared_ptr<Detections> CADDetector3DGlobal<pcl::PointXYZ>::detect(shared_ptr<Scene> scene); + + template + shared_ptr<Detections> CADDetector3DGlobal<pcl::PointXYZ>::detectOmni(shared_ptr<Scene> scene); + + template + void CADDetector3DGlobal<pcl::PointXYZ>::init(); + + template + shared_ptr<Detections3D> CADDetector3DGlobal<pcl::PointXYZ>::detectOmni(shared_ptr<ScenePointCloud<pcl::PointXYZ> > scene); + + template + shared_ptr<Detections> CADDetector3DGlobal<pcl::PointXYZ>::detect(shared_ptr<ScenePointCloud<pcl::PointXYZ> > scene); + + + + template + shared_ptr<Detections> CADDetector3DGlobal<pcl::PointXYZRGB>::detect(shared_ptr<Scene> scene); + + template + shared_ptr<Detections> CADDetector3DGlobal<pcl::PointXYZRGB>::detectOmni(shared_ptr<Scene> scene); + + template + void CADDetector3DGlobal<pcl::PointXYZRGB>::init(); + + template + shared_ptr<Detections3D> CADDetector3DGlobal<pcl::PointXYZRGB>::detectOmni(shared_ptr<ScenePointCloud<pcl::PointXYZRGB> > scene); + + template + shared_ptr<Detections> CADDetector3DGlobal<pcl::PointXYZRGB>::detect(shared_ptr<ScenePointCloud<pcl::PointXYZRGB> > scene); + + + + template + shared_ptr<Detections> CADDetector3DGlobal<pcl::PointXYZRGBA>::detect(shared_ptr<Scene> scene); + + template + shared_ptr<Detections> CADDetector3DGlobal<pcl::PointXYZRGBA>::detectOmni(shared_ptr<Scene> scene); + + template + void CADDetector3DGlobal<pcl::PointXYZRGBA>::init(); + + template + shared_ptr<Detections3D> CADDetector3DGlobal<pcl::PointXYZRGBA>::detectOmni(shared_ptr<ScenePointCloud<pcl::PointXYZRGBA> > scene); + + template + shared_ptr<Detections> CADDetector3DGlobal<pcl::PointXYZRGBA>::detect(shared_ptr<ScenePointCloud<pcl::PointXYZRGBA> > scene); + + } + +} \ No newline at end of file diff --git a/detectors/src/global3D/detection/ODCADDetector3DGlobal.cpp b/detectors/src/global3D/detection/ODCADDetector3DGlobal.cpp deleted file mode 100644 index a7e07984..00000000 --- a/detectors/src/global3D/detection/ODCADDetector3DGlobal.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "od/detectors/global3D/detection/ODCADDetector3DGlobal.hpp" -#include "od/common/pipeline/ODDetection.h" - -namespace od { - - namespace g3d { - - template - shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZ>::detect(shared_ptr<ODScene> scene); - - template - shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZ>::detectOmni(shared_ptr<ODScene> scene); - - template - void ODCADDetector3DGlobal<pcl::PointXYZ>::init(); - - template - shared_ptr<ODDetections3D> ODCADDetector3DGlobal<pcl::PointXYZ>::detectOmni(shared_ptr<ODScenePointCloud<pcl::PointXYZ> > scene); - - template - shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZ>::detect(shared_ptr<ODScenePointCloud<pcl::PointXYZ> > scene); - - - - template - shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZRGB>::detect(shared_ptr<ODScene> scene); - - template - shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZRGB>::detectOmni(shared_ptr<ODScene> scene); - - template - void ODCADDetector3DGlobal<pcl::PointXYZRGB>::init(); - - template - shared_ptr<ODDetections3D> ODCADDetector3DGlobal<pcl::PointXYZRGB>::detectOmni(shared_ptr<ODScenePointCloud<pcl::PointXYZRGB> > scene); - - template - shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZRGB>::detect(shared_ptr<ODScenePointCloud<pcl::PointXYZRGB> > scene); - - - - template - shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZRGBA>::detect(shared_ptr<ODScene> scene); - - template - shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZRGBA>::detectOmni(shared_ptr<ODScene> scene); - - template - void ODCADDetector3DGlobal<pcl::PointXYZRGBA>::init(); - - template - shared_ptr<ODDetections3D> ODCADDetector3DGlobal<pcl::PointXYZRGBA>::detectOmni(shared_ptr<ODScenePointCloud<pcl::PointXYZRGBA> > scene); - - template - shared_ptr<ODDetections> ODCADDetector3DGlobal<pcl::PointXYZRGBA>::detect(shared_ptr<ODScenePointCloud<pcl::PointXYZRGBA> > scene); - - } - -} \ No newline at end of file diff --git a/detectors/src/global3D/training/ODCADDetectTrainer3DGlobal.cpp b/detectors/src/global3D/training/CADDetectTrainer3DGlobal.cpp similarity index 78% rename from detectors/src/global3D/training/ODCADDetectTrainer3DGlobal.cpp rename to detectors/src/global3D/training/CADDetectTrainer3DGlobal.cpp index 215dea6d..d03634b8 100644 --- a/detectors/src/global3D/training/ODCADDetectTrainer3DGlobal.cpp +++ b/detectors/src/global3D/training/CADDetectTrainer3DGlobal.cpp @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -27,21 +27,21 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 16.06.15. // -#include "od/detectors/global3D/training/ODCADDetectTrainer3DGlobal.h" +#include "od/detectors/global3D/training/CADDetectTrainer3DGlobal.h" namespace od { namespace g3d { - ODCADDetectTrainer3DGlobal::ODCADDetectTrainer3DGlobal(const std::string & training_input_location_, const std::string & training_data_location_): - ODTrainer(training_input_location_, training_data_location_) + CADDetectTrainer3DGlobal::CADDetectTrainer3DGlobal(const std::string & training_input_location_, const std::string & training_data_location_): + Trainer(training_input_location_, training_data_location_) { desc_name_ = std::string("esf"); trained_location_identifier_ = std::string("GLOBAL3DVFH"); } - int ODCADDetectTrainer3DGlobal::train() + int CADDetectTrainer3DGlobal::train() { shared_ptr<pcl::rec_3d_framework::MeshSource<pcl::PointXYZ> > mesh_source(new pcl::rec_3d_framework::MeshSource<pcl::PointXYZ>()); @@ -57,12 +57,12 @@ namespace od { return 1; } - const std::string & ODCADDetectTrainer3DGlobal::getDescName() const + const std::string & CADDetectTrainer3DGlobal::getDescName() const { return desc_name_; } - void ODCADDetectTrainer3DGlobal::setDescName(const std::string & desc_name) + void CADDetectTrainer3DGlobal::setDescName(const std::string & desc_name) { desc_name_ = desc_name; } diff --git a/detectors/src/local2D/CMakeLists.txt b/detectors/src/local2D/CMakeLists.txt index d3ac8e16..fe1d30e0 100644 --- a/detectors/src/local2D/CMakeLists.txt +++ b/detectors/src/local2D/CMakeLists.txt @@ -6,23 +6,24 @@ set(SUBSYS_DEPS od_common pugixml) if(BUILD_LOCAL_2D_DETECTION) set(SOURCES - "ODImageLocalMatching.cpp" - "training/ODCADRecogTrainerSnapshotBased.cpp" - "detection/ODCADRecognizer2DLocal.cpp" - "simple_ransac_detection/ODCsvReader.cpp" - "simple_ransac_detection/ODCsvWriter.cpp" - "simple_ransac_detection/ODModelRegistration.cpp" - "simple_ransac_detection/ODMesh.cpp" - "simple_ransac_detection/ODModel.cpp" - "simple_ransac_detection/ODPnPProblem.cpp" - "simple_ransac_detection/ODUtils.cpp" - "simple_ransac_detection/ODRobustMatcher.cpp" + "ImageLocalMatching.cpp" + "training/CADRecogTrainerSnapshotBased.cpp" + "detection/CADRecognizer2DLocal.cpp" + "simple_ransac_detection/CsvReader.cpp" + "simple_ransac_detection/CsvWriter.cpp" + "simple_ransac_detection/ModelRegistration.cpp" + "simple_ransac_detection/Mesh.cpp" + "simple_ransac_detection/Model.cpp" + "simple_ransac_detection/PnPProblem.cpp" + "simple_ransac_detection/Utils.cpp" + "simple_ransac_detection/RobustMatcher.cpp" ) include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${CMAKE_3RDPARTY_DIR}/pugixml/src/) include_directories(${OpenCV_INCLUDE_DIRS}) include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) include_directories(${PCL_INCLUDE_DIRS}) OD_ADD_LIBRARY("${SUBSYS_NAME}" SRCS ${SOURCES} LINK_WITH ${SUBSYS_DEPS}) diff --git a/detectors/src/local2D/ODImageLocalMatching.cpp b/detectors/src/local2D/ImageLocalMatching.cpp similarity index 67% rename from detectors/src/local2D/ODImageLocalMatching.cpp rename to detectors/src/local2D/ImageLocalMatching.cpp index 1c477d8a..545f81f3 100644 --- a/detectors/src/local2D/ODImageLocalMatching.cpp +++ b/detectors/src/local2D/ImageLocalMatching.cpp @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -28,7 +28,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 05.06.15. // -#include "od/detectors/local2D/ODImageLocalMatching.h" +#include "od/detectors/local2D/ImageLocalMatching.h" namespace od { @@ -36,52 +36,52 @@ namespace od namespace l2d { - ODImageLocalMatchingTrainer::ODImageLocalMatchingTrainer(const std::string & training_input_location, + ImageLocalMatchingTrainer::ImageLocalMatchingTrainer(const std::string & training_input_location, const std::string & training_data_location) : - ODTrainer(training_input_location, training_data_location) + Trainer(training_input_location, training_data_location) { trained_location_identifier_ = std::string("FEATCORR"); trained_data_id_ = std::string("corr.xml"); } - ODImageLocalMatchingDetector::ODImageLocalMatchingDetector(const std::string & training_data_location) : - ODDetector2DComplete(training_data_location) + ImageLocalMatchingDetector::ImageLocalMatchingDetector(const std::string & training_data_location) : + Detector2DComplete(training_data_location) { trained_location_identifier_ = std::string("FEATCORR"); trained_data_id_ = std::string(".xml"); } - shared_ptr<ODImageLocalMatchingTrainer> ODImageLocalMatching::getTrainer() const + shared_ptr<ImageLocalMatchingTrainer> ImageLocalMatching::getTrainer() const { return trainer_; } - void ODImageLocalMatching::setTrainer(shared_ptr<ODImageLocalMatchingTrainer> trainer) + void ImageLocalMatching::setTrainer(shared_ptr<ImageLocalMatchingTrainer> trainer) { trainer_ = trainer; } - shared_ptr<ODImageLocalMatchingDetector> ODImageLocalMatching::getDetector() const + shared_ptr<ImageLocalMatchingDetector> ImageLocalMatching::getDetector() const { return detector_; } - void ODImageLocalMatching:: setDetector(shared_ptr<ODImageLocalMatchingDetector> detector) + void ImageLocalMatching:: setDetector(shared_ptr<ImageLocalMatchingDetector> detector) { detector_ = detector; } - ODImageLocalMatching::ODImageLocalMatching() + ImageLocalMatching::ImageLocalMatching() { trained_data_ext_ = std::string("corr.xml"); } - int ODImageLocalMatching::train() + int ImageLocalMatching::train() { return trainer_->train(); } - int ODImageLocalMatching::detect(shared_ptr<ODScene> scene, const std::vector<shared_ptr<ODDetection> > & detections) + int ImageLocalMatching::detect(shared_ptr<Scene> scene, const std::vector<shared_ptr<Detection> > & detections) { //detector_->detect(scene, detections); return 1; diff --git a/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp b/detectors/src/local2D/detection/CADRecognizer2DLocal.cpp similarity index 75% rename from detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp rename to detectors/src/local2D/detection/CADRecognizer2DLocal.cpp index 204bd5e3..bbe75f0b 100644 --- a/detectors/src/local2D/detection/ODCADRecognizer2DLocal.cpp +++ b/detectors/src/local2D/detection/CADRecognizer2DLocal.cpp @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -28,15 +28,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 08.06.15. // -#include "od/detectors/local2D/detection/ODCADRecognizer2DLocal.h" +#include "od/detectors/local2D/detection/CADRecognizer2DLocal.h" namespace od { namespace l2d { - ODCADRecognizer2DLocal::ODCADRecognizer2DLocal(const std::string & trained_data_location): - ODImageLocalMatchingDetector(trained_data_location) + CADRecognizer2DLocal::CADRecognizer2DLocal(const std::string & trained_data_location): + ImageLocalMatchingDetector(trained_data_location) { meta_info_ = true; @@ -62,135 +62,135 @@ namespace od pnp_method_ = cv::SOLVEPNP_EPNP; f_type_default_ = SIFT; -#ifdef WITH_BOOST_SHARED_PTR - feature_detector_ = shared_ptr<ODFeatureDetector>(new ODFeatureDetector(f_type_default_)); +#ifndef WITH_BOOST_SHARED_PTR + feature_detector_ = shared_ptr<FeatureDetector>(new FeatureDetector(f_type_default_)); #else - feature_detector_ = make_shared<ODFeatureDetector>(f_type_default_); + feature_detector_ = make_shared<FeatureDetector>(f_type_default_); #endif } - const std::string & ODCADRecognizer2DLocal::getCameraIntrinsicFile() const + const std::string & CADRecognizer2DLocal::getCameraIntrinsicFile() const { return camera_intrinsic_file_; } - void ODCADRecognizer2DLocal::setCameraIntrinsicFile(const std::string & camera_intrinsic_file) + void CADRecognizer2DLocal::setCameraIntrinsicFile(const std::string & camera_intrinsic_file) { camera_intrinsic_file_ = camera_intrinsic_file; } - int ODCADRecognizer2DLocal::getNumKeyPoints() const + int CADRecognizer2DLocal::getNumKeyPoints() const { return num_key_points_; } - void ODCADRecognizer2DLocal::setNumKeyPoints(int num_key_points) + void CADRecognizer2DLocal::setNumKeyPoints(int num_key_points) { num_key_points_ = num_key_points; } - float ODCADRecognizer2DLocal::getRatioTest() const + float CADRecognizer2DLocal::getRatioTest() const { return ratio_test_; } - void ODCADRecognizer2DLocal::setRatioTest(float ratio_test) + void CADRecognizer2DLocal::setRatioTest(float ratio_test) { ratio_test_ = ratio_test; } - bool ODCADRecognizer2DLocal::isFastMatch() const + bool CADRecognizer2DLocal::isFastMatch() const { return fast_match_; } - void ODCADRecognizer2DLocal::setFastMatch(bool fast_match) + void CADRecognizer2DLocal::setFastMatch(bool fast_match) { fast_match_ = fast_match; } - bool ODCADRecognizer2DLocal::isUseGpu() const + bool CADRecognizer2DLocal::isUseGpu() const { return use_gpu_; } - void ODCADRecognizer2DLocal::setUseGpu(bool use_gpu) + void CADRecognizer2DLocal::setUseGpu(bool use_gpu) { use_gpu_ = use_gpu; } - bool ODCADRecognizer2DLocal::isUseGpuMatch() const + bool CADRecognizer2DLocal::isUseGpuMatch() const { return use_gpu_match_; } - void ODCADRecognizer2DLocal::setUseGpuMatch(bool use_gpu_match) + void CADRecognizer2DLocal::setUseGpuMatch(bool use_gpu_match) { use_gpu_match_ = use_gpu_match; } - bool ODCADRecognizer2DLocal::isMetainfo() const + bool CADRecognizer2DLocal::isMetainfo() const { return meta_info_; } - void ODCADRecognizer2DLocal::setMetainfo(bool meta_info) + void CADRecognizer2DLocal::setMetainfo(bool meta_info) { meta_info_ = meta_info; } - int ODCADRecognizer2DLocal::getIterationsCount() const + int CADRecognizer2DLocal::getIterationsCount() const { return iterations_count_; } - void ODCADRecognizer2DLocal::setIterationsCount(int iterations_count) + void CADRecognizer2DLocal::setIterationsCount(int iterations_count) { iterations_count_ = iterations_count; } - float ODCADRecognizer2DLocal::getReprojectionError() const + float CADRecognizer2DLocal::getReprojectionError() const { return reprojection_error_; } - void ODCADRecognizer2DLocal::setReprojectionError(float reprojection_error) + void CADRecognizer2DLocal::setReprojectionError(float reprojection_error) { reprojection_error_ = reprojection_error; } - double ODCADRecognizer2DLocal::getConfidence() const + double CADRecognizer2DLocal::getConfidence() const { return confidence_; } - void ODCADRecognizer2DLocal::setConfidence(double confidence) + void CADRecognizer2DLocal::setConfidence(double confidence) { confidence_ = confidence; } - int ODCADRecognizer2DLocal::getMinInliers() const + int CADRecognizer2DLocal::getMinInliers() const { return min_inliers_; } - void ODCADRecognizer2DLocal::setMinInliers(int min_inliers) + void CADRecognizer2DLocal::setMinInliers(int min_inliers) { min_inliers_ = min_inliers; } - int ODCADRecognizer2DLocal::getPnpMethod() const + int CADRecognizer2DLocal::getPnpMethod() const { return pnp_method_; } - void ODCADRecognizer2DLocal::setPnpMethod(int pnp_method) + void CADRecognizer2DLocal::setPnpMethod(int pnp_method) { pnp_method_ = pnp_method; } - void ODCADRecognizer2DLocal::parseParameterString(const std::string & parameter_string) + void CADRecognizer2DLocal::parseParameterString(const std::string & parameter_string) { const std::string keys = "{help h | | print this message }" "{video v | | path to recorded video }" @@ -238,7 +238,7 @@ namespace od } - void ODCADRecognizer2DLocal::init() + void CADRecognizer2DLocal::init() { cv::FileStorage fs(camera_intrinsic_file_, cv::FileStorage::READ); cv::Mat cam_man, dist_coeff; @@ -258,33 +258,33 @@ namespace od if(models_.size() > 0) f_type_default_ = string2FeatureType(models_[0].f_type_); -#ifdef WITH_BOOST_SHARED_PTR - feature_detector_ = shared_ptr<ODFeatureDetector>(new ODFeatureDetector(f_type_default_)); +#ifndef WITH_BOOST_SHARED_PTR + feature_detector_ = shared_ptr<FeatureDetector>(new FeatureDetector(f_type_default_)); #else - feature_detector_ = make_shared<ODFeatureDetector>(f_type_default_); + feature_detector_ = make_shared<FeatureDetector>(f_type_default_); #endif } - shared_ptr<ODDetections> ODCADRecognizer2DLocal::detect(shared_ptr<ODScene> scene) + shared_ptr<Detections> CADRecognizer2DLocal::detect(shared_ptr<Scene> scene) { - std::cout << "not implemented, use with shared_ptr<ODScene>" <<std::endl; + std::cout << "not implemented, use with shared_ptr<Scene>" <<std::endl; return nullptr; }; - shared_ptr<ODDetections> ODCADRecognizer2DLocal::detectOmni(shared_ptr<ODScene> scene) + shared_ptr<Detections> CADRecognizer2DLocal::detectOmni(shared_ptr<Scene> scene) { - std::cout << "not implemented, use with shared_ptr<ODScene>" <<std::endl; + std::cout << "not implemented, use with shared_ptr<Scene>" <<std::endl; return nullptr; }; - shared_ptr<ODDetections> ODCADRecognizer2DLocal::detect(shared_ptr<ODSceneImage> scene) + shared_ptr<Detections> CADRecognizer2DLocal::detect(shared_ptr<SceneImage> scene) { - shared_ptr<ODDetections3D> detections = detectOmni(scene); - return dynamic_pointer_cast<ODDetections>(detections); + shared_ptr<Detections3D> detections = detectOmni(scene); + return dynamic_pointer_cast<Detections>(detections); } - bool ODCADRecognizer2DLocal::detectSingleModel(shared_ptr<ODSceneImage> scene, const Model & model, shared_ptr<ODDetection3D> & detection3D, + bool CADRecognizer2DLocal::detectSingleModel(shared_ptr<SceneImage> scene, const Model & model, shared_ptr<Detection3D> & detection3D, const cv::Mat & frame_vis) { @@ -337,16 +337,16 @@ namespace od std::cout << "Recognized: " << model.id_ << std::endl; //else everything is fine; report the detection if(!detection3D) - detection3D = make_shared<ODDetection3D>(); + detection3D = make_shared<Detection3D>(); detection3D->setLocation(pnp_detection_.getTMatrix()); detection3D->setPose(pnp_detection_.getRMatrix()); - detection3D->setType(ODDetection::OD_RECOGNITION); + detection3D->setType(Detection::RECOGNITION); detection3D->setId(model.id_); return true; } - shared_ptr<ODDetections3D> ODCADRecognizer2DLocal::detectOmni(shared_ptr<ODSceneImage> scene) + shared_ptr<Detections3D> CADRecognizer2DLocal::detectOmni(shared_ptr<SceneImage> scene) { std::vector<cv::KeyPoint> keypoints_scene; @@ -355,13 +355,13 @@ namespace od scene->setDescriptors(descriptor_scene); scene->setKeypoints(keypoints_scene); - shared_ptr<ODDetections3D> detections = make_shared<ODDetections3D>(); + shared_ptr<Detections3D> detections = make_shared<Detections3D>(); cv::Mat viz = scene->getCVImage().clone(); for(size_t i = 0; i < models_.size(); ++i) { - shared_ptr<ODDetection3D> detection = make_shared<ODDetection3D>(); + shared_ptr<Detection3D> detection = make_shared<Detection3D>(); if(detectSingleModel(scene, models_[i], detection, viz)) { detections->push_back(detection); diff --git a/detectors/src/local2D/simple_ransac_detection/ODCsvReader.cpp b/detectors/src/local2D/simple_ransac_detection/CsvReader.cpp similarity index 97% rename from detectors/src/local2D/simple_ransac_detection/ODCsvReader.cpp rename to detectors/src/local2D/simple_ransac_detection/CsvReader.cpp index fa699374..887a5b52 100644 --- a/detectors/src/local2D/simple_ransac_detection/ODCsvReader.cpp +++ b/detectors/src/local2D/simple_ransac_detection/CsvReader.cpp @@ -1,4 +1,4 @@ -#include "od/detectors/local2D/simple_ransac_detection/ODCsvReader.h" +#include "od/detectors/local2D/simple_ransac_detection/CsvReader.h" namespace od { diff --git a/detectors/src/local2D/simple_ransac_detection/ODCsvWriter.cpp b/detectors/src/local2D/simple_ransac_detection/CsvWriter.cpp similarity index 95% rename from detectors/src/local2D/simple_ransac_detection/ODCsvWriter.cpp rename to detectors/src/local2D/simple_ransac_detection/CsvWriter.cpp index aec80e67..6737bd7d 100644 --- a/detectors/src/local2D/simple_ransac_detection/ODCsvWriter.cpp +++ b/detectors/src/local2D/simple_ransac_detection/CsvWriter.cpp @@ -1,4 +1,4 @@ -#include "od/detectors/local2D/simple_ransac_detection/ODCsvWriter.h" +#include "od/detectors/local2D/simple_ransac_detection/CsvWriter.h" namespace od { diff --git a/detectors/src/local2D/simple_ransac_detection/ODMesh.cpp b/detectors/src/local2D/simple_ransac_detection/Mesh.cpp similarity index 97% rename from detectors/src/local2D/simple_ransac_detection/ODMesh.cpp rename to detectors/src/local2D/simple_ransac_detection/Mesh.cpp index da633136..77efba26 100644 --- a/detectors/src/local2D/simple_ransac_detection/ODMesh.cpp +++ b/detectors/src/local2D/simple_ransac_detection/Mesh.cpp @@ -5,7 +5,7 @@ * Author: edgar */ -#include "od/detectors/local2D/simple_ransac_detection/ODMesh.h" +#include "od/detectors/local2D/simple_ransac_detection/Mesh.h" namespace od { diff --git a/detectors/src/local2D/simple_ransac_detection/ODModel.cpp b/detectors/src/local2D/simple_ransac_detection/Model.cpp similarity index 97% rename from detectors/src/local2D/simple_ransac_detection/ODModel.cpp rename to detectors/src/local2D/simple_ransac_detection/Model.cpp index 63c7deb6..1b14adc7 100644 --- a/detectors/src/local2D/simple_ransac_detection/ODModel.cpp +++ b/detectors/src/local2D/simple_ransac_detection/Model.cpp @@ -6,7 +6,7 @@ */ -#include "od/detectors/local2D/simple_ransac_detection/ODModel.h" +#include "od/detectors/local2D/simple_ransac_detection/Model.h" namespace od { @@ -49,7 +49,7 @@ namespace od { Model::~Model() { - // TODO Auto-generated destructor stub + // TO Auto-generated destructor stub } void Model::addCorrespondence(const cv::Point2f & point2d, const cv::Point3f & point3d) diff --git a/detectors/src/local2D/simple_ransac_detection/ODModelRegistration.cpp b/detectors/src/local2D/simple_ransac_detection/ModelRegistration.cpp similarity index 91% rename from detectors/src/local2D/simple_ransac_detection/ODModelRegistration.cpp rename to detectors/src/local2D/simple_ransac_detection/ModelRegistration.cpp index 91e51204..bf6905d2 100644 --- a/detectors/src/local2D/simple_ransac_detection/ODModelRegistration.cpp +++ b/detectors/src/local2D/simple_ransac_detection/ModelRegistration.cpp @@ -5,7 +5,7 @@ * Author: edgar */ -#include "od/detectors/local2D/simple_ransac_detection/ODModelRegistration.h" +#include "od/detectors/local2D/simple_ransac_detection/ModelRegistration.h" namespace od { @@ -49,7 +49,7 @@ namespace od { ModelRegistration::~ModelRegistration() { - // TODO Auto-generated destructor stub + // TO Auto-generated destructor stub } void ModelRegistration::registerPoint(const cv::Point2f & point2d, const cv::Point3f & point3d) diff --git a/detectors/src/local2D/simple_ransac_detection/ODPnPProblem.cpp b/detectors/src/local2D/simple_ransac_detection/PnPProblem.cpp similarity index 99% rename from detectors/src/local2D/simple_ransac_detection/ODPnPProblem.cpp rename to detectors/src/local2D/simple_ransac_detection/PnPProblem.cpp index d6e2c51d..532a2b91 100644 --- a/detectors/src/local2D/simple_ransac_detection/ODPnPProblem.cpp +++ b/detectors/src/local2D/simple_ransac_detection/PnPProblem.cpp @@ -5,7 +5,7 @@ * Author: Edgar Riba */ -#include "od/detectors/local2D/simple_ransac_detection/ODPnPProblem.h" +#include "od/detectors/local2D/simple_ransac_detection/PnPProblem.h" namespace od { @@ -128,7 +128,7 @@ namespace od { PnPProblem::~PnPProblem() { - // TODO Auto-generated destructor stub + // TO Auto-generated destructor stub } void PnPProblem::setPMatrix(const cv::Mat & r_matrix, const cv::Mat & t_matrix) diff --git a/detectors/src/local2D/simple_ransac_detection/ODRobustMatcher.cpp b/detectors/src/local2D/simple_ransac_detection/RobustMatcher.cpp similarity index 98% rename from detectors/src/local2D/simple_ransac_detection/ODRobustMatcher.cpp rename to detectors/src/local2D/simple_ransac_detection/RobustMatcher.cpp index 6a84f7bc..c5b4b265 100644 --- a/detectors/src/local2D/simple_ransac_detection/ODRobustMatcher.cpp +++ b/detectors/src/local2D/simple_ransac_detection/RobustMatcher.cpp @@ -5,7 +5,7 @@ * Author: eriba */ -#include "od/detectors/local2D/simple_ransac_detection/ODRobustMatcher.h" +#include "od/detectors/local2D/simple_ransac_detection/RobustMatcher.h" namespace od { @@ -107,7 +107,7 @@ namespace od { // //matcher_ = cv::makePtr<cv::BFMatcher>((int)cv::NORM_L2, false); // // - // //####################TEMPORARY CODE BELOW!!!!!!!!!!!!!!!!!!!!!!!! + // //####################TEMPORARY CE BELOW!!!!!!!!!!!!!!!!!!!!!!!! // // cv::Ptr<cv::flann::IndexParams> indexParams = cv::makePtr<cv::flann::LshIndexParams>(6, 12, 1); // instantiate LSH index parameters // cv::Ptr<cv::flann::SearchParams> searchParams = cv::makePtr<cv::flann::SearchParams>(50); // instantiate flann search parameters @@ -120,7 +120,7 @@ namespace od { RobustMatcher::~RobustMatcher() { - // TODO Auto-generated destructor stub + // TO Auto-generated destructor stub } void RobustMatcher::computeKeyPoints(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) diff --git a/detectors/src/local2D/simple_ransac_detection/ODUtils.cpp b/detectors/src/local2D/simple_ransac_detection/Utils.cpp similarity index 99% rename from detectors/src/local2D/simple_ransac_detection/ODUtils.cpp rename to detectors/src/local2D/simple_ransac_detection/Utils.cpp index ac15b5aa..a4ef573e 100644 --- a/detectors/src/local2D/simple_ransac_detection/ODUtils.cpp +++ b/detectors/src/local2D/simple_ransac_detection/Utils.cpp @@ -5,7 +5,7 @@ * Author: Edgar Riba */ -#include "od/detectors/local2D/simple_ransac_detection/ODUtils.h" +#include "od/detectors/local2D/simple_ransac_detection/Utils.h" namespace od { diff --git a/detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp b/detectors/src/local2D/training/CADRecogTrainerSnapshotBased.cpp similarity index 98% rename from detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp rename to detectors/src/local2D/training/CADRecogTrainerSnapshotBased.cpp index 8c4241f0..031dc559 100644 --- a/detectors/src/local2D/training/ODCADRecogTrainerSnapshotBased.cpp +++ b/detectors/src/local2D/training/CADRecogTrainerSnapshotBased.cpp @@ -18,20 +18,20 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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. */ -#include "od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h" +#include "od/detectors/local2D/training/CADRecogTrainerSnapshotBased.h" namespace od { namespace l2d { - int ODCADRecogTrainerSnapshotBased::train() + int CADRecogTrainerSnapshotBased::train() { fileutils::createTrainingDir(trained_data_location_); @@ -52,7 +52,7 @@ namespace od return 1; } - void ODCADRecogTrainerSnapshotBased::trainSingleModel(const std::string & objname) + void CADRecogTrainerSnapshotBased::trainSingleModel(const std::string & objname) { ///////////setup object diff --git a/detectors/src/misc/CMakeLists.txt b/detectors/src/misc/CMakeLists.txt index 877cbf63..6eb6c07b 100644 --- a/detectors/src/misc/CMakeLists.txt +++ b/detectors/src/misc/CMakeLists.txt @@ -6,12 +6,13 @@ set(SUBSYS_DEPS od_common od_global_image_detector od_pointcloud_global_detector if(BUILD_MISC_DETECTION) if(TARGET od_global_image_detector AND TARGET od_pointcloud_global_detector) set(SOURCES - "detection/ODDetectorMultiAlgo.cpp" + "detection/DetectorMultiAlgo.cpp" ) include_directories(${DETECTORS_IMPL_DIR}) include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) include_directories(${OpenCV_INCLUDE_DIRS}) include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) include_directories(${PCL_INCLUDE_DIRS}) diff --git a/detectors/src/misc/detection/DetectorMultiAlgo.cpp b/detectors/src/misc/detection/DetectorMultiAlgo.cpp new file mode 100644 index 00000000..8e557861 --- /dev/null +++ b/detectors/src/misc/detection/DetectorMultiAlgo.cpp @@ -0,0 +1,107 @@ +#include "od/detectors/misc/detection/DetectorMultiAlgo.hpp" + + +namespace od +{ + + template + shared_ptr<Detections> DetectorMultiAlgo2D<pcl::PointXYZ>::detectOmni(shared_ptr<Scene> scene); + + template + shared_ptr<Detections> DetectorMultiAlgo2D<pcl::PointXYZ>::detect(shared_ptr<Scene> scene); + + template + shared_ptr<Detections> DetectorMultiAlgo2D<pcl::PointXYZ>::detect(shared_ptr<SceneImage> scene); + + template + shared_ptr<Detections2D> DetectorMultiAlgo2D<pcl::PointXYZ>::detectOmni(shared_ptr<SceneImage > scene); + + template + void DetectorMultiAlgo2D<pcl::PointXYZ>::init(); + + + + template + shared_ptr<Detections> DetectorMultiAlgo2D<pcl::PointXYZRGB>::detectOmni(shared_ptr<Scene> scene); + + template + shared_ptr<Detections> DetectorMultiAlgo2D<pcl::PointXYZRGB>::detect(shared_ptr<Scene> scene); + + template + shared_ptr<Detections> DetectorMultiAlgo2D<pcl::PointXYZRGB>::detect(shared_ptr<SceneImage> scene); + + template + shared_ptr<Detections2D> DetectorMultiAlgo2D<pcl::PointXYZRGB>::detectOmni(shared_ptr<SceneImage > scene); + + template + void DetectorMultiAlgo2D<pcl::PointXYZRGB>::init(); + + + + template + shared_ptr<Detections> DetectorMultiAlgo2D<pcl::PointXYZRGBA>::detectOmni(shared_ptr<Scene> scene); + + template + shared_ptr<Detections> DetectorMultiAlgo2D<pcl::PointXYZRGBA>::detect(shared_ptr<Scene> scene); + + template + shared_ptr<Detections> DetectorMultiAlgo2D<pcl::PointXYZRGBA>::detect(shared_ptr<SceneImage> scene); + + template + shared_ptr<Detections2D> DetectorMultiAlgo2D<pcl::PointXYZRGBA>::detectOmni(shared_ptr<SceneImage > scene); + + template + void DetectorMultiAlgo2D<pcl::PointXYZRGBA>::init(); + + + + template + shared_ptr<Detections> DetectorMultiAlgo<pcl::PointXYZ>::detectOmni(shared_ptr<Scene> scene); + + template + shared_ptr<Detections> DetectorMultiAlgo<pcl::PointXYZ>::detect(shared_ptr<Scene> scene); + + template + void DetectorMultiAlgo<pcl::PointXYZ>::init(); + + template + shared_ptr<Detections> DetectorMultiAlgo<pcl::PointXYZ>::detect(shared_ptr<ScenePointCloud<pcl::PointXYZ> > scene); + + template + shared_ptr<Detections3D> DetectorMultiAlgo<pcl::PointXYZ>::detectOmni(shared_ptr<ScenePointCloud<pcl::PointXYZ> > scene); + + + + template + shared_ptr<Detections> DetectorMultiAlgo<pcl::PointXYZRGB>::detectOmni(shared_ptr<Scene> scene); + + template + shared_ptr<Detections> DetectorMultiAlgo<pcl::PointXYZRGB>::detect(shared_ptr<Scene> scene); + + template + void DetectorMultiAlgo<pcl::PointXYZRGB>::init(); + + template + shared_ptr<Detections> DetectorMultiAlgo<pcl::PointXYZRGB>::detect(shared_ptr<ScenePointCloud<pcl::PointXYZRGB> > scene); + + template + shared_ptr<Detections3D> DetectorMultiAlgo<pcl::PointXYZRGB>::detectOmni(shared_ptr<ScenePointCloud<pcl::PointXYZRGB> > scene); + + + + template + shared_ptr<Detections> DetectorMultiAlgo<pcl::PointXYZRGBA>::detectOmni(shared_ptr<Scene> scene); + + template + shared_ptr<Detections> DetectorMultiAlgo<pcl::PointXYZRGBA>::detect(shared_ptr<Scene> scene); + + template + void DetectorMultiAlgo<pcl::PointXYZRGBA>::init(); + + template + shared_ptr<Detections> DetectorMultiAlgo<pcl::PointXYZRGBA>::detect(shared_ptr<ScenePointCloud<pcl::PointXYZRGBA> > scene); + + template + shared_ptr<Detections3D> DetectorMultiAlgo<pcl::PointXYZRGBA>::detectOmni(shared_ptr<ScenePointCloud<pcl::PointXYZRGBA> > scene); + +} \ No newline at end of file diff --git a/detectors/src/misc/detection/ODDetectorMultiAlgo.cpp b/detectors/src/misc/detection/ODDetectorMultiAlgo.cpp deleted file mode 100644 index 09012d0e..00000000 --- a/detectors/src/misc/detection/ODDetectorMultiAlgo.cpp +++ /dev/null @@ -1,107 +0,0 @@ -#include "od/detectors/misc/detection/ODDetectorMultiAlgo.hpp" - - -namespace od -{ - - template - shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZ>::detectOmni(shared_ptr<ODScene> scene); - - template - shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZ>::detect(shared_ptr<ODScene> scene); - - template - shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZ>::detect(shared_ptr<ODSceneImage> scene); - - template - shared_ptr<ODDetections2D> ODDetectorMultiAlgo2D<pcl::PointXYZ>::detectOmni(shared_ptr<ODSceneImage > scene); - - template - void ODDetectorMultiAlgo2D<pcl::PointXYZ>::init(); - - - - template - shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZRGB>::detectOmni(shared_ptr<ODScene> scene); - - template - shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZRGB>::detect(shared_ptr<ODScene> scene); - - template - shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZRGB>::detect(shared_ptr<ODSceneImage> scene); - - template - shared_ptr<ODDetections2D> ODDetectorMultiAlgo2D<pcl::PointXYZRGB>::detectOmni(shared_ptr<ODSceneImage > scene); - - template - void ODDetectorMultiAlgo2D<pcl::PointXYZRGB>::init(); - - - - template - shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZRGBA>::detectOmni(shared_ptr<ODScene> scene); - - template - shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZRGBA>::detect(shared_ptr<ODScene> scene); - - template - shared_ptr<ODDetections> ODDetectorMultiAlgo2D<pcl::PointXYZRGBA>::detect(shared_ptr<ODSceneImage> scene); - - template - shared_ptr<ODDetections2D> ODDetectorMultiAlgo2D<pcl::PointXYZRGBA>::detectOmni(shared_ptr<ODSceneImage > scene); - - template - void ODDetectorMultiAlgo2D<pcl::PointXYZRGBA>::init(); - - - - template - shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZ>::detectOmni(shared_ptr<ODScene> scene); - - template - shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZ>::detect(shared_ptr<ODScene> scene); - - template - void ODDetectorMultiAlgo<pcl::PointXYZ>::init(); - - template - shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZ>::detect(shared_ptr<ODScenePointCloud<pcl::PointXYZ> > scene); - - template - shared_ptr<ODDetections3D> ODDetectorMultiAlgo<pcl::PointXYZ>::detectOmni(shared_ptr<ODScenePointCloud<pcl::PointXYZ> > scene); - - - - template - shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZRGB>::detectOmni(shared_ptr<ODScene> scene); - - template - shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZRGB>::detect(shared_ptr<ODScene> scene); - - template - void ODDetectorMultiAlgo<pcl::PointXYZRGB>::init(); - - template - shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZRGB>::detect(shared_ptr<ODScenePointCloud<pcl::PointXYZRGB> > scene); - - template - shared_ptr<ODDetections3D> ODDetectorMultiAlgo<pcl::PointXYZRGB>::detectOmni(shared_ptr<ODScenePointCloud<pcl::PointXYZRGB> > scene); - - - - template - shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZRGBA>::detectOmni(shared_ptr<ODScene> scene); - - template - shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZRGBA>::detect(shared_ptr<ODScene> scene); - - template - void ODDetectorMultiAlgo<pcl::PointXYZRGBA>::init(); - - template - shared_ptr<ODDetections> ODDetectorMultiAlgo<pcl::PointXYZRGBA>::detect(shared_ptr<ODScenePointCloud<pcl::PointXYZRGBA> > scene); - - template - shared_ptr<ODDetections3D> ODDetectorMultiAlgo<pcl::PointXYZRGBA>::detectOmni(shared_ptr<ODScenePointCloud<pcl::PointXYZRGBA> > scene); - -} \ No newline at end of file diff --git a/doc/doxygen/od_root.md b/doc/doxygen/od_root.md index 174187b0..94dfb8e9 100644 --- a/doc/doxygen/od_root.md +++ b/doc/doxygen/od_root.md @@ -90,7 +90,7 @@ We will be presenting our library at FOSSASIA 2016, Singapore - an annual confer <div class="item"> \endhtmlonly - \link od::l2d::ODCADRecognizer2DLocal + \link od::l2d::CADRecognizer2DLocal \htmlonly <img src="cadrecog2d.png"> <div class="carousel-caption"> @@ -118,7 +118,7 @@ We will be presenting our library at FOSSASIA 2016, Singapore - an annual confer <div class="item"> \endhtmlonly - \link od::g2d::ODCascadeDetector + \link od::g2d::CascadeDetector \htmlonly <img src="face_business.jpg"> <div class="carousel-caption"> diff --git a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md index c876d08a..e509ed8c 100644 --- a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md +++ b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md @@ -367,4 +367,39 @@ The third change is a major change composed by two parts. The first one involves The second main change was to create the same interface for the **ODFeatureDetector2D** which could create gpu detectors or cpu ones. I did the same interface as before creating **ODFeatureDetector**, **ODFeatureDetector2D**, **ODFeatureDetectorInterface**. The constructor switches the passed parameter and based on the type allocates the correct feature detector, cpu or gpu. Also the gpu part is in the gpu module to maintain the advantages described before. The only small drawback of this structure is that the common module and the gpu_common module have a circular dependency on the include files, so we have to set the **include_directories** statment outside of the two modules. I do this in the **ODDependency.cmake** file. -Obviously I had to change all the examples to adapt to this new structure, but that was not so hard, while the new interface was quite tricky to be created in a clean way. \ No newline at end of file +Obviously I had to change all the examples to adapt to this new structure, but that was not so hard, while the new interface was quite tricky to be created in a clean way. + +##Naming convention and build example 29/07/15## + +I changed the nameing convention in the library to adapt it to a more C++ style. Before all file names started with **OD**, along with all classes and types. This is useful in C code since you are laking namespaces, but in C++ it makes no sense. I changed everything to the new style and fixed some compilation errors which arose. + +I also changed the **WITH_BOOST_SHARED_PTR** to **WITH_STD_SHARED_PTR** since when you include the file in your own project the default will be boost. + +I created an example in apps/buildExample which shows how to create your own project using the opendetection lirbary. + +@code + +# Example on how to build a project using the OpenDetection Library + +cmake_minimum_required(VERSION 2.8) +project(viewer) + +find_package(OD REQUIRED) +find_package(PCL REQUIRED) +find_package(OpenCV REQUIRED) + +include_directories(${OD_INCLUDE_DIRS}) +include_directories(${PCL_INCLUDE_DIRS}) +include_directories(${OpenCV_INCLUDE_DIRS}) + +link_directories(${OD_LIBRARY_PATH}) + +add_executable(viewer VisualizationTest.cpp) +target_link_libraries(viewer ${OD_LIBRARIES} ${OpenCV_LIBS} ${PCL_LIBRARIES}) + +@endcode + +This shows that you can use the provided config file, which is created during instllation time, to find and include the lirbary. +I am finishing now the Doxygen documentation which is missing in some parts of the lbirary and then i will finish to do also some template percompilation which I saw was missing. +After that there is the need to integrate the other google summer of code Project. + diff --git a/examples/apps/CMakeLists.txt b/examples/apps/CMakeLists.txt index f554c9e8..e7870891 100644 --- a/examples/apps/CMakeLists.txt +++ b/examples/apps/CMakeLists.txt @@ -28,15 +28,15 @@ if(od_multihog_app) include_directories(${COMMON_INCLUDE_DIR}) OD_ADD_EXAMPLE(od_multihog_app - FILES global2D/od_multihog_app.cpp + FILES global2D/multihog_app.cpp LINK_WITH od_global_image_detector ) else() - message("!!! od_multihog_app is set to on but BUILD_GLOBAL_2D_DETECTION is necessary to build od_multihog_app") + message("!!! multihog_app is set to on but BUILD_GLOBAL_2D_DETECTION is necessary to build od_multihog_app") endif(TARGET od_global_image_detector) endif(od_multihog_app) ################################################################################## set(SOURCE_FILES version/opendetection.cpp) -include_directories(${OD_BINARY_DIR}/generated) +include_directories(${_BINARY_DIR}/generated) add_executable(opendetection ${SOURCE_FILES}) diff --git a/examples/apps/buildExample/CMakeLists.txt b/examples/apps/buildExample/CMakeLists.txt index 44b24530..b4b6d85c 100644 --- a/examples/apps/buildExample/CMakeLists.txt +++ b/examples/apps/buildExample/CMakeLists.txt @@ -13,6 +13,6 @@ include_directories(${OpenCV_INCLUDE_DIRS}) link_directories(${OD_LIBRARY_PATH}) -add_executable(viewer ODVisualizationTest.cpp) +add_executable(viewer VisualizationTest.cpp) target_link_libraries(viewer ${OD_LIBRARIES} ${OpenCV_LIBS} ${PCL_LIBRARIES}) diff --git a/examples/apps/buildExample/ODVisualizationTest.cpp b/examples/apps/buildExample/VisualizationTest.cpp similarity index 93% rename from examples/apps/buildExample/ODVisualizationTest.cpp rename to examples/apps/buildExample/VisualizationTest.cpp index 436fa046..3e4b78fa 100644 --- a/examples/apps/buildExample/ODVisualizationTest.cpp +++ b/examples/apps/buildExample/VisualizationTest.cpp @@ -1,4 +1,4 @@ -#include "od/common/utils/ODViewer.h" +#include "od/common/utils/Viewer.h" #include <iostream> #include <pcl/io/pcd_io.h> @@ -16,7 +16,7 @@ int main(int argc, char * argv[]){ return -1; } - od::ODViewer viewer; + od::Viewer viewer; viewer.render(cloud, std::string("test_cloud")); while(!viewer.toStop()){ diff --git a/examples/apps/cadrecog2D/od_test_single_db_single_model.cpp b/examples/apps/cadrecog2D/test_single_db_single_model.cpp similarity index 80% rename from examples/apps/cadrecog2D/od_test_single_db_single_model.cpp rename to examples/apps/cadrecog2D/test_single_db_single_model.cpp index 1303dab6..3c531a1a 100644 --- a/examples/apps/cadrecog2D/od_test_single_db_single_model.cpp +++ b/examples/apps/cadrecog2D/test_single_db_single_model.cpp @@ -18,15 +18,15 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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. */ -#include "od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h" -#include "od/detectors/local2D/detection/ODCADRecognizer2DLocal.h" -#include "od/common/utils/ODFrameGenerator.h" +#include "od/detectors/local2D/training/CADRecogTrainerSnapshotBased.h" +#include "od/detectors/local2D/detection/CADRecognizer2DLocal.h" +#include "od/common/utils/FrameGenerator.h" #include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) @@ -44,23 +44,23 @@ int main(int argc, char *argv[]) std::ofstream logfile(outputfile.c_str()); //detector - od::l2d::ODCADRecognizer2DLocal detector(modelsPath); + od::l2d::CADRecognizer2DLocal detector(modelsPath); //set commandline options type inputs detector.parseParameterString("--use_gpu --method=1 --error=2 --confidence=0.5 --iterations=1000 --inliers=6 --metainfo"); detector.setCameraIntrinsicFile(camerapath); //set some other inputs detector.init(); //get scenes - od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(imagespath); + od::FrameGenerator<od::SceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(imagespath); //GUI //cv::namedWindow("Overlay", cv::WINDOW_NORMAL); while(frameGenerator.isValid() && cv::waitKey(10) != 27) { - boost::shared_ptr<od::ODSceneImage> scene = frameGenerator.getNextFrame(); + boost::shared_ptr<od::SceneImage> scene = frameGenerator.getNextFrame(); //cv::imshow("Overlay", scene->getCVImage()); //Detect - boost::shared_ptr<od::ODDetections3D> detections = detector.detectOmni(scene); + boost::shared_ptr<od::Detections3D> detections = detector.detectOmni(scene); if(detections->size() > 0) { @@ -69,7 +69,7 @@ int main(int argc, char *argv[]) for(size_t i = 0; i < detections->size(); i++) { - boost::shared_ptr<od::ODDetection3D> detection = detections->at(i); + boost::shared_ptr<od::Detection3D> detection = detections->at(i); detection->printSelf(); logfile << detection->getId() << endl; } diff --git a/examples/apps/global2D/od_multihog_app.cpp b/examples/apps/global2D/multihog_app.cpp similarity index 78% rename from examples/apps/global2D/od_multihog_app.cpp rename to examples/apps/global2D/multihog_app.cpp index 2c4a94ee..1d17e122 100644 --- a/examples/apps/global2D/od_multihog_app.cpp +++ b/examples/apps/global2D/multihog_app.cpp @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -30,9 +30,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -#include "od/detectors/global2D/detection/ODHOGDetector.h" -#include "od/common/utils/ODFrameGenerator.h" -#include "od/common/utils/ODUtils.h" +#include "od/detectors/global2D/detection/HOGDetector.h" +#include "od/common/utils/FrameGenerator.h" +#include "od/common/utils/Utils.h" #include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) @@ -46,17 +46,17 @@ int main(int argc, char *argv[]) std::vector<std::string> messages; messages.push_back("Original"); - std::vector<od::g2d::ODHOGDetector> detectors; - od::g2d::ODHOGDetector detector1; // + std::vector<od::g2d::HOGDetector> detectors; + od::g2d::HOGDetector detector1; // messages.push_back("OpenCV Default People"); detectors.push_back(detector1); - od::g2d::ODHOGDetector detector2; - detector2.setSvmtype(od::g2d::ODHOGDetector::OD_DAIMLER_PEOPLE); + od::g2d::HOGDetector detector2; + detector2.setSvmtype(od::g2d::HOGDetector::_DAIMLER_PEOPLE); messages.push_back("OpenCV Daimler People"); detectors.push_back(detector2); - od::g2d::ODHOGDetector detector3(trained_data_dir); + od::g2d::HOGDetector detector3(trained_data_dir); messages.push_back("Custom HOG from trained data"); detectors.push_back(detector3); @@ -65,8 +65,8 @@ int main(int argc, char *argv[]) detectors[i].init(); //get scenes - od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_DEVICE> frameGenerator(input_video); - //od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(input_video); + od::FrameGenerator<od::SceneImage, od::GENERATOR_TYPE_DEVICE> frameGenerator(input_video); + //od::FrameGenerator<od::SceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(input_video); cv::VideoWriter videoWriter(output_video, CV_FOURCC('M','J','P','G'), 25, size_single * 2, true); //GUI @@ -74,7 +74,7 @@ int main(int argc, char *argv[]) cv::Mat multi_image; while(frameGenerator.isValid() && cv::waitKey(33) != 27) { - boost::shared_ptr<od::ODSceneImage> scene = frameGenerator.getNextFrame(); + boost::shared_ptr<od::SceneImage> scene = frameGenerator.getNextFrame(); std::vector<cv::Mat> images_to_show; images_to_show.push_back(scene->getCVImage()); //push the first image @@ -82,7 +82,7 @@ int main(int argc, char *argv[]) //detect 3 times for(size_t i = 0; i < detectors.size(); i++) { - boost::shared_ptr<od::ODDetections2D> detections = detectors[i].detectOmni(scene); + boost::shared_ptr<od::Detections2D> detections = detectors[i].detectOmni(scene); if(detections->size() > 0) images_to_show.push_back(detections->renderMetainfo(*scene).getCVImage()); else diff --git a/examples/objectdetector/CMakeLists.txt b/examples/objectdetector/CMakeLists.txt index f9399fd6..644944e7 100644 --- a/examples/objectdetector/CMakeLists.txt +++ b/examples/objectdetector/CMakeLists.txt @@ -10,7 +10,7 @@ if(od_image_camera_example) include_directories(${COMMON_IMPL_DIR}) OD_ADD_EXAMPLE(od_image_camera - FILES od_image_cadrecog_camera.cpp + FILES image_cadrecog_camera.cpp LINK_WITH od_local_image_detector) else() message("!!! od_image_camera_example is set to on but BUILD_LOCAL_2D_DETECTION is necessary to build od_image_camera_example") @@ -28,7 +28,7 @@ if(image_recognition_example) include_directories(${CMAKE_3RDPARTY_DIR}/pugixml/src/) OD_ADD_EXAMPLE(od_example_files - FILES od_image_cadrecog_files.cpp + FILES image_cadrecog_files.cpp LINK_WITH od_local_image_detector) else() message("!!! image_recognition_example is set to on but BUILD_LOCAL_2D_DETECTION is necessary to build image_recognition_example") @@ -47,7 +47,7 @@ if(hog_train_example) OD_ADD_EXAMPLE(od_hog_train FILES od_hog_train.cpp - LINK_WITH od_global_image_detector) + LINK_WITH global_image_detector) else() message("!!! built WITH_SVMLIGHT to enable hog_train_example") endif() @@ -68,7 +68,7 @@ if(image_hog_files_example) include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) OD_ADD_EXAMPLE(od_image_hog_files - FILES od_image_hog_files.cpp + FILES image_hog_files.cpp LINK_WITH od_global_image_detector od_gpu_global_image_detector) else() message("!!! built WITH_SVMLIGHT to enable image_hog_files_example") @@ -95,7 +95,7 @@ if(face_detection_files_example) endif() OD_ADD_EXAMPLE(od_face_detection_files - FILES od_face_detector_files.cpp + FILES face_detector_files.cpp LINK_WITH ${SYS_DEP}) else() message("!!! face_detection_files_example is set to on but BUILD_GLOBAL_2D_DETECTION is necessary to build face_detection_files_example") @@ -119,7 +119,7 @@ if(face_detection_cam_example) endif() OD_ADD_EXAMPLE(od_face_detection_cam - FILES od_face_detector_cam.cpp + FILES face_detector_cam.cpp LINK_WITH ${SYS_DEP}) else() message("!!! face_detection_cam_example is set to on but BUILD_GLOBAL_2D_DETECTION is necessary to build face_detection_cam_example") @@ -137,7 +137,7 @@ if(image_customhog_example) include_directories(${COMMON_IMPL_DIR}) OD_ADD_EXAMPLE(od_image_customhog - FILES od_image_customhog.cpp + FILES image_customhog.cpp LINK_WITH od_global_image_detector) else() message("!!! built WITH_SVMLIGHT to enable image_customhog_example") @@ -160,7 +160,7 @@ if(multialgo_files_example) include_directories(${OpenCV_INCLUDE_DIRS}) OD_ADD_EXAMPLE(od_multialgo_files - FILES od_multialgo_files.cpp + FILES multialgo_files.cpp LINK_WITH od_misc_detector) else() message("!!! built WITH_SVMLIGHT to enable multialgo_files_example") @@ -181,7 +181,7 @@ if(multialgo_pc_example) include_directories(${COMMON_IMPL_DIR}) OD_ADD_EXAMPLE(od_multialgo_pc - FILES od_multialgo_pc.cpp + FILES multialgo_pc.cpp LINK_WITH od_misc_detector) else() message("!!! built WITH_SVMLIGHT to enable multialgo_pc_example") @@ -202,7 +202,7 @@ if(cascade_cam_example) include_directories(${PCL_INCLUDE_DIRS}) OD_ADD_EXAMPLE(od_cascade_cam - FILES od_cascade_cam.cpp + FILES cascade_cam.cpp LINK_WITH od_global_image_detector) else() message("!!! cascade_cam_example is set to on but BUILD_GLOBAL_2D_DETECTION is necessary to build cascade_cam_example") @@ -218,8 +218,8 @@ if(cascade_files_example) include_directories(${COMMON_INCLUDE_DIR}) include_directories(${COMMON_IMPL_DIR}) - OD_ADD_EXAMPLE(od_cascade_files - FILES od_cascade_files.cpp + V_ADD_EXAMPLE(od_cascade_files + FILES cascade_files.cpp LINK_WITH od_global_image_detector) else() message("!!! cascade_files_example is set to on but BUILD_GLOBAL_2D_DETECTION is necessary to build cascade_files_example") @@ -236,7 +236,7 @@ if(image_facerecog_example) include_directories(${COMMON_IMPL_DIR}) OD_ADD_EXAMPLE(od_image_facerecog - FILES od_image_facerecog.cpp + FILES image_facerecog.cpp LINK_WITH od_global_image_detector) else() message("!!! image_facerecog_example is set to on but BUILD_GLOBAL_2D_DETECTION is necessary to build image_facerecog_example") @@ -253,7 +253,7 @@ if(pc_global_example) include_directories(${COMMON_INCLUDE_DIR}) OD_ADD_EXAMPLE(od_example_pc_global - FILES od_pc_global.cpp + FILES pc_global.cpp LINK_WITH od_pointcloud_global_detector) else() message("!!! pc_global_example is set to on but BUILD_GLOBAL_3D_DETECTION is necessary to build pc_global_example") @@ -270,7 +270,7 @@ if(pc_global_files_example) include_directories(${COMMON_INCLUDE_DIR}) OD_ADD_EXAMPLE(od_example_pc_global_files - FILES od_pc_global_files.cpp + FILES pc_global_files.cpp LINK_WITH od_pointcloud_global_detector) else() message("!!! pc_global_files_example is set to on but BUILD_GLOBAL_3D_DETECTION is necessary to build pc_global_files_example") @@ -287,7 +287,7 @@ if(pc_global_real_time_example) include_directories(${COMMON_INCLUDE_DIR}) OD_ADD_EXAMPLE(od_example_pc_global_real_time - FILES od_pc_global_real_time.cpp + FILES pc_global_real_time.cpp LINK_WITH od_pointcloud_global_detector) else() message("!!! pc_global_real_time_example is set to on but BUILD_GLOBAL_3D_DETECTION is necessary to build pc_global_real_time_example") @@ -303,7 +303,7 @@ if(framegenerator_example) include_directories(${PCL_INCLUDE_DIRS}) OD_ADD_EXAMPLE(od_framegenerator - FILES od_framegenerator.cpp + FILES framegenerator.cpp LINK_WITH od_common) endif() diff --git a/examples/objectdetector/od_cascade_cam.cpp b/examples/objectdetector/cascade_cam.cpp similarity index 80% rename from examples/objectdetector/od_cascade_cam.cpp rename to examples/objectdetector/cascade_cam.cpp index 74addd6f..900e81d9 100644 --- a/examples/objectdetector/od_cascade_cam.cpp +++ b/examples/objectdetector/cascade_cam.cpp @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -29,9 +29,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -#include "od/detectors/global2D/detection/ODCascadeDetector.h" -#include "od/common/utils/ODFrameGenerator.h" -#include "od/common/utils/ODViewer.h" +#include "od/detectors/global2D/detection/CascadeDetector.h" +#include "od/common/utils/FrameGenerator.h" +#include "od/common/utils/Viewer.h" #include <boost/shared_ptr.hpp> int main(int argc, char * argv[]) @@ -45,23 +45,23 @@ int main(int argc, char * argv[]) std::string trained_cascade(argv[1]); //detector - od::g2d::ODCascadeDetector detector; + od::g2d::CascadeDetector detector; detector.setTrainedDataLocation(trained_cascade); detector.init(); - od::ODViewer viewer; + od::Viewer viewer; viewer.initCVWindow(std::string("Overlay")); //get scenes - od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_DEVICE> frameGenerator(0); + od::FrameGenerator<od::SceneImage, od::GENERATOR_TYPE_DEVICE> frameGenerator(0); //GUI cv::namedWindow("Overlay", cv::WINDOW_NORMAL); while(frameGenerator.isValid() && viewer.wait(10) != 27) { - boost::shared_ptr<od::ODSceneImage> scene = frameGenerator.getNextFrame(); + boost::shared_ptr<od::SceneImage> scene = frameGenerator.getNextFrame(); //Detect - boost::shared_ptr<od::ODDetections2D> detections = detector.detectOmni(scene); + boost::shared_ptr<od::Detections2D> detections = detector.detectOmni(scene); if(detections->size() > 0) viewer.render(detections->getMetainfoImage(), "Overlay"); diff --git a/examples/objectdetector/od_cascade_files.cpp b/examples/objectdetector/cascade_files.cpp similarity index 81% rename from examples/objectdetector/od_cascade_files.cpp rename to examples/objectdetector/cascade_files.cpp index fbfda158..2e53a786 100644 --- a/examples/objectdetector/od_cascade_files.cpp +++ b/examples/objectdetector/cascade_files.cpp @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -29,9 +29,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -#include "od/detectors/global2D/detection/ODCascadeDetector.h" -#include "od/common/utils/ODFrameGenerator.h" -#include "od/common/utils/ODViewer.h" +#include "od/detectors/global2D/detection/CascadeDetector.h" +#include "od/common/utils/FrameGenerator.h" +#include "od/common/utils/Viewer.h" #include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) @@ -46,22 +46,22 @@ int main(int argc, char *argv[]) std::string images(argv[2]); //detector - od::g2d::ODCascadeDetector detector; + od::g2d::CascadeDetector detector; detector.setTrainedDataLocation(trained_cascade); detector.init(); //get scenes - od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(images); + od::FrameGenerator<od::SceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(images); - od::ODViewer viewer; + od::Viewer viewer; viewer.initCVWindow(std::string("Overlay")); while(frameGenerator.isValid() && viewer.wait(10) != 27) { - boost::shared_ptr<od::ODSceneImage> scene = frameGenerator.getNextFrame(); + boost::shared_ptr<od::SceneImage> scene = frameGenerator.getNextFrame(); //Detect - boost::shared_ptr<od::ODDetections2D> detections = detector.detectOmni(scene); + boost::shared_ptr<od::Detections2D> detections = detector.detectOmni(scene); if(detections->size() > 0) { diff --git a/examples/objectdetector/od_face_detector_cam.cpp b/examples/objectdetector/face_detector_cam.cpp similarity index 50% rename from examples/objectdetector/od_face_detector_cam.cpp rename to examples/objectdetector/face_detector_cam.cpp index 463e0e7c..4ee7580b 100644 --- a/examples/objectdetector/od_face_detector_cam.cpp +++ b/examples/objectdetector/face_detector_cam.cpp @@ -1,7 +1,7 @@ -#include "od/detectors/global2D/detection/ODCascadeDetector.h" +#include "od/detectors/global2D/detection/CascadeDetector.h" -#include "od/common/utils/ODFrameGenerator.h" -#include "od/common/utils/ODViewer.h" +#include "od/common/utils/FrameGenerator.h" +#include "od/common/utils/Viewer.h" #include <iostream> #include <string> @@ -17,29 +17,29 @@ int main(int argc, char * argv[]) } unsigned int gpu = 0; - shared_ptr<od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_DEVICE> > frame_generator; + shared_ptr<od::FrameGenerator<od::SceneImage, od::GENERATOR_TYPE_DEVICE> > frame_generator; if(argc > 2){ gpu = atoi(argv[2]); } - frame_generator = make_shared<od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_DEVICE> >(0); + frame_generator = make_shared<od::FrameGenerator<od::SceneImage, od::GENERATOR_TYPE_DEVICE> >(0); - od::ODViewer viewer; + od::Viewer viewer; viewer.initCVWindow(std::string("Faces")); - boost::shared_ptr<od::ODDetector2D> detector; + boost::shared_ptr<od::Detector2D> detector; //ADD GPU - detector = boost::make_shared<od::g2d::ODCascadeDetector>(gpu, argv[1]); + detector = boost::make_shared<od::g2d::CascadeDetector>(gpu, argv[1]); detector->init(); while(frame_generator->isValid() && viewer.wait(100) != 27) { - boost::shared_ptr<od::ODSceneImage> scene = frame_generator->getNextFrame(); + boost::shared_ptr<od::SceneImage> scene = frame_generator->getNextFrame(); - boost::shared_ptr<od::ODDetections2D> detections = detector->detectOmni(scene); + boost::shared_ptr<od::Detections2D> detections = detector->detectOmni(scene); if(detections->size() > 0) viewer.update(detections->renderMetainfo(scene), std::string("Faces")); diff --git a/examples/objectdetector/od_face_detector_files.cpp b/examples/objectdetector/face_detector_files.cpp similarity index 56% rename from examples/objectdetector/od_face_detector_files.cpp rename to examples/objectdetector/face_detector_files.cpp index 9dd79db9..9b015ce8 100644 --- a/examples/objectdetector/od_face_detector_files.cpp +++ b/examples/objectdetector/face_detector_files.cpp @@ -1,7 +1,7 @@ -#include "od/detectors/global2D/detection/ODCascadeDetector.h" +#include "od/detectors/global2D/detection/CascadeDetector.h" -#include "od/common/utils/ODFrameGenerator.h" -#include "od/common/utils/ODViewer.h" +#include "od/common/utils/FrameGenerator.h" +#include "od/common/utils/Viewer.h" #include <iostream> #include <boost/shared_ptr.hpp> @@ -16,28 +16,28 @@ int main(int argc, char * argv[]) } std::string image_path(argv[1]); - od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_FILE_LIST> frame_generator(image_path); + od::FrameGenerator<od::SceneImage, od::GENERATOR_TYPE_FILE_LIST> frame_generator(image_path); unsigned int gpu = 0; if(argc > 3) gpu = atoi(argv[3]); - boost::shared_ptr<od::ODDetector2D> detector; + boost::shared_ptr<od::Detector2D> detector; - detector = boost::make_shared<od::g2d::ODCascadeDetector>(gpu, argv[1]); + detector = boost::make_shared<od::g2d::CascadeDetector>(gpu, argv[1]); detector->init(); - od::ODViewer viewer; + od::Viewer viewer; viewer.initCVWindow(std::string("Faces")); while(frame_generator.isValid() && viewer.wait(10) != 27) { - boost::shared_ptr<od::ODSceneImage> scene = frame_generator.getNextFrame(); + boost::shared_ptr<od::SceneImage> scene = frame_generator.getNextFrame(); - boost::shared_ptr<od::ODDetections2D> detections = detector->detectOmni(scene); + boost::shared_ptr<od::Detections2D> detections = detector->detectOmni(scene); if(detections->size() > 0) viewer.update(detections->renderMetainfo(scene), std::string("Faces")); diff --git a/examples/objectdetector/od_framegenerator.cpp b/examples/objectdetector/framegenerator.cpp similarity index 82% rename from examples/objectdetector/od_framegenerator.cpp rename to examples/objectdetector/framegenerator.cpp index 3ef236db..7243ac80 100644 --- a/examples/objectdetector/od_framegenerator.cpp +++ b/examples/objectdetector/framegenerator.cpp @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -33,21 +33,21 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -#include "od/common/utils/ODFrameGenerator.h" -#include "od/common/utils/ODViewer.h" +#include "od/common/utils/FrameGenerator.h" +#include "od/common/utils/Viewer.h" int main(int argc, char *argv[]) { //GUI and feedback - od::ODViewer viewer; + od::Viewer viewer; viewer.initPCLWindow(std::string("frame")); - od::ODFrameGenerator<od::ODScenePointCloud<pcl::PointXYZRGBA>, od::GENERATOR_TYPE_DEVICE> frameGenerator(""); + od::FrameGenerator<od::ScenePointCloud<pcl::PointXYZRGBA>, od::GENERATOR_TYPE_DEVICE> frameGenerator(""); while(frameGenerator.isValid()) { - boost::shared_ptr<od::ODScenePointCloud<pcl::PointXYZRGBA>> frame = frameGenerator.getNextFrame(); + boost::shared_ptr<od::ScenePointCloud<pcl::PointXYZRGBA>> frame = frameGenerator.getNextFrame(); viewer.remove("frame"); viewer.update(frame, "frame"); diff --git a/examples/objectdetector/od_hog_train.cpp b/examples/objectdetector/hog_train.cpp similarity index 79% rename from examples/objectdetector/od_hog_train.cpp rename to examples/objectdetector/hog_train.cpp index b7eacfbd..83510fdf 100644 --- a/examples/objectdetector/od_hog_train.cpp +++ b/examples/objectdetector/hog_train.cpp @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -31,10 +31,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "od/detectors/global2D/training/ODHOGTrainer.h" -#include "od/detectors/global2D/detection/ODHOGDetector.h" -#include "od/common/utils/ODFrameGenerator.h" -#include "od/common/utils/ODViewer.h" +#include "od/detectors/global2D/training/HOGTrainer.h" +#include "od/detectors/global2D/detection/HOGDetector.h" +#include "od/common/utils/FrameGenerator.h" +#include "od/common/utils/Viewer.h" #include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) @@ -51,29 +51,29 @@ int main(int argc, char *argv[]) std::string test_images(argv[4]); //trainer - od::g2d::ODHOGTrainer trainer("", trained_data_dir); + od::g2d::HOGTrainer trainer("", trained_data_dir); trainer.setPosSamplesDir(pos_samples); trainer.setNegSamplesDir(neg_samples); trainer.setNOFeaturesNeg(10); trainer.setTrainHardNegetive(true); trainer.train(); - od::g2d::ODHOGDetector detector; + od::g2d::HOGDetector detector; detector.setTrainedDataLocation(trained_data_dir); detector.init(); //get scenes - od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(test_images); + od::FrameGenerator<od::SceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(test_images); //GUI - od::ODViewer viewer; + od::Viewer viewer; viewer.initCVWindow(std::string("Overlay")); while(frameGenerator.isValid() && viewer.wait(10) != 27) { - boost::shared_ptr<od::ODSceneImage> scene = frameGenerator.getNextFrame(); + boost::shared_ptr<od::SceneImage> scene = frameGenerator.getNextFrame(); //Detect - boost::shared_ptr<od::ODDetections2D> detections = detector.detectOmni(scene); + boost::shared_ptr<od::Detections2D> detections = detector.detectOmni(scene); if(detections->size() > 0) viewer.render(detections->renderMetainfo(*scene), "Overlay"); diff --git a/examples/objectdetector/od_image_cadrecog_camera.cpp b/examples/objectdetector/image_cadrecog_camera.cpp similarity index 77% rename from examples/objectdetector/od_image_cadrecog_camera.cpp rename to examples/objectdetector/image_cadrecog_camera.cpp index b6165bb1..f5f0c0aa 100644 --- a/examples/objectdetector/od_image_cadrecog_camera.cpp +++ b/examples/objectdetector/image_cadrecog_camera.cpp @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -30,10 +30,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -#include "od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h" -#include "od/detectors/local2D/detection/ODCADRecognizer2DLocal.h" -#include "od/common/utils/ODFrameGenerator.h" -#include "od/common/utils/ODViewer.h" +#include "od/detectors/local2D/training/CADRecogTrainerSnapshotBased.h" +#include "od/detectors/local2D/detection/CADRecognizer2DLocal.h" +#include "od/common/utils/FrameGenerator.h" +#include "od/common/utils/Viewer.h" #include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) @@ -48,29 +48,29 @@ int main(int argc, char *argv[]) std::string trained_data_dir(argv[2]); //trainer - //TODO CHECK WHY COMMENTED - od::l2d::ODCADRecogTrainerSnapshotBased trainer(training_input_dir, trained_data_dir); + //TO CHECK WHY COMMENTED + od::l2d::CADRecogTrainerSnapshotBased trainer(training_input_dir, trained_data_dir); //trainer->train(); //detector - od::l2d::ODCADRecognizer2DLocal detector(trained_data_dir); + od::l2d::CADRecognizer2DLocal detector(trained_data_dir); //set commandline options type inputs detector.parseParameterString("--use_gpu --fast --method=1 --error=2 --confidence=0.9 --iterations=500 --inliers=20 --metainfo"); detector.setCameraIntrinsicFile("image_local_scenes/camera_webcam_fixed.xml"); //set some other inputs detector.init(); //get scenes - od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_DEVICE> frameGenerator(0); + od::FrameGenerator<od::SceneImage, od::GENERATOR_TYPE_DEVICE> frameGenerator(0); //GUI - od::ODViewer viewer; + od::Viewer viewer; viewer.initCVWindow(std::string("Overlay")); while(frameGenerator.isValid() && viewer.wait(10) != 27) { - boost::shared_ptr<od::ODSceneImage> scene = frameGenerator.getNextFrame(); + boost::shared_ptr<od::SceneImage> scene = frameGenerator.getNextFrame(); //Detect - boost::shared_ptr<ODDetections3D> detections = detector.detectOmni(scene); + boost::shared_ptr<Detections3D> detections = detector.detectOmni(scene); if(detections->size() > 0) viewer.update(detections->getMetainfoImage(), "Overlay"); diff --git a/examples/objectdetector/od_image_cadrecog_files.cpp b/examples/objectdetector/image_cadrecog_files.cpp similarity index 77% rename from examples/objectdetector/od_image_cadrecog_files.cpp rename to examples/objectdetector/image_cadrecog_files.cpp index 075bacee..67842aea 100644 --- a/examples/objectdetector/od_image_cadrecog_files.cpp +++ b/examples/objectdetector/image_cadrecog_files.cpp @@ -18,17 +18,17 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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. */ -#include "od/detectors/local2D/training/ODCADRecogTrainerSnapshotBased.h" -#include "od/detectors/local2D/detection/ODCADRecognizer2DLocal.h" -#include "od/common/utils/ODFrameGenerator.h" -#include "od/common/utils/ODViewer.h" +#include "od/detectors/local2D/training/CADRecogTrainerSnapshotBased.h" +#include "od/detectors/local2D/detection/CADRecognizer2DLocal.h" +#include "od/common/utils/FrameGenerator.h" +#include "od/common/utils/Viewer.h" #include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) @@ -44,29 +44,29 @@ int main(int argc, char *argv[]) std::string query_images(argv[3]); //trainer - od::l2d::ODCADRecogTrainerSnapshotBased trainer(training_input_dir, trained_data_dir); + od::l2d::CADRecogTrainerSnapshotBased trainer(training_input_dir, trained_data_dir); //trainer->train(); //detector - od::l2d::ODCADRecognizer2DLocal detector(trained_data_dir); + od::l2d::CADRecognizer2DLocal detector(trained_data_dir); //set commandline options type inputs detector.parseParameterString("--use_gpu --method=1 --error=2 --confidence=0.8 --iterations=500 --inliers=20 --metainfo"); detector.setCameraIntrinsicFile("/home/sarkar/models/opendetection_local/image_local_scenes/camera_householdnew.xml"); //set some other inputs detector.init(); //get scenes - od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(query_images); + od::FrameGenerator<od::SceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(query_images); //GUI - od::ODViewer viewer; + od::Viewer viewer; viewer.initCVWindow(std::string("Overlay")); while(frameGenerator.isValid() && viewer.wait(10) != 27) { - boost::shared_ptr<od::ODSceneImage> scene = frameGenerator.getNextFrame(); + boost::shared_ptr<od::SceneImage> scene = frameGenerator.getNextFrame(); cv::imshow("Overlay", scene->getCVImage()); //Detect - boost::shared_ptr<od::ODDetections3D> detections = detector.detectOmni(scene); + boost::shared_ptr<od::Detections3D> detections = detector.detectOmni(scene); if(detections->size() > 0) viewer.update(detections->getMetainfoImage(), "Overlay"); diff --git a/examples/objectdetector/od_image_customhog.cpp b/examples/objectdetector/image_customhog.cpp similarity index 77% rename from examples/objectdetector/od_image_customhog.cpp rename to examples/objectdetector/image_customhog.cpp index 9cd73724..9aed6a52 100644 --- a/examples/objectdetector/od_image_customhog.cpp +++ b/examples/objectdetector/image_customhog.cpp @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -31,9 +31,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "od/detectors/global2D/detection/ODHOGDetector.h" -#include "od/common/utils/ODFrameGenerator.h" -#include "od/common/utils/ODViewer.h" +#include "od/detectors/global2D/detection/HOGDetector.h" +#include "od/common/utils/FrameGenerator.h" +#include "od/common/utils/Viewer.h" #include <boost/shared_ptr.hpp> int main(int argc, char * argv[]) @@ -46,22 +46,22 @@ int main(int argc, char * argv[]) std::string trained_data_dir(argv[1]); //detector - od::g2d::ODHOGDetector detector(trained_data_dir); - detector.setSvmtype(od::g2d::ODHOGDetector::OD_FILE); + od::g2d::HOGDetector detector(trained_data_dir); + detector.setSvmtype(od::g2d::HOGDetector::_FILE); detector.init(); //get scenes - od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_DEVICE> frameGenerator(0); + od::FrameGenerator<od::SceneImage, od::GENERATOR_TYPE_DEVICE> frameGenerator(0); - od::ODViewer viewer; + od::Viewer viewer; viewer.initCVWindow(std::string("Overlay")); while(frameGenerator.isValid() && viewer.wait(10) != 27) { - boost::shared_ptr<od::ODSceneImage> scene = frameGenerator.getNextFrame(); + boost::shared_ptr<od::SceneImage> scene = frameGenerator.getNextFrame(); //Detect - boost::shared_ptr<od::ODDetections2D> detections = detector.detectOmni(scene); + boost::shared_ptr<od::Detections2D> detections = detector.detectOmni(scene); if(detections->size() > 0) viewer.update(detections->renderMetainfo(*scene), "Overlay"); diff --git a/examples/objectdetector/od_image_facerecog.cpp b/examples/objectdetector/image_facerecog.cpp similarity index 79% rename from examples/objectdetector/od_image_facerecog.cpp rename to examples/objectdetector/image_facerecog.cpp index 33aada69..d505b87f 100644 --- a/examples/objectdetector/od_image_facerecog.cpp +++ b/examples/objectdetector/image_facerecog.cpp @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -30,9 +30,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "od/detectors/global2D/ODFaceRecognizer.h" -#include "od/common/utils/ODFrameGenerator.h" -#include "od/common/utils/ODViewer.h" +#include "od/detectors/global2D/FaceRecognizer.h" +#include "od/common/utils/FrameGenerator.h" +#include "od/common/utils/Viewer.h" #include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) @@ -47,26 +47,26 @@ int main(int argc, char *argv[]) std::string trained_xml(argv[2]); std::string query_images(argv[3]); //detector - od::g2d::ODFaceRecognizer objdetector ; + od::g2d::FaceRecognizer objdetector ; objdetector.setTrainingInputLocation(training_input_csv); objdetector.setTrainingDataLocation(trained_xml); - objdetector.setRecogtype(od::g2d::ODFaceRecognizer::OD_FACE_FISCHER); + objdetector.setRecogtype(od::g2d::FaceRecognizer::_FACE_FISCHER); objdetector.initTrainer(); objdetector.train(); objdetector.initDetector(); //get scenes - od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(query_images); + od::FrameGenerator<od::SceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(query_images); - od::ODViewer viewer; + od::Viewer viewer; viewer.initCVWindow(std::string("Overlay")); while(frameGenerator.isValid() && viewer.wait(10) != 27) { - boost::shared_ptr<od::ODSceneImage> scene = frameGenerator.getNextFrame(); + boost::shared_ptr<od::SceneImage> scene = frameGenerator.getNextFrame(); //Detect - boost::shared_ptr<od::ODDetections> detections = objdetector.detect(scene); + boost::shared_ptr<od::Detections> detections = objdetector.detect(scene); (*detections)[0]->printSelf(); viewer.update(scene, "Overlay"); diff --git a/examples/objectdetector/od_image_hog_files.cpp b/examples/objectdetector/image_hog_files.cpp similarity index 76% rename from examples/objectdetector/od_image_hog_files.cpp rename to examples/objectdetector/image_hog_files.cpp index acf8b393..b672359d 100644 --- a/examples/objectdetector/od_image_hog_files.cpp +++ b/examples/objectdetector/image_hog_files.cpp @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -29,10 +29,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -#include "od/detectors/global2D/detection/ODCascadeDetector.h" -#include "od/detectors/global2D/detection/ODHOGDetector.h" -#include "od/common/utils/ODFrameGenerator.h" -#include "od/common/utils/ODViewer.h" +#include "od/detectors/global2D/detection/CascadeDetector.h" +#include "od/detectors/global2D/detection/HOGDetector.h" +#include "od/common/utils/FrameGenerator.h" +#include "od/common/utils/Viewer.h" #include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) @@ -47,23 +47,23 @@ int main(int argc, char *argv[]) std::string images(argv[2]); //detector - od::g2d::ODHOGDetector detector ; + od::g2d::HOGDetector detector ; detector.setTrainedDataLocation(trained_hog); - //detector->setSvmtype(g2d::ODHOGDetector::OD_DAIMLER_PEOPLE); + //detector->setSvmtype(g2d::HOGDetector::_DAIMLER_PEOPLE); detector.init(); //get scenes - od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(images); + od::FrameGenerator<od::SceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(images); - od::ODViewer viewer; + od::Viewer viewer; viewer.initCVWindow(std::string("Overlay")); while(frameGenerator.isValid() && viewer.wait(10) != 27) { - boost::shared_ptr<od::ODSceneImage> scene = frameGenerator.getNextFrame(); + boost::shared_ptr<od::SceneImage> scene = frameGenerator.getNextFrame(); //Detect - boost::shared_ptr<od::ODDetections2D> detections = detector.detectOmni(scene); + boost::shared_ptr<od::Detections2D> detections = detector.detectOmni(scene); if(detections->size() > 0) viewer.update(detections->renderMetainfo(*scene), "Overlay"); diff --git a/examples/objectdetector/od_multialgo_files.cpp b/examples/objectdetector/multialgo_files.cpp similarity index 77% rename from examples/objectdetector/od_multialgo_files.cpp rename to examples/objectdetector/multialgo_files.cpp index 56d0e2db..f3eb530d 100644 --- a/examples/objectdetector/od_multialgo_files.cpp +++ b/examples/objectdetector/multialgo_files.cpp @@ -18,16 +18,16 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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. */ -#include "od/detectors/misc/detection/ODDetectorMultiAlgo.hpp" -#include "od/common/utils/ODFrameGenerator.h" -#include "od/common/utils/ODViewer.h" +#include "od/detectors/misc/detection/DetectorMultiAlgo.hpp" +#include "od/common/utils/FrameGenerator.h" +#include "od/common/utils/Viewer.h" #include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) @@ -42,21 +42,21 @@ int main(int argc, char *argv[]) std::string query_images(argv[2]); //detector - od::ODDetectorMultiAlgo2D<pcl::PointXYZRGBA> detector(trained_data_dir); + od::DetectorMultiAlgo2D<pcl::PointXYZRGBA> detector(trained_data_dir); detector.init(); //get scenes - od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(query_images); + od::FrameGenerator<od::SceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(query_images); - od::ODViewer viewer; + od::Viewer viewer; viewer.initCVWindow(std::string("Overlay")); while(frameGenerator.isValid() && viewer.wait(10) != 27) { - boost::shared_ptr<od::ODSceneImage> scene = frameGenerator.getNextFrame(); + boost::shared_ptr<od::SceneImage> scene = frameGenerator.getNextFrame(); //Detect - boost::shared_ptr<od::ODDetections2D> detections = detector.detectOmni(scene); + boost::shared_ptr<od::Detections2D> detections = detector.detectOmni(scene); if(detections->size() > 0) viewer.render(detections->renderMetainfo(*scene), "Overlay"); diff --git a/examples/objectdetector/od_multialgo_pc.cpp b/examples/objectdetector/multialgo_pc.cpp similarity index 83% rename from examples/objectdetector/od_multialgo_pc.cpp rename to examples/objectdetector/multialgo_pc.cpp index 59a62f2f..0c51bfe8 100644 --- a/examples/objectdetector/od_multialgo_pc.cpp +++ b/examples/objectdetector/multialgo_pc.cpp @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -31,10 +31,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -#include "od/detectors/misc/detection/ODDetectorMultiAlgo.hpp" -#include "od/detectors/global3D/ODPointCloudGlobalMatching.h" -#include "od/common/utils/ODFrameGenerator.h" -#include "od/common/utils/ODViewer.h" +#include "od/detectors/misc/detection/DetectorMultiAlgo.hpp" +#include "od/detectors/global3D/PointCloudGlobalMatching.h" +#include "od/common/utils/FrameGenerator.h" +#include "od/common/utils/Viewer.h" #include <string> #include <boost/shared_ptr.hpp> @@ -50,23 +50,23 @@ int main(int argc, char *argv[]) std::string trained_data_dir(argv[2]); //detector - od::ODDetectorMultiAlgo<pcl::PointXYZRGBA> detector(trained_data_dir); + od::DetectorMultiAlgo<pcl::PointXYZRGBA> detector(trained_data_dir); detector.setTrainingInputLocation(training_input_dir); detector.init(); //GUI and feedback - od::ODViewer viewer; + od::Viewer viewer; viewer.initPCLWindow(std::string("kinect")); size_t previous_cluster_size = 0; - od::ODFrameGenerator<od::ODScenePointCloud<pcl::PointXYZRGBA>, od::GENERATOR_TYPE_DEVICE> frameGenerator; + od::FrameGenerator<od::ScenePointCloud<pcl::PointXYZRGBA>, od::GENERATOR_TYPE_DEVICE> frameGenerator; std::stringstream cluster_name; pcl::PointXYZ pos; while(frameGenerator.isValid()) { - boost::shared_ptr<od::ODScenePointCloud<pcl::PointXYZRGBA>> frame = frameGenerator.getNextFrame(); + boost::shared_ptr<od::ScenePointCloud<pcl::PointXYZRGBA>> frame = frameGenerator.getNextFrame(); //remove previous point clouds and text and add new ones in the visualizer viewer.remove("frame"); @@ -85,7 +85,7 @@ int main(int argc, char *argv[]) //Detect - boost::shared_ptr<od::ODDetections3D> detections = detector.detectOmni(frame); + boost::shared_ptr<od::Detections3D> detections = detector.detectOmni(frame); //add all the detections in the visualizer with its id as text for(size_t i = 0; i < detections->size (); ++i) diff --git a/examples/objectdetector/od_pc_global.cpp b/examples/objectdetector/pc_global.cpp similarity index 81% rename from examples/objectdetector/od_pc_global.cpp rename to examples/objectdetector/pc_global.cpp index d07c1f3d..b951e8d1 100644 --- a/examples/objectdetector/od_pc_global.cpp +++ b/examples/objectdetector/pc_global.cpp @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -33,7 +33,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -#include "od/detectors/global3D/ODPointCloudGlobalMatching.h" +#include "od/detectors/global3D/PointCloudGlobalMatching.h" #include <boost/shared_ptr.hpp> #include <iostream> @@ -50,19 +50,19 @@ int main(int argc, char *argv[]) std::string pointcloud_file(argv[3]); //trainer - od::g3d::ODCADDetectTrainer3DGlobal trainer(training_input_dir, trained_data_dir); + od::g3d::CADDetectTrainer3DGlobal trainer(training_input_dir, trained_data_dir); trainer.train(); //detector - od::g3d::ODCADDetector3DGlobal<pcl::PointXYZRGBA> detector ; + od::g3d::CADDetector3DGlobal<pcl::PointXYZRGBA> detector ; detector.setTrainingInputLocation(training_input_dir); detector.setTrainedDataLocation(trained_data_dir); detector.init(); //Get a scene - boost::shared_ptr<od::ODScenePointCloud<pcl::PointXYZRGBA> > scene = boost::make_shared<od::ODScenePointCloud<pcl::PointXYZRGBA>>(pointcloud_file); + boost::shared_ptr<od::ScenePointCloud<pcl::PointXYZRGBA> > scene = boost::make_shared<od::ScenePointCloud<pcl::PointXYZRGBA>>(pointcloud_file); - boost::shared_ptr<od::ODDetections3D> detections = detector.detectOmni(scene); + boost::shared_ptr<od::Detections3D> detections = detector.detectOmni(scene); //feedback for(size_t i = 0; i < detections->size(); ++i) diff --git a/examples/objectdetector/od_pc_global_files.cpp b/examples/objectdetector/pc_global_files.cpp similarity index 78% rename from examples/objectdetector/od_pc_global_files.cpp rename to examples/objectdetector/pc_global_files.cpp index 55d29ab1..85c16d1c 100644 --- a/examples/objectdetector/od_pc_global_files.cpp +++ b/examples/objectdetector/pc_global_files.cpp @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -33,8 +33,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -#include "od/common/utils/ODFrameGenerator.h" -#include "od/detectors/global3D/ODPointCloudGlobalMatching.h" +#include "od/common/utils/FrameGenerator.h" +#include "od/detectors/global3D/PointCloudGlobalMatching.h" #include <boost/shared_ptr.hpp> #include <vector> @@ -52,24 +52,24 @@ int main(int argc, char *argv[]) std::string pointcloud_file(argv[3]); //trainer - od::g3d::ODCADDetectTrainer3DGlobal trainer(training_input_dir, trained_data_dir); + od::g3d::CADDetectTrainer3DGlobal trainer(training_input_dir, trained_data_dir); trainer.train(); //detector - od::g3d::ODCADDetector3DGlobal<pcl::PointXYZRGBA> detector; + od::g3d::CADDetector3DGlobal<pcl::PointXYZRGBA> detector; detector.setTrainingInputLocation(training_input_dir); detector.setTrainedDataLocation(trained_data_dir); detector.init(); //Get a scene - od::ODFrameGenerator<od::ODScenePointCloud<pcl::PointXYZRGBA>, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(pointcloud_file); + od::FrameGenerator<od::ScenePointCloud<pcl::PointXYZRGBA>, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(pointcloud_file); while(frameGenerator.isValid()) { //get frame - boost::shared_ptr<od::ODScenePointCloud<pcl::PointXYZRGBA>> frame = frameGenerator.getNextFrame(); + boost::shared_ptr<od::ScenePointCloud<pcl::PointXYZRGBA>> frame = frameGenerator.getNextFrame(); - boost::shared_ptr<od::ODDetections3D> detections = detector.detectOmni(frame); + boost::shared_ptr<od::Detections3D> detections = detector.detectOmni(frame); for(size_t i = 0; i < detections->size(); ++i) { diff --git a/examples/objectdetector/od_pc_global_real_time.cpp b/examples/objectdetector/pc_global_real_time.cpp similarity index 81% rename from examples/objectdetector/od_pc_global_real_time.cpp rename to examples/objectdetector/pc_global_real_time.cpp index 5e3f8806..c66f913d 100644 --- a/examples/objectdetector/od_pc_global_real_time.cpp +++ b/examples/objectdetector/pc_global_real_time.cpp @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -34,9 +34,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "od/detectors/global3D/ODPointCloudGlobalMatching.h" -#include "od/common/utils/ODFrameGenerator.h" -#include "od/common/utils/ODViewer.h" +#include "od/detectors/global3D/PointCloudGlobalMatching.h" +#include "od/common/utils/FrameGenerator.h" +#include "od/common/utils/Viewer.h" #include <string> #include <boost/shared_ptr.hpp> @@ -52,26 +52,26 @@ int main(int argc, char *argv[]) std::string trained_data_dir(argv[2]); //trainer - od::g3d::ODCADDetectTrainer3DGlobal trainer(training_input_dir, trained_data_dir); + od::g3d::CADDetectTrainer3DGlobal trainer(training_input_dir, trained_data_dir); trainer.train(); //detector - od::g3d::ODCADDetector3DGlobal<pcl::PointXYZRGBA> detector;; + od::g3d::CADDetector3DGlobal<pcl::PointXYZRGBA> detector;; detector.setTrainingInputLocation(training_input_dir); detector.setTrainedDataLocation(trained_data_dir); detector.init(); //GUI and feedback - od::ODViewer viewer; + od::Viewer viewer; viewer.initPCLWindow(std::string("kinect")); - od::ODFrameGenerator<od::ODScenePointCloud<pcl::PointXYZRGBA>, od::GENERATOR_TYPE_DEVICE> frameGenerator; + od::FrameGenerator<od::ScenePointCloud<pcl::PointXYZRGBA>, od::GENERATOR_TYPE_DEVICE> frameGenerator; pcl::PointXYZ pos; while(frameGenerator.isValid()) { - boost::shared_ptr<od::ODScenePointCloud<pcl::PointXYZRGBA>> frame = frameGenerator.getNextFrame(); + boost::shared_ptr<od::ScenePointCloud<pcl::PointXYZRGBA>> frame = frameGenerator.getNextFrame(); //remove previous point clouds and text and add new ones in the visualizer viewer.removeAll(); @@ -79,7 +79,7 @@ int main(int argc, char *argv[]) viewer.render(frame->getPointCloud(), std::string("frame")); //Detect - boost::shared_ptr<od::ODDetections3D> detections = detector.detectOmni(frame); + boost::shared_ptr<od::Detections3D> detections = detector.detectOmni(frame); //add all the detections in the visualizer with its id as text for(size_t i = 0; i < detections->size(); ++i) diff --git a/gpu/common/CMakeLists.txt b/gpu/common/CMakeLists.txt index 85aea3f2..48143d5a 100644 --- a/gpu/common/CMakeLists.txt +++ b/gpu/common/CMakeLists.txt @@ -9,7 +9,7 @@ if(WITH_GPU) set(SUBSYS_DEPS siftgpu) set(SOURCES - "src/utils/ODFeatureDetector2D.cpp" + "src/utils/FeatureDetector2D.cpp" ) include_directories(${GPU_COMMON_INCLUDE_DIR}) diff --git a/gpu/common/include/od/gpu/common/utils/ODFeatureDetector2D.h b/gpu/common/include/od/gpu/common/utils/FeatureDetector2D.h similarity index 83% rename from gpu/common/include/od/gpu/common/utils/ODFeatureDetector2D.h rename to gpu/common/include/od/gpu/common/utils/FeatureDetector2D.h index 57d0e6fa..1529fb98 100644 --- a/gpu/common/include/od/gpu/common/utils/ODFeatureDetector2D.h +++ b/gpu/common/include/od/gpu/common/utils/FeatureDetector2D.h @@ -2,7 +2,7 @@ // Created by sarkar on 20.04.15. // #pragma once -#include "od/common/utils/ODFeatureDetectorInterface.h" +#include "od/common/utils/FeatureDetectorInterface.h" #include <opencv2/cudafeatures2d.hpp> #include <GL/gl.h> #include "SiftGPU.h" @@ -14,12 +14,12 @@ namespace od namespace gpu { - class ODFeatureDetector2D : public ODFeatureDetectorIterface + class FeatureDetector2D : public FeatureDetectorIterface { public: - ODFeatureDetector2D(FeatureType type); + FeatureDetector2D(FeatureType type); void computeKeypointsAndDescriptors(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints); diff --git a/gpu/common/src/utils/ODFeatureDetector2D.cpp b/gpu/common/src/utils/FeatureDetector2D.cpp similarity index 86% rename from gpu/common/src/utils/ODFeatureDetector2D.cpp rename to gpu/common/src/utils/FeatureDetector2D.cpp index 5a985539..ee931c46 100644 --- a/gpu/common/src/utils/ODFeatureDetector2D.cpp +++ b/gpu/common/src/utils/FeatureDetector2D.cpp @@ -2,7 +2,7 @@ // Created by sarkar on 20.04.15. // -#include "od/gpu/common/utils/ODFeatureDetector2D.h" +#include "od/gpu/common/utils/FeatureDetector2D.h" namespace od { @@ -11,7 +11,7 @@ namespace od { - ODFeatureDetector2D::ODFeatureDetector2D(FeatureType type) + FeatureDetector2D::FeatureDetector2D(FeatureType type) { mode_ = type; @@ -45,7 +45,7 @@ namespace od } } - void ODFeatureDetector2D::computeKeypointsAndDescriptors(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) + void FeatureDetector2D::computeKeypointsAndDescriptors(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) { if(mode_ == SIFT_GPU) { findSiftGPUDescriptors_(image, descriptors, keypoints); @@ -64,7 +64,7 @@ namespace od memcpy(siftImage, grey.data, image.rows * image.cols); } - void ODFeatureDetector2D::findSiftGPUDescriptors_(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) + void FeatureDetector2D::findSiftGPUDescriptors_(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) { unsigned char * data = image.data; cv::Mat greyimage; @@ -101,7 +101,7 @@ namespace od } - void ODFeatureDetector2D::findSiftGPUDescriptors(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) + void FeatureDetector2D::findSiftGPUDescriptors(const cv::Mat & image, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) { unsigned char *data = image.data; cv::Mat greyimage; @@ -137,7 +137,7 @@ namespace od } - void ODFeatureDetector2D::findSiftGPUDescriptors(const std::string & image_name, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) + void FeatureDetector2D::findSiftGPUDescriptors(const std::string & image_name, cv::Mat & descriptors, std::vector<cv::KeyPoint> & keypoints) { sift_gpu_->RunSIFT(image_name.c_str()); @@ -165,7 +165,7 @@ namespace od cv::Mat image = cv::imread(image_name); } - void ODFeatureDetector2D::computeAndSave(const cv::Mat & image, const std::string & path) + void FeatureDetector2D::computeAndSave(const cv::Mat & image, const std::string & path) { cv::Mat descriptors; std::vector<cv::KeyPoint> keypoints; diff --git a/gpu/detectors/include/od/gpu/detectors/global2D/detection/ODCascadeDetectorImpl.h b/gpu/detectors/include/od/gpu/detectors/global2D/detection/CascadeDetectorImpl.h similarity index 79% rename from gpu/detectors/include/od/gpu/detectors/global2D/detection/ODCascadeDetectorImpl.h rename to gpu/detectors/include/od/gpu/detectors/global2D/detection/CascadeDetectorImpl.h index 9757222e..03208c48 100644 --- a/gpu/detectors/include/od/gpu/detectors/global2D/detection/ODCascadeDetectorImpl.h +++ b/gpu/detectors/include/od/gpu/detectors/global2D/detection/CascadeDetectorImpl.h @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -27,11 +27,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 17.07.15. // #pragma once -#include "od/common/pipeline/ODDetector.h" -#include "od/common/pipeline/ODScene.h" -#include "od/common/utils/ODUtils.h" -#include "od/common/utils/ODFeatureDetector2D.h" -#include "od/detectors/global2D/detection/ODCascadeDetectorInterface.h" +#include "od/common/pipeline/Detector.h" +#include "od/common/pipeline/Scene.h" +#include "od/common/utils/Utils.h" +#include "od/common/utils/FeatureDetector2D.h" +#include "od/detectors/global2D/detection/CascadeDetectorInterface.h" #include <opencv2/cudaobjdetect.hpp> #include <opencv2/cudaimgproc.hpp> @@ -47,7 +47,7 @@ namespace od namespace g2d { /** \brief A class for detection using Cascade classifiers. - * Given a scene and a cascade classifier, this class performs a classification and returns detections. The training is not supported in OD currently but is compatible to the cascade training of OpenCV. + * Given a scene and a cascade classifier, this class performs a classification and returns detections. The training is not supported in currently but is compatible to the cascade training of OpenCV. * Train your cascade classifiers using OpenCV's *opencv_traincascade* utility (http://docs.opencv.org/master/dc/d88/tutorial_traincascade.html#gsc.tab=0). It is a great tool for traning your cascade. * Paste the generated xml in trained_data_location_/TD_CASCADE/\*.cascade.xml to use your trained cascade. * @@ -55,19 +55,19 @@ namespace od * */ - class ODCascadeDetectorImpl : public ODCascadeDetectorInterface + class CascadeDetectorImpl : public CascadeDetectorInterface { public: - ODCascadeDetectorImpl(const std::string & trainer = std::string("cascade.xml"), const std::string & trained_data_location = std::string(""), double scale_factor = 1.1, int min_neighbors = 3, + CascadeDetectorImpl(const std::string & trainer = std::string("cascade.xml"), const std::string & trained_data_location = std::string(""), double scale_factor = 1.1, int min_neighbors = 3, int flags = 0, const cv::Size & min_size = cv::Size(), const cv::Size & max_size = cv::Size()); void init(); - shared_ptr<ODDetections2D> detectOmni(shared_ptr<ODSceneImage> scene); - shared_ptr<ODDetections> detect(shared_ptr<ODSceneImage> scene); + shared_ptr<Detections2D> detectOmni(shared_ptr<SceneImage> scene); + shared_ptr<Detections> detect(shared_ptr<SceneImage> scene); - shared_ptr<ODDetections> detectOmni(shared_ptr<ODScene> scene); + shared_ptr<Detections> detectOmni(shared_ptr<Scene> scene); void setScale(const float scale); float getScale() const; diff --git a/gpu/detectors/src/global2D/CMakeLists.txt b/gpu/detectors/src/global2D/CMakeLists.txt index c2858916..4bb364ad 100644 --- a/gpu/detectors/src/global2D/CMakeLists.txt +++ b/gpu/detectors/src/global2D/CMakeLists.txt @@ -5,12 +5,13 @@ set(SUBSYS_DESC "global gpu feature based detection in 2D images") set(SUBSYS_DEPS od_common) set(SOURCES - "detection/ODCascadeDetectorImpl.cpp" + "detection/CascadeDetectorImpl.cpp" ) include_directories(${GPU_DETECTORS_INCLUDE_DIR}) include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) +include_directories(${COMMON_IMPL_DIR}) include_directories(${OpenCV_INCLUDE_DIRS}) include_directories(${PCL_INCLUDE_DIRS}) diff --git a/gpu/detectors/src/global2D/detection/ODCascadeDetectorImpl.cpp b/gpu/detectors/src/global2D/detection/CascadeDetectorImpl.cpp similarity index 75% rename from gpu/detectors/src/global2D/detection/ODCascadeDetectorImpl.cpp rename to gpu/detectors/src/global2D/detection/CascadeDetectorImpl.cpp index 5f0db01a..74812ec9 100644 --- a/gpu/detectors/src/global2D/detection/ODCascadeDetectorImpl.cpp +++ b/gpu/detectors/src/global2D/detection/CascadeDetectorImpl.cpp @@ -18,7 +18,7 @@ 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 COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOS 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 @@ -27,7 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Created by sarkar on 17.07.15. // -#include "od/gpu/detectors/global2D/detection/ODCascadeDetectorImpl.h" +#include "od/gpu/detectors/global2D/detection/CascadeDetectorImpl.h" namespace od { @@ -38,24 +38,24 @@ namespace od namespace g2d { - ODCascadeDetectorImpl::ODCascadeDetectorImpl(const std::string & trainer, const std::string & trained_data_location, double scale_factor, int min_neighbors, int flags, + CascadeDetectorImpl::CascadeDetectorImpl(const std::string & trainer, const std::string & trained_data_location, double scale_factor, int min_neighbors, int flags, const cv::Size & min_size, const cv::Size & max_size): trained_data_location_(trained_data_location), scale_factor_(scale_factor), min_neighbors_(min_neighbors), min_size_(min_size), max_size_(max_size), trained_data_id_(trainer), meta_info_(true), largest_(true) {} - shared_ptr<ODDetections> ODCascadeDetectorImpl::detectOmni(shared_ptr<ODScene> scene) + shared_ptr<Detections> CascadeDetectorImpl::detectOmni(shared_ptr<Scene> scene) { std::cout << "not implemented, use detect()" <<std::endl; return nullptr; } - void ODCascadeDetectorImpl::init() + void CascadeDetectorImpl::init() { haar_cascade_ = cv::cuda::CascadeClassifier::create(trained_data_id_); } - shared_ptr<ODDetections2D> ODCascadeDetectorImpl::detectOmni(shared_ptr<ODSceneImage> scene) + shared_ptr<Detections2D> CascadeDetectorImpl::detectOmni(shared_ptr<SceneImage> scene) { if(!haar_cascade_){ std::cout << "Call init() first!" << std::endl; @@ -80,12 +80,12 @@ namespace od haar_cascade_->convert(buf_, objects_); //always create detections - shared_ptr<ODDetections2D> detections = make_shared<ODDetections2D>(); + shared_ptr<Detections2D> detections = make_shared<Detections2D>(); cv::Mat viz = scene->getCVImage().clone(); for(auto & o : objects_) { // Process face by face: - shared_ptr<ODDetection2D> detection2D = make_shared<ODDetection2D>(ODDetection::OD_CLASSIFICATION, "OBJ", 1); + shared_ptr<Detection2D> detection2D = make_shared<Detection2D>(Detection::CLASSIFICATION, "OBJ", 1); detection2D->setBoundingBox(o); detections->push_back(detection2D); @@ -99,7 +99,7 @@ namespace od return detections; } - shared_ptr<ODDetections> ODCascadeDetectorImpl::detect(shared_ptr<ODSceneImage> scene) + shared_ptr<Detections> CascadeDetectorImpl::detect(shared_ptr<SceneImage> scene) { if(!haar_cascade_){ @@ -125,54 +125,54 @@ namespace od haar_cascade_->convert(buf_, objects_); //always create detections - shared_ptr<ODDetections> detections = make_shared<ODDetections>(); + shared_ptr<Detections> detections = make_shared<Detections>(); //hack for single detection, //note: maxsize = minsize = size of input image for single window detection //todo: implement in some other way of fast single detection; currently this will work, but maynot be fast if(objects_.size() > 0) { - detections->push_back(make_shared<ODDetection>(ODDetection::OD_CLASSIFICATION, "OBJ", 1)); + detections->push_back(make_shared<Detection>(Detection::CLASSIFICATION, "OBJ", 1)); } return detections; } - void ODCascadeDetectorImpl::setScale(const float scale) + void CascadeDetectorImpl::setScale(const float scale) { scale_factor_ = scale; } - float ODCascadeDetectorImpl::getScale() const + float CascadeDetectorImpl::getScale() const { return scale_factor_; } - void ODCascadeDetectorImpl::setMinNeighbors(const unsigned int min_neighbors) + void CascadeDetectorImpl::setMinNeighbors(const unsigned int min_neighbors) { min_neighbors_ = min_neighbors; } - unsigned int ODCascadeDetectorImpl::getMinNeighbors() const + unsigned int CascadeDetectorImpl::getMinNeighbors() const { return min_neighbors_; } - void ODCascadeDetectorImpl::setMinSize(const cv::Size & size) + void CascadeDetectorImpl::setMinSize(const cv::Size & size) { min_size_ = size; } - cv::Size ODCascadeDetectorImpl::getMinSize() const + cv::Size CascadeDetectorImpl::getMinSize() const { return min_size_; } - void ODCascadeDetectorImpl::setMaxSize(const cv::Size & size) + void CascadeDetectorImpl::setMaxSize(const cv::Size & size) { max_size_ = size; } - cv::Size ODCascadeDetectorImpl::getMaxSize() const + cv::Size CascadeDetectorImpl::getMaxSize() const { return max_size_; } From e7beff753ce81d7d47b9eb822755603f29a9f817 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Mon, 1 Aug 2016 15:40:36 +0200 Subject: [PATCH 60/79] adds abhishek caffe module --- CMakeLists.txt | 37 +- cmake/{ODConfig.cmake.in => Config.cmake.in} | 0 cmake/{ODDeb.cmake => Deb.cmake} | 0 .../{ODDependency.cmake => Dependency.cmake} | 16 + cmake/{ODMacros.cmake => Macros.cmake} | 0 cmake/{ODOptions.cmake => Options.cmake} | 7 + cmake/{ODTargets.cmake => Targets.cmake} | 0 cmake/Uninstall.cmake | 3 + ...DUninstall.cmake.in => Uninstall.cmake.in} | 0 cmake/{ODVersion.cmake => Version.cmake} | 0 cmake/{ODVersion.h.in => Version.h.in} | 0 .../global2D/detection/ConvClassification.h | 62 + .../global2D/localization/Convolve.h | 67 + .../global2D/localization/CreateModel_test.h | 597 ++++++ .../global2D/localization/Disjoint-set.h | 77 + .../detectors/global2D/localization/Filter.h | 97 + .../detectors/global2D/localization/Image.h | 98 + .../detectors/global2D/localization/Imconv.h | 175 ++ .../detectors/global2D/localization/Imutil.h | 63 + .../od/detectors/global2D/localization/Misc.h | 63 + .../detectors/global2D/localization/Pnmfile.h | 208 ++ .../global2D/localization/Segment-graph.h | 81 + .../global2D/localization/Segment-image.h | 152 ++ .../localization/SelectiveSearchBase.h | 67 + .../localization/SelectiveSearchModel.h | 95 + .../global2D/training/ActivationWindow.h | 458 +++++ .../detectors/global2D/training/ConvTrainer.h | 42 + .../global2D/training/CriticalWindow.h | 1240 ++++++++++++ .../global2D/training/DisplayWindow.h | 14 + .../detectors/global2D/training/ExtraWindow.h | 24 + .../detectors/global2D/training/LossWindow.h | 127 ++ .../detectors/global2D/training/MainWindow.h | 146 ++ .../od/detectors/global2D/training/Network.h | 239 +++ .../od/detectors/global2D/training/Node.h | 86 + .../global2D/training/NormalizationWindow.h | 345 ++++ .../od/detectors/global2D/training/Solver.h | 131 ++ detectors/src/global2D/CMakeLists.txt | 29 +- .../global2D/detection/ConvClassification.cpp | 122 ++ .../localization/SelectiveSearchBase.cpp | 145 ++ .../localization/SelectiveSearchModel.cpp | 604 ++++++ .../src/global2D/training/ConvTrainer.cpp | 53 + detectors/src/global2D/training/Network.cpp | 1667 +++++++++++++++++ detectors/src/global2D/training/Solver.cpp | 1001 ++++++++++ examples/apps/CMakeLists.txt | 5 +- examples/apps/version/opendetection.cpp | 2 +- examples/objectdetector/CMakeLists.txt | 6 +- 46 files changed, 8419 insertions(+), 32 deletions(-) rename cmake/{ODConfig.cmake.in => Config.cmake.in} (100%) rename cmake/{ODDeb.cmake => Deb.cmake} (100%) rename cmake/{ODDependency.cmake => Dependency.cmake} (57%) rename cmake/{ODMacros.cmake => Macros.cmake} (100%) rename cmake/{ODOptions.cmake => Options.cmake} (90%) rename cmake/{ODTargets.cmake => Targets.cmake} (100%) create mode 100644 cmake/Uninstall.cmake rename cmake/{ODUninstall.cmake.in => Uninstall.cmake.in} (100%) rename cmake/{ODVersion.cmake => Version.cmake} (100%) rename cmake/{ODVersion.h.in => Version.h.in} (100%) create mode 100644 detectors/include/od/detectors/global2D/detection/ConvClassification.h create mode 100755 detectors/include/od/detectors/global2D/localization/Convolve.h create mode 100644 detectors/include/od/detectors/global2D/localization/CreateModel_test.h create mode 100755 detectors/include/od/detectors/global2D/localization/Disjoint-set.h create mode 100755 detectors/include/od/detectors/global2D/localization/Filter.h create mode 100755 detectors/include/od/detectors/global2D/localization/Image.h create mode 100755 detectors/include/od/detectors/global2D/localization/Imconv.h create mode 100755 detectors/include/od/detectors/global2D/localization/Imutil.h create mode 100755 detectors/include/od/detectors/global2D/localization/Misc.h create mode 100755 detectors/include/od/detectors/global2D/localization/Pnmfile.h create mode 100755 detectors/include/od/detectors/global2D/localization/Segment-graph.h create mode 100755 detectors/include/od/detectors/global2D/localization/Segment-image.h create mode 100644 detectors/include/od/detectors/global2D/localization/SelectiveSearchBase.h create mode 100644 detectors/include/od/detectors/global2D/localization/SelectiveSearchModel.h create mode 100644 detectors/include/od/detectors/global2D/training/ActivationWindow.h create mode 100644 detectors/include/od/detectors/global2D/training/ConvTrainer.h create mode 100644 detectors/include/od/detectors/global2D/training/CriticalWindow.h create mode 100644 detectors/include/od/detectors/global2D/training/DisplayWindow.h create mode 100644 detectors/include/od/detectors/global2D/training/ExtraWindow.h create mode 100644 detectors/include/od/detectors/global2D/training/LossWindow.h create mode 100644 detectors/include/od/detectors/global2D/training/MainWindow.h create mode 100644 detectors/include/od/detectors/global2D/training/Network.h create mode 100644 detectors/include/od/detectors/global2D/training/Node.h create mode 100644 detectors/include/od/detectors/global2D/training/NormalizationWindow.h create mode 100644 detectors/include/od/detectors/global2D/training/Solver.h create mode 100644 detectors/src/global2D/detection/ConvClassification.cpp create mode 100644 detectors/src/global2D/localization/SelectiveSearchBase.cpp create mode 100644 detectors/src/global2D/localization/SelectiveSearchModel.cpp create mode 100644 detectors/src/global2D/training/ConvTrainer.cpp create mode 100644 detectors/src/global2D/training/Network.cpp create mode 100644 detectors/src/global2D/training/Solver.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index af93c4b8..0f8fe1eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,44 +4,37 @@ project(OpenDetection) # Initialize variables set(OD_SOURCE_DIR ${OpenDetection_SOURCE_DIR}) set(OD_BINARY_DIR ${OpenDetection_BINARY_DIR}) -set(OD_CMAKE_DIR ${OpenDetection_SOURCE_DIR}/cmake) +set(OD_CMAKE_DIR ${OD_SOURCE_DIR}/cmake) set(CMAKE_MODULE_PATH ${OD_CMAKE_DIR}/modules) set(CMAKE_3RDPARTY_DIR ${OD_SOURCE_DIR}/3rdparty) +set(CMAKE_OD_GENERATED_DIR ${OD_BINARY_DIR}/generated) # Initialize versioning -include(${OD_CMAKE_DIR}/ODVersion.cmake) +include(${OD_CMAKE_DIR}/Version.cmake) set(OD_VERSION ${OD_MAJOR_VERSION}.${OD_MINOR_VERSION}) set(OD_VERSION_DETAILED ${OD_MAJOR_VERSION}.${OD_MINOR_VERSION}.${OD_BUILD_VERSION}) # Set up macros and configurations -include(${OD_CMAKE_DIR}/ODOptions.cmake) -include(${OD_CMAKE_DIR}/ODTargets.cmake) -include(${OD_CMAKE_DIR}/ODDependency.cmake) -include(${OD_CMAKE_DIR}/ODMacros.cmake) -include(${OD_CMAKE_DIR}/ODDeb.cmake) +include(${OD_CMAKE_DIR}/Options.cmake) +include(${OD_CMAKE_DIR}/Targets.cmake) +include(${OD_CMAKE_DIR}/Dependency.cmake) +include(${OD_CMAKE_DIR}/Macros.cmake) +include(${OD_CMAKE_DIR}/Deb.cmake) +include(${OD_CMAKE_DIR}/Uninstall.cmake) # Prepare configuration files -configure_file(${OD_CMAKE_DIR}/ODVersion.h.in ${OD_BINARY_DIR}/generated/od_version.h) -install(FILES ${OD_BINARY_DIR}/generated/od_version.h DESTINATION ${OD_INSTALL_INCLUDE_DIR}) +configure_file(${OD_CMAKE_DIR}/Version.h.in ${CMAKE_OD_GENERATED_DIR}/version.h) +install(FILES ${CMAKE_OD_GENERATED_DIR}/version.h DESTINATION ${OD_INSTALL_INCLUDE_DIR}) # Set modules -set(OD_MODULES_NAMES 3rdparty common "gpu/common" detectors "gpu/detectors" doc examples) -set(OD_MODULES_DIRS ${OD_MODULES_NAMES}) +set(OD_MODULES_NAMES 3rdparty common gpu/common detectors gpu/detectors doc examples) # Add modules -foreach(subdir ${OD_MODULES_DIRS}) +foreach(subdir ${OD_MODULES_NAMES}) add_subdirectory(${OD_SOURCE_DIR}/${subdir}) endforeach(subdir) # Install the FindPackage configuration file get_property(OD_INSTALLED_LIBRARIES GLOBAL PROPERTY OD_INSTALLED_LIBRARIES) -configure_file(${OD_CMAKE_DIR}/ODConfig.cmake.in ${OD_BINARY_DIR}/ODConfig.cmake) -install(FILES ${OD_BINARY_DIR}/ODConfig.cmake DESTINATION ${OD_CMAKE_INSTALL_DIR}) - -# uninstall target -configure_file( - "${OD_CMAKE_DIR}/ODUninstall.cmake.in" - "${OD_BINARY_DIR}/cmake_uninstall.cmake" - IMMEDIATE @ONLY) - -add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) \ No newline at end of file +configure_file(${OD_CMAKE_DIR}/Config.cmake.in ${OD_BINARY_DIR}/Config.cmake) +install(FILES ${OD_BINARY_DIR}/Config.cmake DESTINATION ${OD_CMAKE_INSTALL_DIR}) diff --git a/cmake/ODConfig.cmake.in b/cmake/Config.cmake.in similarity index 100% rename from cmake/ODConfig.cmake.in rename to cmake/Config.cmake.in diff --git a/cmake/ODDeb.cmake b/cmake/Deb.cmake similarity index 100% rename from cmake/ODDeb.cmake rename to cmake/Deb.cmake diff --git a/cmake/ODDependency.cmake b/cmake/Dependency.cmake similarity index 57% rename from cmake/ODDependency.cmake rename to cmake/Dependency.cmake index 5e857b3c..db008085 100644 --- a/cmake/ODDependency.cmake +++ b/cmake/Dependency.cmake @@ -3,6 +3,22 @@ find_package(OpenCV 3 REQUIRED) find_package(VTK REQUIRED) find_package(Boost 1.40 COMPONENTS program_options REQUIRED) +if(WITH_CAFFE) + find_package(Caffe REQUIRED) + if(Caffe_FOUND) + add_definitions(${Caffe_DEFINITIONS}) + if(WITH_GPU) + find_package(CUDA REQUIRED) + endif() + if(WITH_GTKMM) + find_package(PkgConfig) + pkg_check_modules(GTKMM gtkmm-3.0) + endif() + else() + message("Caffe not found!") + endif() +endif() + # Needed by od_common. There is a circular dependency between od_common and od_gpu_common include dirs. include_directories(GPU_COMMON_INCLUDE_DIRS ${OD_SOURCE_DIR}/gpu/common/include) include_directories(GPU_DETECTORS_INCLUDE_DIRS ${OD_SOURCE_DIR}/gpu/detectors/include) \ No newline at end of file diff --git a/cmake/ODMacros.cmake b/cmake/Macros.cmake similarity index 100% rename from cmake/ODMacros.cmake rename to cmake/Macros.cmake diff --git a/cmake/ODOptions.cmake b/cmake/Options.cmake similarity index 90% rename from cmake/ODOptions.cmake rename to cmake/Options.cmake index d818a743..cc868c00 100644 --- a/cmake/ODOptions.cmake +++ b/cmake/Options.cmake @@ -23,6 +23,9 @@ option(WITH_GPU "Build GPU modules" ON) option(WITH_EXAMPLES "Build examples" OFF) option(WITH_SVMLIGHT "Build with svmlight support" ON) option(WITH_STD_SHARED_PTR "use std::shared_ptr instead of boost::shared_ptr" OFF) +option(WITH_CAFFE "build caffe support" OFF) +option(WITH_GTKMM "build gtkmm support" OFF) + option(BUILD_GLOBAL_2D_DETECTION "build global 2D detection" ON) option(BUILD_GLOBAL_3D_DETECTION "build global 3D detection" ON) option(BUILD_LOCAL_2D_DETECTION "build local 2D detection" ON) @@ -48,3 +51,7 @@ if(WITH_GPU) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWITH_GPU") endif() +if(WITH_CAFFE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWITH_CAFFE") +endif() + diff --git a/cmake/ODTargets.cmake b/cmake/Targets.cmake similarity index 100% rename from cmake/ODTargets.cmake rename to cmake/Targets.cmake diff --git a/cmake/Uninstall.cmake b/cmake/Uninstall.cmake new file mode 100644 index 00000000..8937476d --- /dev/null +++ b/cmake/Uninstall.cmake @@ -0,0 +1,3 @@ +# uninstall target +configure_file("${OD_CMAKE_DIR}/Uninstall.cmake.in" "${OD_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) +add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) \ No newline at end of file diff --git a/cmake/ODUninstall.cmake.in b/cmake/Uninstall.cmake.in similarity index 100% rename from cmake/ODUninstall.cmake.in rename to cmake/Uninstall.cmake.in diff --git a/cmake/ODVersion.cmake b/cmake/Version.cmake similarity index 100% rename from cmake/ODVersion.cmake rename to cmake/Version.cmake diff --git a/cmake/ODVersion.h.in b/cmake/Version.h.in similarity index 100% rename from cmake/ODVersion.h.in rename to cmake/Version.h.in diff --git a/detectors/include/od/detectors/global2D/detection/ConvClassification.h b/detectors/include/od/detectors/global2D/detection/ConvClassification.h new file mode 100644 index 00000000..a87db5b4 --- /dev/null +++ b/detectors/include/od/detectors/global2D/detection/ConvClassification.h @@ -0,0 +1,62 @@ +#pragma once + +#include "od/common/pipeline/Detector.h" +#include "od/common/pipeline/Scene.h" +#include "od/common/utils/Utils.h" +#include "od/common/utils/FeatureDetector2D.h" + + +#include <opencv2/opencv.hpp> + +#include <cstring> +#include <cstdlib> +#include <vector> + +#include <string> +#include <iostream> +#include <stdio.h> + +#include <caffe/caffe.hpp> +#include <caffe/util/io.hpp> +#include <caffe/blob.hpp> + +#include "od/common/utils/Shared_pointers.h" + +namespace od +{ + namespace g2d + { + class ConvClassification : public Detector2D + { + public: + + ConvClassification(const std::string & trained_data_location = std::string("")); + + void setWeightModelFileLocation(const std::string & location); + void setNetworkModelFileLocation(const std::string & location); + void setImageFileLocation(const std::string & location); + + std::string getWeightModelFileLocation(); + std::string getNetworkModelFileLocation(); + std::string getImageFileLocation(); + + void setTestBlob(int num_channels, int img_height, int img_width); + void classify(); + + void init(); + shared_ptr<Detections2D> detectOmni(shared_ptr<SceneImage> scene); + shared_ptr<Detections> detect(shared_ptr<SceneImage> scene); + + private: + std::string weightModelFileLoaction; + std::string networkFileLocation; + std::string imageFileLocation; + caffe::Datum strucBlob; + caffe::BlobProto protoBlob; + std::vector<caffe::Blob<float>*> inputBlob; + + }; + } +} + + diff --git a/detectors/include/od/detectors/global2D/localization/Convolve.h b/detectors/include/od/detectors/global2D/localization/Convolve.h new file mode 100755 index 00000000..f0e824d7 --- /dev/null +++ b/detectors/include/od/detectors/global2D/localization/Convolve.h @@ -0,0 +1,67 @@ +/* +Copyright (C) 2006 Pedro Felzenszwalb + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* convolution */ + +#pragma once + +#include <vector> +#include <algorithm> +#include <cmath> +#include "od/detectors/global2D/localization/Image.h" + +/* convolve src with mask. dst is flipped! */ +static void convolve_even(image<float> *src, image<float> *dst, + std::vector<float> &mask) { + int width = src->width(); + int height = src->height(); + int len = mask.size(); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + float sum = mask[0] * imRef(src, x, y); + for (int i = 1; i < len; i++) { + sum += mask[i] * + (imRef(src, std::max(x-i,0), y) + + imRef(src, std::min(x+i, width-1), y)); + } + imRef(dst, y, x) = sum; + } + } +} + +/* convolve src with mask. dst is flipped! */ +static void convolve_odd(image<float> *src, image<float> *dst, + std::vector<float> &mask) { + int width = src->width(); + int height = src->height(); + int len = mask.size(); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + float sum = mask[0] * imRef(src, x, y); + for (int i = 1; i < len; i++) { + sum += mask[i] * + (imRef(src, std::max(x-i,0), y) - + imRef(src, std::min(x+i, width-1), y)); + } + imRef(dst, y, x) = sum; + } + } +} + diff --git a/detectors/include/od/detectors/global2D/localization/CreateModel_test.h b/detectors/include/od/detectors/global2D/localization/CreateModel_test.h new file mode 100644 index 00000000..f391e9fb --- /dev/null +++ b/detectors/include/od/detectors/global2D/localization/CreateModel_test.h @@ -0,0 +1,597 @@ +#pragma once + +#include <opencv2/objdetect/objdetect.hpp> +#include <opencv2/highgui/highgui.hpp> +#include <opencv2/imgproc/imgproc.hpp> +#include <opencv2/core/core.hpp> + +#include <cmath> +#include <cstdio> +#include <vector> +#include <iostream> +#include <algorithm> +#include <stdio.h> +#include <math.h> +#include <stdlib.h> +#include <time.h> +# include <cstdlib> +# include <iomanip> +# include <fstream> +# include <ctime> +# include <cstring> +#include <sstream> +#include <string> + +class regionProperties +{ + public: + void setLabel(int l); + void setBoundaries(int ix, int iy, int ax, int ay); + int label; + int min_x; + int max_x; + int min_y; + int max_y; + int size; + Mat xx_hist, xy_hist, yy_hist, orientation_image_hist,differential_excitation_hist, color_hist; + vector <int> neighbors; + bool validity; +}; + +void regionProperties::setLabel(int l) +{ + label = l; +} + +Mat get_hess_hist_xx(Mat regionMask, int histSize, float hist_range_min, float hist_range_max) +{ + Mat kernel_filter_1(7,7, CV_32FC1,1); + kernel_filter_1 = (Mat_<double>(7,7) << 1.57130243e-04, 7.17839338e-04, 0, -1.76805171e-03, 0, 7.17839338e-04, 1.57130243e-04, + 1.91423823e-03, 8.74507340e-03, 0, -2.15392793e-02, 0, 8.74507340e-03, 1.91423823e-03, + 8.57902057e-03, 3.91926999e-02, 0, -9.65323526e-02, 0, 3.91926999e-02, 8.57902057e-03, + 1.41444137e-02, 6.46178379e-02, 0, -1.59154943e-01, 0, 6.46178379e-02, 1.41444137e-02, + 8.57902057e-03, 3.91926999e-02, 0, -9.65323526e-02, 0, 3.91926999e-02, 8.57902057e-03, + 1.91423823e-03, 8.74507340e-03, 0, -2.15392793e-02, 0, 8.74507340e-03, 1.91423823e-03, + 1.57130243e-04, 7.17839338e-04, 0, -1.76805171e-03, 0, 7.17839338e-04, 1.57130243e-04 + ); + Mat image_filtered_xx; + filter2D(regionMask, image_filtered_xx, -1, kernel_filter_1,Point(-1,-1), 0,BORDER_DEFAULT ); + Mat xx_hist; + float range[] = { hist_range_min, hist_range_max } ; + const float* histRange = { range }; + bool uniform = true; bool accumulate = false; + calcHist( &image_filtered_xx, 1, 0, Mat(), xx_hist, 1, &histSize, &histRange, uniform, accumulate ); + return xx_hist; +} + +Mat get_hess_hist_xy(Mat regionMask, int histSize, float hist_range_min, float hist_range_max) +{ + Mat kernel_filter_2(7,7, CV_32FC1,1); + kernel_filter_2 = (Mat_<double>(7,7) << 0.00017677, 0.00143568, 0.00321713, 0, -0.00321713, -0.00143568, -0.00017677, + 0.00143568, 0.0116601, 0.02612847, 0, -0.02612847, -0.0116601, -0.00143568, + 0.00321713, 0.02612847, 0.05854983, 0, -0.05854983, -0.02612847, -0.00321713, + 0, 0, 0, 0, 0, 0, 0, + -0.00321713, -0.02612847, -0.05854983, 0, 0.05854983, 0.02612847, 0.00321713, + -0.00143568, -0.0116601, -0.02612847, 0, 0.02612847, 0.0116601, 0.00143568, + -0.00017677, -0.00143568, -0.00321713, 0, 0.00321713, 0.00143568, 0.00017677 + ); + Mat image_filtered_xy; + filter2D(regionMask, image_filtered_xy, -1, kernel_filter_2,Point(-1,-1), 0,BORDER_DEFAULT ); + Mat xy_hist; + float range[] = { hist_range_min, hist_range_max } ; + const float* histRange = { range }; + bool uniform = true; bool accumulate = false; + calcHist( &image_filtered_xy, 1, 0, Mat(), xy_hist, 1, &histSize, &histRange, uniform, accumulate ); + return xy_hist; +} + +Mat get_hess_hist_yy(Mat regionMask, int histSize, float hist_range_min, float hist_range_max) +{ + Mat kernel_filter_3(7,7, CV_32FC1,1); + kernel_filter_3 = (Mat_<double>(7,7) << + 1.57130243e-04, 1.91423823e-03, 8.57902057e-03, 1.41444137e-02, 8.57902057e-03, 1.91423823e-03, 1.57130243e-04, + 7.17839338e-04, 8.74507340e-03, 3.91926999e-02, 6.46178379e-02, 3.91926999e-02, 8.74507340e-03, 7.17839338e-04, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + -1.76805171e-03, -2.15392793e-02, -9.65323526e-02, -1.59154943e-01, -9.65323526e-02, -2.15392793e-02, -1.76805171e-03, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 7.17839338e-04, 8.74507340e-03, 3.91926999e-02, 6.46178379e-02, 3.91926999e-02, 8.74507340e-03, 7.17839338e-04, + 1.57130243e-04, 1.91423823e-03, 8.57902057e-03, 1.41444137e-02, 8.57902057e-03, 1.91423823e-03, 1.57130243e-04 + ); + Mat image_filtered_yy; + filter2D(regionMask, image_filtered_yy, -1, kernel_filter_3,Point(-1,-1), 0,BORDER_DEFAULT ); + Mat yy_hist; + float range[] = { hist_range_min, hist_range_max } ; + const float* histRange = { range }; + bool uniform = true; bool accumulate = false; + + calcHist( &image_filtered_yy, 1, 0, Mat(), yy_hist, 1, &histSize, &histRange, uniform, accumulate ); + return yy_hist; +} + +Mat get_orientation_hist(Mat regionMask, int histSize, float hist_range_min, float hist_range_max) +{ + Mat kernel_filter_1(3,3, CV_32FC1,1); + kernel_filter_1 = (Mat_<double>(3,3) << 1, 1, 1, 1, -8, 1, 1, 1, 1); + Mat kernel_filter_2(3,3, CV_32FC1,1); + kernel_filter_2 = (Mat_<double>(3,3) << 0,0,0,0,1,0,0,0,0); + Mat kernel_filter_3(3,3, CV_32FC1,1); + kernel_filter_3 = (Mat_<double>(3,3) << 1,2,1,0,0,0,-1,-2,-1); + Mat kernel_filter_4(3,3, CV_32FC1,1); + kernel_filter_4 = (Mat_<double>(3,3) << 1,0,-1,2,0,-2,1,0,-1); + Mat image_filtered_v1; + Mat image_filtered_v2; + Mat image_filtered_v3; + Mat image_filtered_v4; + int temp30; + //filtering + filter2D(regionMask, image_filtered_v1, -1, kernel_filter_1,Point(-1,-1), 0,BORDER_DEFAULT ); + filter2D(regionMask, image_filtered_v2, -1, kernel_filter_2,Point(-1,-1), 0,BORDER_DEFAULT ); + filter2D(regionMask, image_filtered_v3, -1, kernel_filter_3,Point(-1,-1), 0,BORDER_DEFAULT ); + filter2D(regionMask, image_filtered_v4, -1, kernel_filter_4,Point(-1,-1), 0,BORDER_DEFAULT ); + + //Orientation New + float temp_5; + float temp_6; + float temp_7_theta; + float temp_8_theta_dash; + int temp_9_theta_dash_quantized; + float quantized_1[12] = {0,1,2,3,4,5,6,7,8,9,10,11}; + int quantized_count_1 = 0; + float orientation_image_matrix[regionMask.rows][regionMask.cols]; + Mat orientation_image = regionMask.clone(); + for(int r = 0; r < regionMask.rows; r++) + { + for(int c = 0; c < regionMask.cols; c++) + { + orientation_image_matrix[r][c] = 0; + } + } + for(int r = 0; r < regionMask.rows; r++) + { + for(int c = 0; c < regionMask.cols; c++) + { + temp_5 = image_filtered_v3.at<schar>(r,c); + temp_6 = image_filtered_v4.at<schar>(r,c); + if(temp_6 != 0 && temp_5 != 0) + { + temp_7_theta = atan(temp_5/temp_6); + } + else if(temp_6 == 0 && temp_5 > 0) + { + temp_7_theta = M_PI/2; + } + else if(temp_6 == 0 && temp_5 < 0) + { + temp_7_theta = -M_PI/2; + } + else if(temp_6 == 0 && temp_5 == 0) + { + temp_7_theta = 0; + } + else if(temp_6 != 0 && temp_5 == 0) + { + temp_7_theta = 0; + } + + if(temp_5 >= 0 && temp_6 >= 0) + { + temp_8_theta_dash = temp_7_theta; + } + else if(temp_5 < 0 && temp_6 >= 0) + { + temp_8_theta_dash = temp_7_theta + M_PI; + } + else if(temp_5 < 0 && temp_6 < 0) + { + temp_8_theta_dash = temp_7_theta + M_PI; + } + else if(temp_5 >= 0 && temp_6 < 0) + { + temp_8_theta_dash = temp_7_theta + 2*M_PI; + } + temp_9_theta_dash_quantized = floor((temp_8_theta_dash*11)/(2*M_PI)); + orientation_image_matrix[r][c] = temp_9_theta_dash_quantized; + orientation_image.at<uchar>(r,c) = temp_9_theta_dash_quantized; + } + } +/* for(int r = 0; r < img.rows; r++) + { + for(int c = 0; c < img.cols; c++) + { + cout << orientation_image_matrix[r][c] << " "; + } + cout << endl; + } +*/ + float range[] = { hist_range_min, hist_range_max } ; + const float* histRange = { range }; + bool uniform = true; bool accumulate = false; + Mat orientation_image_hist; + + /// Compute the histograms: + calcHist( &orientation_image, 1, 0, Mat(), orientation_image_hist, 1, &histSize, &histRange, uniform, accumulate ); + return orientation_image_hist; +} + +Mat get_diff_exci_hist(Mat regionMask, int histSize, float hist_range_min, float hist_range_max) +{ + Mat kernel_filter_1(3,3, CV_32FC1,1); + kernel_filter_1 = (Mat_<double>(3,3) << 1, 1, 1, 1, -8, 1, 1, 1, 1); + Mat kernel_filter_2(3,3, CV_32FC1,1); + kernel_filter_2 = (Mat_<double>(3,3) << 0,0,0,0,1,0,0,0,0); + Mat kernel_filter_3(3,3, CV_32FC1,1); + kernel_filter_3 = (Mat_<double>(3,3) << 1,2,1,0,0,0,-1,-2,-1); + Mat kernel_filter_4(3,3, CV_32FC1,1); + kernel_filter_4 = (Mat_<double>(3,3) << 1,0,-1,2,0,-2,1,0,-1); + Mat image_filtered_v1; + Mat image_filtered_v2; + Mat image_filtered_v3; + Mat image_filtered_v4; + int temp30; + //filtering + filter2D(regionMask, image_filtered_v1, -1, kernel_filter_1,Point(-1,-1), 0,BORDER_DEFAULT ); + filter2D(regionMask, image_filtered_v2, -1, kernel_filter_2,Point(-1,-1), 0,BORDER_DEFAULT ); + filter2D(regionMask, image_filtered_v3, -1, kernel_filter_3,Point(-1,-1), 0,BORDER_DEFAULT ); + filter2D(regionMask, image_filtered_v4, -1, kernel_filter_4,Point(-1,-1), 0,BORDER_DEFAULT ); + + //Differential Excitation New + float temp_1; + float temp_2; + float temp_3_alpha; + float temp_4_quantized_alpha; + int quantized[8] = {0,1,2,3,4,5,6,7}; + int quantized_count = 0; + int differential_excitation_image_matrix[regionMask.rows][regionMask.cols]; + Mat differential_excitation_image = regionMask.clone(); + + for(int r = 0; r < regionMask.rows; r++) + { + for(int c = 0; c < regionMask.cols; c++) + { + differential_excitation_image_matrix[r][c] = 0; + } + } + for(int r = 0; r < regionMask.rows; r++) + { + for(int c = 0; c < regionMask.cols; c++) + { + temp_1 = image_filtered_v1.at<schar>(r,c); + temp_2 = image_filtered_v2.at<schar>(r,c); + if(temp_2 != 0) + { + temp_3_alpha = atan(temp_1/temp_2); + } + else if(temp_2 == 0 && temp_1 > 0) + { + temp_3_alpha = M_PI/2; + } + else if(temp_2 == 0 && temp_1 < 0) + { + temp_3_alpha = -M_PI/2; + } + else if(temp_2 == 0 && temp_1 == 0) + { + temp_3_alpha = 0; + } + temp_4_quantized_alpha = floor(((temp_3_alpha + M_PI/2)/M_PI)*7); + differential_excitation_image_matrix[r][c] = temp_4_quantized_alpha; + differential_excitation_image.at<uchar>(r,c) = temp_4_quantized_alpha; + } + } +/* for(int r = 0; r < img.rows; r++) + { + for(int c = 0; c < img.cols; c++) + { + cout << differential_excitation_image_matrix[r][c] << " "; + } + cout << endl; + } +*/ + float range[] = { hist_range_min, hist_range_max } ; + const float* histRange = { range }; + bool uniform = true; bool accumulate = false; + Mat differential_excitation_hist; + + /// Compute the histograms: + calcHist( &differential_excitation_image, 1, 0, Mat(), differential_excitation_hist, 1, &histSize, &histRange, uniform, accumulate ); + return differential_excitation_hist; +} + +void refineRegions(vector < vector <int> > sp, int total_masks, regionProperties regions[], int min_height, int min_width) +{ + for (int i = 0; i < total_masks; i++) + { + regions[i].setLabel(i); + regions[i].min_x = 100000; + regions[i].min_y = 100000; + regions[i].max_x = -1; + regions[i].max_y = -1; + } + for (int r = 0; r < sp.size(); r++) + { + for(int c = 0; c < sp[0].size(); c++) + { + regions[sp[r][c]].size++; + if(regions[sp[r][c]].min_x > c) + regions[sp[r][c]].min_x = c; + if(regions[sp[r][c]].min_y > r) + regions[sp[r][c]].min_y = r; + if(regions[sp[r][c]].max_x < c) + regions[sp[r][c]].max_x = c; + if(regions[sp[r][c]].max_y < r) + regions[sp[r][c]].max_y = r; + } + } + for (int i = 0; i < total_masks; i++) + { + if((regions[i].max_x - regions[i].min_x > min_width) and (regions[i].max_y - regions[i].min_y > min_height)) + regions[i].validity = true; + else + regions[i].validity = false; + } +} + + + +void createModel(vector < vector <int> > sp, int total_masks, Mat grayMask, regionProperties regions[], int histSize, float hist_range_min, float hist_range_max) +{ + Mat regionMask = grayMask.clone(); + + + for (int i = 0; i < total_masks; i++) + { + regionMask = grayMask.clone(); +// cout << i << endl; + for (int r = 0; r < sp.size(); r++) + { + for(int c = 0; c < sp[0].size(); c++) + { + if(sp[r][c] != i) + regionMask.at<uchar>(r,c) = 0; + } + } + if(regions[i].size<200) + regions[i].validity = false; +// cout << i << " hessian" << endl; + //Hessian Matrix + regions[i].xx_hist = get_hess_hist_xx(regionMask, histSize, hist_range_min, hist_range_max); + regions[i].xy_hist = get_hess_hist_xy(regionMask, histSize, hist_range_min, hist_range_max); + regions[i].yy_hist = get_hess_hist_yy(regionMask, histSize, hist_range_min, hist_range_max); + +// cout << i << " orien" << endl; + //orientation Matrix + regions[i].orientation_image_hist = get_orientation_hist(regionMask, histSize, hist_range_min, hist_range_max); + +// cout << i << " diff" << endl; + //Differential Excitation Matrix + regions[i].differential_excitation_hist = get_diff_exci_hist(regionMask, histSize, hist_range_min, hist_range_max); + +// cout << i << " color" << endl; + //Color Histogram + float range[] = { hist_range_min, hist_range_max } ; + const float* histRange = { range }; + bool uniform = true; bool accumulate = false; + calcHist(&grayMask, 1, 0, Mat(), regions[i].color_hist, 1, &histSize, &histRange, uniform, accumulate ); + + + } +} + +bool checkNeighbors(regionProperties a, regionProperties b) +{ + if( + ((a.min_x < b.min_x) and (b.min_x < a.max_x) and (a.min_y < b.min_y) and (b.min_y < a.max_y)) + or + ((a.min_x < b.max_x) and (b.max_x < a.max_x) and (a.min_y < b.max_y) and (b.max_y < a.max_y)) + or + ((a.min_x < b.min_x) and (b.min_x < a.max_x) and (a.min_y < b.max_y) and (b.max_y < a.max_y)) + or + ((a.min_x < b.max_x) and (b.min_x < a.max_x) and (a.min_y < b.max_y) and (b.max_y < a.max_y)) + ) + { + + return true; + } + + return false; +} + +vector < vector <int> > findNeighbors(regionProperties regions[], int total_masks, Mat regionMask, vector < vector <int> > sp) +{ + vector < vector <int> > neighbors; + vector <int> rows; + rows.push_back(0); + rows.push_back(0); + int num = 0; + for(int i = 1; i < total_masks-1; i++) + { + for(int j = i+1; j < i+20; j++) + { + if(j<total_masks-2) + { + if(checkNeighbors(regions[i], regions[j]) and regions[i].validity == true and regions[j].validity == true) + { + /* cout << i << " " << j << endl; + cout << regions[i].min_x << " " << regions[i].min_y << " " << regions[i].max_x << " " << regions[i].max_y << endl; + cout << regions[j].min_x << " " << regions[j].min_y << " " << regions[j].max_x << " " << regions[j].max_y << endl; + for (int r = 0; r < regionMask.rows; r++) + { + for(int c = 0; c < regionMask.cols; c++) + { + if(sp[r][c] != i) + { + regionMask.at<Vec3b>(r,c)[0] = 0; + regionMask.at<Vec3b>(r,c)[1] = 0; + regionMask.at<Vec3b>(r,c)[2] = 0; + } + else + { + regionMask.at<Vec3b>(r,c)[0] = 0; + regionMask.at<Vec3b>(r,c)[1] = 0; + regionMask.at<Vec3b>(r,c)[2] = 255; + } + } + } + for (int r = 0; r < regionMask.rows; r++) + { + for(int c = 0; c < regionMask.cols; c++) + { + if(sp[r][c] != j and regionMask.at<Vec3b>(r,c)[2] != 255) + { + regionMask.at<Vec3b>(r,c)[0] = 0; + regionMask.at<Vec3b>(r,c)[1] = 0; + regionMask.at<Vec3b>(r,c)[2] = 0; + } + else + { + regionMask.at<Vec3b>(r,c)[0] = 0; + regionMask.at<Vec3b>(r,c)[1] = 255; + regionMask.at<Vec3b>(r,c)[2] = 0; + } + } + } + imshow("regionMask", regionMask); + waitKey(0); + */ + rows[0] = i; + rows[1] = j; + neighbors.push_back(rows); + } + } + } + } + return neighbors; +} + +float calcSimilarities(regionProperties a, regionProperties b, float spSize) +{ + double sim = 0.0; + sim += compareHist( a.xx_hist, b.xx_hist, 1); + sim += compareHist( a.xy_hist, b.xy_hist, 1); + sim += compareHist( a.yy_hist, b.yy_hist, 1); + sim += compareHist( a.orientation_image_hist, b.orientation_image_hist, 1); + sim += compareHist( a.differential_excitation_hist, b.differential_excitation_hist, 1); + sim += compareHist( a.color_hist, b.color_hist, 1); + sim += 100 * ((a.size + b.size)/spSize); + double bbsize = ((max(a.max_x, b.max_x) - min(a.min_x, b.min_x))* (max(a.max_y, b.max_y) - min(a.min_y, b.min_y)) ); + sim += 100*((bbsize - a.size - b.size) / spSize); + return sim; +} + +void mergeRegions(int value, regionProperties regions[], vector < vector <int> > sp_neighbors, Mat grayMask, vector < vector <int> > sp, int minRegionSize, int histSize, float hist_range_min, float hist_range_max) +{ +/* + void setBoundaries(int ix, int iy, int ax, int ay); + int label; + int min_x; + int max_x; + int min_y; + int max_y; + int size; + Mat xx_hist, xy_hist, yy_hist, orientation_image_hist,differential_excitation_hist, color_hist; + vector <int> neighbors; + bool validity; +*/ + regions[sp_neighbors[value][0]].validity = true; + regions[sp_neighbors[value][1]].validity = false; + regions[sp_neighbors[value][0]].min_x = min(regions[sp_neighbors[value][0]].min_x, regions[sp_neighbors[value][1]].min_x); + regions[sp_neighbors[value][0]].max_y = max(regions[sp_neighbors[value][0]].max_y, regions[sp_neighbors[value][1]].max_y); + regions[sp_neighbors[value][0]].min_x = min(regions[sp_neighbors[value][0]].min_x, regions[sp_neighbors[value][1]].min_x); + regions[sp_neighbors[value][0]].max_y = max(regions[sp_neighbors[value][0]].max_y, regions[sp_neighbors[value][1]].max_y); + regions[sp_neighbors[value][0]].size = regions[sp_neighbors[value][0]].size + regions[sp_neighbors[value][1]].size; + + Mat regionMask = grayMask.clone(); + int i = sp_neighbors[value][0]; + for (int r = 0; r < sp.size(); r++) + { + for(int c = 0; c < sp[0].size(); c++) + { + if(sp[r][c] != i) + regionMask.at<uchar>(r,c) = 0; + } + } + if(regions[i].size<minRegionSize) + regions[i].validity = false; +// cout << i << " hessian " << regionMask.channels() << endl; + //Hessian Matrix + regions[i].xx_hist = get_hess_hist_xx(regionMask, histSize, hist_range_min, hist_range_max); + regions[i].xy_hist = get_hess_hist_xy(regionMask, histSize, hist_range_min, hist_range_max); + regions[i].yy_hist = get_hess_hist_yy(regionMask, histSize, hist_range_min, hist_range_max); + +// cout << i << " orien" << endl; + //orientation Matrix + regions[i].orientation_image_hist = get_orientation_hist(regionMask, histSize, hist_range_min, hist_range_max); + +// cout << i << " diff" << endl; + //Differential Excitation Matrix + regions[i].differential_excitation_hist = get_diff_exci_hist(regionMask, histSize, hist_range_min, hist_range_max); + +// cout << i << " color" << endl; + //Color Histogram + float range[] = { hist_range_min, hist_range_max } ; + const float* histRange = { range }; + bool uniform = true; bool accumulate = false; + calcHist(&grayMask, 1, 0, Mat(), regions[i].color_hist, 1, &histSize, &histRange, uniform, accumulate ); +} + +bool checkRounds(int totals, regionProperties regions[], int numRounds) +{ + int num = 0; + for(int i = 0; i < totals; i++) + { + if(regions[i].validity == false) + { + num++; + } + } +// cout << "num = " << num << endl; + if(num > numRounds) + return true; + else + return false; +} + +vector < vector <int> > extractROIs(int total_masks, regionProperties regions[], int numRounds, float spSize, vector < vector <int> > sp, Mat img, Mat gray_mask, int minRegionSize, int histSize, float hist_range_min, float hist_range_max) +{ + vector < vector <int> > pts; + int totals = total_masks; + int value = 10; + while(checkRounds(totals, regions, numRounds) and value > 0) + { + vector < vector <int> > sp_neighbors = findNeighbors(regions, total_masks, img, sp); + vector <float> similarities; + for(int i=0; i<sp_neighbors.size(); i++) + { + + float sim = calcSimilarities(regions[sp_neighbors[i][0]],regions[sp_neighbors[i][1]], spSize); +// cout << i << " " << sp_neighbors[i][0] << " " << sp_neighbors[i][1] << " " << sim << endl; + similarities.push_back(sim); + } + + //finding closest two regions + value = min_element(similarities.begin(), similarities.end()) - similarities.begin(); +// cout << "in here" << endl; + + //merging + mergeRegions(value, regions, sp_neighbors, gray_mask, sp, minRegionSize, histSize, hist_range_min, hist_range_max); +// cout << "value = " << value << endl; + for(int i = 0; i < total_masks; i++) + { + if(regions[i].validity == true) + { + vector <int> temp; + temp.push_back(regions[i].min_x); + temp.push_back(regions[i].min_y); + temp.push_back(regions[i].max_x); + temp.push_back(regions[i].max_y); + pts.push_back(temp); + // rectangle(outputImg, Point(regions[i].min_x, regions[i].min_y), Point(regions[i].max_x, regions[i].max_y), Scalar(0, 0, 255)); + } + } + } + std::sort(pts.begin(), pts.end()); + pts.erase(std::unique(pts.begin(), pts.end()), pts.end()); + cout << "Total bounding boxes = " << pts.size() << endl; + return pts; +} + diff --git a/detectors/include/od/detectors/global2D/localization/Disjoint-set.h b/detectors/include/od/detectors/global2D/localization/Disjoint-set.h new file mode 100755 index 00000000..69d81566 --- /dev/null +++ b/detectors/include/od/detectors/global2D/localization/Disjoint-set.h @@ -0,0 +1,77 @@ +/* +Copyright (C) 2006 Pedro Felzenszwalb + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#pragma once + +// disjoint-set forests using union-by-rank and path compression (sort of). + +typedef struct { + int rank; + int p; + int size; +} uni_elt; + +class universe { +public: + universe(int elements); + ~universe(); + int find(int x); + void join(int x, int y); + int size(int x) const { return elts[x].size; } + int num_sets() const { return num; } + +private: + uni_elt *elts; + int num; +}; + +universe::universe(int elements) { + elts = new uni_elt[elements]; + num = elements; + for (int i = 0; i < elements; i++) { + elts[i].rank = 0; + elts[i].size = 1; + elts[i].p = i; + } +} + +universe::~universe() { + delete [] elts; +} + +int universe::find(int x) { + int y = x; + while (y != elts[y].p) + y = elts[y].p; + elts[x].p = y; + return y; +} + +void universe::join(int x, int y) { + if (elts[x].rank > elts[y].rank) { + elts[y].p = x; + elts[x].size += elts[y].size; + } else { + elts[x].p = y; + elts[y].size += elts[x].size; + if (elts[x].rank == elts[y].rank) + elts[y].rank++; + } + num--; +} + diff --git a/detectors/include/od/detectors/global2D/localization/Filter.h b/detectors/include/od/detectors/global2D/localization/Filter.h new file mode 100755 index 00000000..be1007ee --- /dev/null +++ b/detectors/include/od/detectors/global2D/localization/Filter.h @@ -0,0 +1,97 @@ +/* +Copyright (C) 2006 Pedro Felzenszwalb + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* simple filters */ + +#pragma once + +#include <vector> +#include <cmath> +#include "od/detectors/global2D/localization/Image.h" +#include "od/detectors/global2D/localization/Misc.h" +#include "od/detectors/global2D/localization/Convolve.h" +#include "od/detectors/global2D/localization/Imconv.h" + +#define WIDTH 4.0 + +/* normalize mask so it integrates to one */ +static void normalize(std::vector<float> &mask) { + int len = mask.size(); + float sum = 0; + for (int i = 1; i < len; i++) { + sum += fabs(mask[i]); + } + sum = 2*sum + fabs(mask[0]); + for (int i = 0; i < len; i++) { + mask[i] /= sum; + } +} + +/* make filters */ +#define MAKE_FILTER(name, fun) \ +static std::vector<float> make_ ## name (float sigma) { \ + sigma = std::max(sigma, 0.01F); \ + int len = (int)ceil(sigma * WIDTH) + 1; \ + std::vector<float> mask(len); \ + for (int i = 0; i < len; i++) { \ + mask[i] = fun; \ + } \ + return mask; \ +} + +MAKE_FILTER(fgauss, exp(-0.5*square(i/sigma))); + +/* convolve image with gaussian filter */ +static image<float> *smooth(image<float> *src, float sigma) { + std::vector<float> mask = make_fgauss(sigma); + normalize(mask); + + image<float> *tmp = new image<float>(src->height(), src->width(), false); + image<float> *dst = new image<float>(src->width(), src->height(), false); + convolve_even(src, tmp, mask); + convolve_even(tmp, dst, mask); + + delete tmp; + return dst; +} + +/* convolve image with gaussian filter */ +image<float> *smooth(image<uchar> *src, float sigma) { + image<float> *tmp = imageUCHARtoFLOAT(src); + image<float> *dst = smooth(tmp, sigma); + delete tmp; + return dst; +} + +/* compute laplacian */ +static image<float> *laplacian(image<float> *src) { + int width = src->width(); + int height = src->height(); + image<float> *dst = new image<float>(width, height); + + for (int y = 1; y < height-1; y++) { + for (int x = 1; x < width-1; x++) { + float d2x = imRef(src, x-1, y) + imRef(src, x+1, y) - + 2*imRef(src, x, y); + float d2y = imRef(src, x, y-1) + imRef(src, x, y+1) - + 2*imRef(src, x, y); + imRef(dst, x, y) = d2x + d2y; + } + } + return dst; +} diff --git a/detectors/include/od/detectors/global2D/localization/Image.h b/detectors/include/od/detectors/global2D/localization/Image.h new file mode 100755 index 00000000..891cc020 --- /dev/null +++ b/detectors/include/od/detectors/global2D/localization/Image.h @@ -0,0 +1,98 @@ +/* +Copyright (C) 2006 Pedro Felzenszwalb + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* a simple image class */ + +#pragma once + +#include <cstring> + +template <class T> +class image { + public: + /* create an image */ + image(const int width, const int height, const bool init = true); + + /* delete an image */ + ~image(); + + /* init an image */ + void init(const T &val); + + /* copy an image */ + image<T> *copy() const; + + /* get the width of an image. */ + int width() const { return w; } + + /* get the height of an image. */ + int height() const { return h; } + + /* image data. */ + T *data; + + /* row pointers. */ + T **access; + + private: + int w, h; +}; + +/* use imRef to access image data. */ +#define imRef(im, x, y) (im->access[y][x]) + +/* use imPtr to get pointer to image data. */ +#define imPtr(im, x, y) &(im->access[y][x]) + +template <class T> +image<T>::image(const int width, const int height, const bool init) { + w = width; + h = height; + data = new T[w * h]; // allocate space for image data + access = new T*[h]; // allocate space for row pointers + + // initialize row pointers + for (int i = 0; i < h; i++) + access[i] = data + (i * w); + + if (init) + memset(data, 0, w * h * sizeof(T)); +} + +template <class T> +image<T>::~image() { + delete [] data; + delete [] access; +} + +template <class T> +void image<T>::init(const T &val) { + T *ptr = imPtr(this, 0, 0); + T *end = imPtr(this, w-1, h-1); + while (ptr <= end) + *ptr++ = val; +} + + +template <class T> +image<T> *image<T>::copy() const { + image<T> *im = new image<T>(w, h, false); + memcpy(im->data, data, w * h * sizeof(T)); + return im; +} + diff --git a/detectors/include/od/detectors/global2D/localization/Imconv.h b/detectors/include/od/detectors/global2D/localization/Imconv.h new file mode 100755 index 00000000..4325b6fc --- /dev/null +++ b/detectors/include/od/detectors/global2D/localization/Imconv.h @@ -0,0 +1,175 @@ +/* +Copyright (C) 2006 Pedro Felzenszwalb + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* image conversion */ + +#pragma once + +#include <climits> +#include "od/detectors/global2D/localization/Image.h" +#include "od/detectors/global2D/localization/Imutil.h" +#include "od/detectors/global2D/localization/Misc.h" + +#define RED_WEIGHT 0.299 +#define GREEN_WEIGHT 0.587 +#define BLUE_WEIGHT 0.114 + +static image<uchar> *imageRGBtoGRAY(image<rgb> *input) { + int width = input->width(); + int height = input->height(); + image<uchar> *output = new image<uchar>(width, height, false); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + imRef(output, x, y) = (uchar) + (imRef(input, x, y).r * RED_WEIGHT + + imRef(input, x, y).g * GREEN_WEIGHT + + imRef(input, x, y).b * BLUE_WEIGHT); + } + } + return output; +} + +static image<rgb> *imageGRAYtoRGB(image<uchar> *input) { + int width = input->width(); + int height = input->height(); + image<rgb> *output = new image<rgb>(width, height, false); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + imRef(output, x, y).r = imRef(input, x, y); + imRef(output, x, y).g = imRef(input, x, y); + imRef(output, x, y).b = imRef(input, x, y); + } + } + return output; +} + +static image<float> *imageUCHARtoFLOAT(image<uchar> *input) { + int width = input->width(); + int height = input->height(); + image<float> *output = new image<float>(width, height, false); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + imRef(output, x, y) = imRef(input, x, y); + } + } + return output; +} + +static image<float> *imageINTtoFLOAT(image<int> *input) { + int width = input->width(); + int height = input->height(); + image<float> *output = new image<float>(width, height, false); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + imRef(output, x, y) = imRef(input, x, y); + } + } + return output; +} + +static image<uchar> *imageFLOATtoUCHAR(image<float> *input, + float min, float max) { + int width = input->width(); + int height = input->height(); + image<uchar> *output = new image<uchar>(width, height, false); + + if (max == min) + return output; + + float scale = UCHAR_MAX / (max - min); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + uchar val = (uchar)((imRef(input, x, y) - min) * scale); + imRef(output, x, y) = bound(val, (uchar)0, (uchar)UCHAR_MAX); + } + } + return output; +} + +static image<uchar> *imageFLOATtoUCHAR(image<float> *input) { + float min, max; + min_max(input, &min, &max); + return imageFLOATtoUCHAR(input, min, max); +} + +static image<long> *imageUCHARtoLONG(image<uchar> *input) { + int width = input->width(); + int height = input->height(); + image<long> *output = new image<long>(width, height, false); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + imRef(output, x, y) = imRef(input, x, y); + } + } + return output; +} + +static image<uchar> *imageLONGtoUCHAR(image<long> *input, long min, long max) { + int width = input->width(); + int height = input->height(); + image<uchar> *output = new image<uchar>(width, height, false); + + if (max == min) + return output; + + float scale = UCHAR_MAX / (float)(max - min); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + uchar val = (uchar)((imRef(input, x, y) - min) * scale); + imRef(output, x, y) = bound(val, (uchar)0, (uchar)UCHAR_MAX); + } + } + return output; +} + +static image<uchar> *imageLONGtoUCHAR(image<long> *input) { + long min, max; + min_max(input, &min, &max); + return imageLONGtoUCHAR(input, min, max); +} + +static image<uchar> *imageSHORTtoUCHAR(image<short> *input, + short min, short max) { + int width = input->width(); + int height = input->height(); + image<uchar> *output = new image<uchar>(width, height, false); + + if (max == min) + return output; + + float scale = UCHAR_MAX / (float)(max - min); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + uchar val = (uchar)((imRef(input, x, y) - min) * scale); + imRef(output, x, y) = bound(val, (uchar)0, (uchar)UCHAR_MAX); + } + } + return output; +} + +static image<uchar> *imageSHORTtoUCHAR(image<short> *input) { + short min, max; + min_max(input, &min, &max); + return imageSHORTtoUCHAR(input, min, max); +} + diff --git a/detectors/include/od/detectors/global2D/localization/Imutil.h b/detectors/include/od/detectors/global2D/localization/Imutil.h new file mode 100755 index 00000000..1e14b9eb --- /dev/null +++ b/detectors/include/od/detectors/global2D/localization/Imutil.h @@ -0,0 +1,63 @@ +/* +Copyright (C) 2006 Pedro Felzenszwalb + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* some image utilities */ + +#pragma once + +#include "od/detectors/global2D/localization/Image.h" +#include "od/detectors/global2D/localization/Misc.h" + +/* compute minimum and maximum value in an image */ +template <class T> +void min_max(image<T> *im, T *ret_min, T *ret_max) { + int width = im->width(); + int height = im->height(); + + T min = imRef(im, 0, 0); + T max = imRef(im, 0, 0); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + T val = imRef(im, x, y); + if (min > val) + min = val; + if (max < val) + max = val; + } + } + + *ret_min = min; + *ret_max = max; +} + +/* threshold image */ +template <class T> +image<uchar> *threshold(image<T> *src, int t) { + int width = src->width(); + int height = src->height(); + image<uchar> *dst = new image<uchar>(width, height); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + imRef(dst, x, y) = (imRef(src, x, y) >= t); + } + } + + return dst; +} + diff --git a/detectors/include/od/detectors/global2D/localization/Misc.h b/detectors/include/od/detectors/global2D/localization/Misc.h new file mode 100755 index 00000000..95dabe15 --- /dev/null +++ b/detectors/include/od/detectors/global2D/localization/Misc.h @@ -0,0 +1,63 @@ +/* +Copyright (C) 2006 Pedro Felzenszwalb + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* random stuff */ + +#pragma once + +#include <cmath> + +#ifndef M_PI +#define M_PI 3.141592653589793 +#endif + +typedef unsigned char uchar; + +typedef struct { uchar r, g, b; } rgb; + +inline bool operator==(const rgb &a, const rgb &b) { + return ((a.r == b.r) && (a.g == b.g) && (a.b == b.b)); +} + +template <class T> +inline T abs(const T &x) { return (x > 0 ? x : -x); }; + +template <class T> +inline int sign(const T &x) { return (x >= 0 ? 1 : -1); }; + +template <class T> +inline T square(const T &x) { return x*x; }; + +template <class T> +inline T bound(const T &x, const T &min, const T &max) { + return (x < min ? min : (x > max ? max : x)); +} + +template <class T> +inline bool check_bound(const T &x, const T&min, const T &max) { + return ((x < min) || (x > max)); +} + +inline int vlib_round(float x) { return (int)(x + 0.5F); } + +inline int vlib_round(double x) { return (int)(x + 0.5); } + +inline double gaussian(double val, double sigma) { + return exp(-square(val/sigma)/2)/(sqrt(2*M_PI)*sigma); +} + diff --git a/detectors/include/od/detectors/global2D/localization/Pnmfile.h b/detectors/include/od/detectors/global2D/localization/Pnmfile.h new file mode 100755 index 00000000..f69738a2 --- /dev/null +++ b/detectors/include/od/detectors/global2D/localization/Pnmfile.h @@ -0,0 +1,208 @@ +/* +Copyright (C) 2006 Pedro Felzenszwalb + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* basic image I/O */ + +#pragma once + +#include <cstdlib> +#include <climits> +#include <cstring> +#include <fstream> +#include "od/detectors/global2D/localization/Image.h" +#include "od/detectors/global2D/localization/Misc.h" + +#define BUF_SIZE 256 + +class pnm_error { }; + +static void read_packed(unsigned char *data, int size, std::ifstream &f) { + unsigned char c = 0; + + int bitshift = -1; + for (int pos = 0; pos < size; pos++) { + if (bitshift == -1) { + c = f.get(); + bitshift = 7; + } + data[pos] = (c >> bitshift) & 1; + bitshift--; + } +} + +static void write_packed(unsigned char *data, int size, std::ofstream &f) { + unsigned char c = 0; + + int bitshift = 7; + for (int pos = 0; pos < size; pos++) { + c = c | (data[pos] << bitshift); + bitshift--; + if ((bitshift == -1) || (pos == size-1)) { + f.put(c); + bitshift = 7; + c = 0; + } + } +} + +/* read PNM field, skipping comments */ +static void pnm_read(std::ifstream &file, char *buf) { + char doc[BUF_SIZE]; + char c; + + file >> c; + while (c == '#') { + file.getline(doc, BUF_SIZE); + file >> c; + } + file.putback(c); + + file.width(BUF_SIZE); + file >> buf; + file.ignore(); +} + +static image<uchar> *loadPBM(const char *name) { + char buf[BUF_SIZE]; + + /* read header */ + std::ifstream file(name, std::ios::in | std::ios::binary); + pnm_read(file, buf); + if (strncmp(buf, "P4", 2)) + throw pnm_error(); + + pnm_read(file, buf); + int width = atoi(buf); + pnm_read(file, buf); + int height = atoi(buf); + + /* read data */ + image<uchar> *im = new image<uchar>(width, height); + for (int i = 0; i < height; i++) + read_packed(imPtr(im, 0, i), width, file); + + return im; +} + +static void savePBM(image<uchar> *im, const char *name) { + int width = im->width(); + int height = im->height(); + std::ofstream file(name, std::ios::out | std::ios::binary); + + file << "P4\n" << width << " " << height << "\n"; + for (int i = 0; i < height; i++) + write_packed(imPtr(im, 0, i), width, file); +} + +static image<uchar> *loadPGM(const char *name) { + char buf[BUF_SIZE]; + + /* read header */ + std::ifstream file(name, std::ios::in | std::ios::binary); + pnm_read(file, buf); + if (strncmp(buf, "P5", 2)) + throw pnm_error(); + + pnm_read(file, buf); + int width = atoi(buf); + pnm_read(file, buf); + int height = atoi(buf); + + pnm_read(file, buf); + if (atoi(buf) > UCHAR_MAX) + throw pnm_error(); + + /* read data */ + image<uchar> *im = new image<uchar>(width, height); + file.read((char *)imPtr(im, 0, 0), width * height * sizeof(uchar)); + + return im; +} + +static void savePGM(image<uchar> *im, const char *name) { + int width = im->width(); + int height = im->height(); + std::ofstream file(name, std::ios::out | std::ios::binary); + + file << "P5\n" << width << " " << height << "\n" << UCHAR_MAX << "\n"; + file.write((char *)imPtr(im, 0, 0), width * height * sizeof(uchar)); +} + +static image<rgb> *loadPPM(const char *name) { + char buf[BUF_SIZE], doc[BUF_SIZE]; + + /* read header */ + std::ifstream file(name, std::ios::in | std::ios::binary); + pnm_read(file, buf); + if (strncmp(buf, "P6", 2)) + throw pnm_error(); + + pnm_read(file, buf); + int width = atoi(buf); + pnm_read(file, buf); + int height = atoi(buf); + + pnm_read(file, buf); + if (atoi(buf) > UCHAR_MAX) + throw pnm_error(); + + /* read data */ + image<rgb> *im = new image<rgb>(width, height); + file.read((char *)imPtr(im, 0, 0), width * height * sizeof(rgb)); + + return im; +} + +static void savePPM(image<rgb> *im, const char *name) { + int width = im->width(); + int height = im->height(); + std::ofstream file(name, std::ios::out | std::ios::binary); + + file << "P6\n" << width << " " << height << "\n" << UCHAR_MAX << "\n"; + file.write((char *)imPtr(im, 0, 0), width * height * sizeof(rgb)); +} + +template <class T> +void load_image(image<T> **im, const char *name) { + char buf[BUF_SIZE]; + + /* read header */ + std::ifstream file(name, std::ios::in | std::ios::binary); + pnm_read(file, buf); + if (strncmp(buf, "VLIB", 9)) + throw pnm_error(); + + pnm_read(file, buf); + int width = atoi(buf); + pnm_read(file, buf); + int height = atoi(buf); + + /* read data */ + *im = new image<T>(width, height); + file.read((char *)imPtr((*im), 0, 0), width * height * sizeof(T)); +} + +template <class T> +void save_image(image<T> *im, const char *name) { + int width = im->width(); + int height = im->height(); + std::ofstream file(name, std::ios::out | std::ios::binary); + + file << "VLIB\n" << width << " " << height << "\n"; + file.write((char *)imPtr(im, 0, 0), width * height * sizeof(T)); +} diff --git a/detectors/include/od/detectors/global2D/localization/Segment-graph.h b/detectors/include/od/detectors/global2D/localization/Segment-graph.h new file mode 100755 index 00000000..c79d1d3b --- /dev/null +++ b/detectors/include/od/detectors/global2D/localization/Segment-graph.h @@ -0,0 +1,81 @@ +/* +Copyright (C) 2006 Pedro Felzenszwalb + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#pragma once + +#include <algorithm> +#include <cmath> +#include "od/detectors/global2D/localization/Disjoint-set.h" + +// threshold function +#define THRESHOLD(size, c) (c/size) + +typedef struct { + float w; + int a, b; +} edge; + +bool operator<(const edge &a, const edge &b) { + return a.w < b.w; +} + +/* + * Segment a graph + * + * Returns a disjoint-set forest representing the segmentation. + * + * num_vertices: number of vertices in graph. + * num_edges: number of edges in graph + * edges: array of edges. + * c: constant for treshold function. + */ +universe *segment_graph(int num_vertices, int num_edges, edge *edges, + float c) { + // sort edges by weight + std::sort(edges, edges + num_edges); + + // make a disjoint-set forest + universe *u = new universe(num_vertices); + + // init thresholds + float *threshold = new float[num_vertices]; + for (int i = 0; i < num_vertices; i++) + threshold[i] = THRESHOLD(1,c); + + // for each edge, in non-decreasing weight order... + for (int i = 0; i < num_edges; i++) { + edge *pedge = &edges[i]; + + // components conected by this edge + int a = u->find(pedge->a); + int b = u->find(pedge->b); + if (a != b) { + if ((pedge->w <= threshold[a]) && + (pedge->w <= threshold[b])) { + u->join(a, b); + a = u->find(a); + threshold[a] = pedge->w + THRESHOLD(u->size(a), c); + } + } + } + + // free up + delete threshold; + return u; +} + diff --git a/detectors/include/od/detectors/global2D/localization/Segment-image.h b/detectors/include/od/detectors/global2D/localization/Segment-image.h new file mode 100755 index 00000000..12080265 --- /dev/null +++ b/detectors/include/od/detectors/global2D/localization/Segment-image.h @@ -0,0 +1,152 @@ +/* +Copyright (C) 2006 Pedro Felzenszwalb + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#pragma once + +#include <cstdlib> +#include "od/detectors/global2D/localization/Image.h" +#include "od/detectors/global2D/localization/Misc.h" +#include "od/detectors/global2D/localization/Filter.h" +#include "od/detectors/global2D/localization/Segment-graph.h" + +// random color +rgb random_rgb(){ + rgb c; + double r; + + c.r = (uchar)random(); + c.g = (uchar)random(); + c.b = (uchar)random(); + + return c; +} + +// dissimilarity measure between pixels +static inline float diff(image<float> *r, image<float> *g, image<float> *b, + int x1, int y1, int x2, int y2) { + return sqrt(square(imRef(r, x1, y1)-imRef(r, x2, y2)) + + square(imRef(g, x1, y1)-imRef(g, x2, y2)) + + square(imRef(b, x1, y1)-imRef(b, x2, y2))); +} + +/* + * Segment an image + * + * Returns a color image representing the segmentation. + * + * im: image to segment. + * sigma: to smooth the image. + * c: constant for treshold function. + * min_size: minimum component size (enforced by post-processing stage). + * num_ccs: number of connected components in the segmentation. + */ +image<rgb> *segment_image(image<rgb> *im, float sigma, float c, int min_size, + int *num_ccs) { + int width = im->width(); + int height = im->height(); + + image<float> *r = new image<float>(width, height); + image<float> *g = new image<float>(width, height); + image<float> *b = new image<float>(width, height); + + // smooth each color channel + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + imRef(r, x, y) = imRef(im, x, y).r; + imRef(g, x, y) = imRef(im, x, y).g; + imRef(b, x, y) = imRef(im, x, y).b; + } + } + image<float> *smooth_r = smooth(r, sigma); + image<float> *smooth_g = smooth(g, sigma); + image<float> *smooth_b = smooth(b, sigma); + delete r; + delete g; + delete b; + + // build graph + edge *edges = new edge[width*height*4]; + int num = 0; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + if (x < width-1) { + edges[num].a = y * width + x; + edges[num].b = y * width + (x+1); + edges[num].w = diff(smooth_r, smooth_g, smooth_b, x, y, x+1, y); + num++; + } + + if (y < height-1) { + edges[num].a = y * width + x; + edges[num].b = (y+1) * width + x; + edges[num].w = diff(smooth_r, smooth_g, smooth_b, x, y, x, y+1); + num++; + } + + if ((x < width-1) && (y < height-1)) { + edges[num].a = y * width + x; + edges[num].b = (y+1) * width + (x+1); + edges[num].w = diff(smooth_r, smooth_g, smooth_b, x, y, x+1, y+1); + num++; + } + + if ((x < width-1) && (y > 0)) { + edges[num].a = y * width + x; + edges[num].b = (y-1) * width + (x+1); + edges[num].w = diff(smooth_r, smooth_g, smooth_b, x, y, x+1, y-1); + num++; + } + } + } + delete smooth_r; + delete smooth_g; + delete smooth_b; + + // segment + universe *u = segment_graph(width*height, num, edges, c); + + // post process small components + for (int i = 0; i < num; i++) { + int a = u->find(edges[i].a); + int b = u->find(edges[i].b); + if ((a != b) && ((u->size(a) < min_size) || (u->size(b) < min_size))) + u->join(a, b); + } + delete [] edges; + *num_ccs = u->num_sets(); + + image<rgb> *output = new image<rgb>(width, height); + + // pick random colors for each component + rgb *colors = new rgb[width*height]; + for (int i = 0; i < width*height; i++) + colors[i] = random_rgb(); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int comp = u->find(y * width + x); + imRef(output, x, y) = colors[comp]; + } + } + + delete [] colors; + delete u; + + return output; +} + diff --git a/detectors/include/od/detectors/global2D/localization/SelectiveSearchBase.h b/detectors/include/od/detectors/global2D/localization/SelectiveSearchBase.h new file mode 100644 index 00000000..fd641102 --- /dev/null +++ b/detectors/include/od/detectors/global2D/localization/SelectiveSearchBase.h @@ -0,0 +1,67 @@ +#pragma once + +#include "od/common/pipeline/Detector.h" +#include "od/common/pipeline/Scene.h" +#include "od/common/utils/Utils.h" +#include "od/common/utils/FeatureDetector2D.h" +#include "od/common/utils/Shared_pointers.h" +#include "od/detectors/global2D/localization/Image.h" +#include "od/detectors/global2D/localization/Misc.h" +#include "od/detectors/global2D/localization/Pnmfile.h" +#include "od/detectors/global2D/localization/Segment-image.h" + +#include <opencv2/opencv.hpp> +#include <opencv2/objdetect/objdetect.hpp> +#include <opencv2/highgui/highgui.hpp> +#include <opencv2/imgproc/imgproc.hpp> +#include <opencv2/core/core.hpp> + +#include <cmath> +#include <cstdio> +#include <vector> +#include <iostream> +#include <algorithm> +#include <stdio.h> +#include <math.h> +#include <stdlib.h> +#include <time.h> +#include <cstdlib> +#include <iomanip> +#include <fstream> +#include <ctime> +#include <cstring> +#include <sstream> +#include <string> +#include <time.h> + +namespace od +{ + namespace g2d + { + class SelectiveSearchBase: public Detector2D + { + public: + + SelectiveSearchBase(const std::string & trained_data_location = std::string("")); + + void acquireImages(const std::string & image_location, int img_width, int img_height); + cv::Mat preProcessImg(const cv::Mat & image); + std::vector<std::vector<int> > getSuperPixels(const cv::Mat & im, int & total_masks, float sigma, float k, float min_size, + const std::string & image_location); + + void init(); + shared_ptr<Detections2D> detectOmni(shared_ptr<SceneImage> scene); + shared_ptr<Detections> detect(shared_ptr<SceneImage> scene); + + private: + + cv::Mat img, cluster, outputImg, sp_preProcessed, gray_mask; + int inputImageHeight; + int inputImageWidth; + int total_masks; + std::vector<std::vector<int> > sp; + }; + } +} + + diff --git a/detectors/include/od/detectors/global2D/localization/SelectiveSearchModel.h b/detectors/include/od/detectors/global2D/localization/SelectiveSearchModel.h new file mode 100644 index 00000000..df118527 --- /dev/null +++ b/detectors/include/od/detectors/global2D/localization/SelectiveSearchModel.h @@ -0,0 +1,95 @@ +#pragma once + +#include "od/common/pipeline/Detector.h" +#include "od/common/pipeline/Scene.h" +#include "od/common/utils/Utils.h" +#include "od/common/utils/FeatureDetector2D.h" +#include "od/common/utils/Shared_pointers.h" + +#include <opencv2/opencv.hpp> +#include <opencv2/objdetect/objdetect.hpp> +#include <opencv2/highgui/highgui.hpp> +#include <opencv2/imgproc/imgproc.hpp> +#include <opencv2/core/core.hpp> + +#include <cmath> +#include <cstdio> +#include <vector> +#include <iostream> +#include <algorithm> +#include <stdio.h> +#include <math.h> +#include <stdlib.h> +#include <time.h> +#include <cstdlib> +#include <iomanip> +#include <fstream> +#include <ctime> +#include <cstring> +#include <sstream> +#include <string> +#include <time.h> + +namespace od +{ + namespace g2d + { + class SelectiveSearchModel: public Detector2D + { + public: + + SelectiveSearchModel(const std::string & trained_data_location = std::string("")); + + void setLabel(int l); + void setBoundaries(int ix, int iy, int ax, int ay); + + void init(); + shared_ptr<Detections2D> detectOmni(shared_ptr<SceneImage> scene); + shared_ptr<Detections> detect(shared_ptr<SceneImage> scene); + shared_ptr<Detections> detectOmni(shared_ptr<Scene> scene); + + int min_x; + int max_x; + int min_y; + int max_y; + bool validity; + int size; + + int label; + + cv::Mat xx_hist, xy_hist, yy_hist, orientation_image_hist, differential_excitation_hist, color_hist; + std::vector<int> neighbors; + + }; + + cv::Mat get_hess_hist_xx(const cv::Mat & regionMask, int histSize, float hist_range_min, float hist_range_max); + + cv::Mat get_hess_hist_xy(const cv::Mat & regionMask, int histSize, float hist_range_min, float hist_range_max); + + cv::Mat get_hess_hist_yy(const cv::Mat & regionMask, int histSize, float hist_range_min, float hist_range_max); + + cv::Mat get_orientation_hist(const cv::Mat & regionMask, int histSize, float hist_range_min, float hist_range_max); + + cv::Mat get_diff_exci_hist(const cv::Mat & regionMask, int histSize, float hist_range_min, float hist_range_max); + + void refineRegions(const std::vector<std::vector<int> > & sp, int total_masks, SelectiveSearchModel regions[], int min_height, int min_width); + + void createModel(const std::vector<std::vector<int> > & sp, int total_masks, const cv::Mat & grayMask, SelectiveSearchModel regions[], + int histSize, float hist_range_min, float hist_range_max); + + bool checkNeighbors(const SelectiveSearchModel & a, const SelectiveSearchModel & b); + + std::vector<std::vector<int> > findNeighbors(SelectiveSearchModel regions[], int total_masks, cv::Mat & regionMask, std::vector<std::vector<int> > & sp); + float calcSimilarities(const SelectiveSearchModel & a, const SelectiveSearchModel & b, float spSize); + + void mergeRegions(int value, SelectiveSearchModel regions[], std::vector<std::vector<int> > & sp_neighbors, cv::Mat & grayMask, + const std::vector<std::vector<int> > & sp, int minRegionSize, int histSize, float hist_range_min, float hist_range_max); + + bool checkRounds(int totals, SelectiveSearchModel regions[], int numRounds); + std::vector<std::vector<int> > extractROIs(int total_masks, SelectiveSearchModel regions[], int numRounds, float spSize, + const std::vector<std::vector<int> > & sp, const cv::Mat & img, const cv::Mat & gray_mask, + int minRegionSize, int histSize, float hist_range_min, float hist_range_max); + } +} + + diff --git a/detectors/include/od/detectors/global2D/training/ActivationWindow.h b/detectors/include/od/detectors/global2D/training/ActivationWindow.h new file mode 100644 index 00000000..c5284b9d --- /dev/null +++ b/detectors/include/od/detectors/global2D/training/ActivationWindow.h @@ -0,0 +1,458 @@ +#include "od/detectors/global2D/training/Network.h" + +void NetworkCreator::showWindow_activationLayerType(Glib::ustring data) +{ + + remove(); + set_title("Activation Layer"); + set_border_width(10); + add(m_sw_activationLayerType); + m_grid_activationLayerType.set_column_spacing (10); + m_grid_activationLayerType.set_row_spacing (50); + + //level 0 + if(data == "") + title_activationLayerType.set_text("Set the Properties of Activation Layer type: AbsVal"); + else + title_activationLayerType.set_text("Set the Properties of Activation Layer type: " + data); + title_activationLayerType.set_line_wrap(); + title_activationLayerType.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_activationLayerType.attach(title_activationLayerType,0,0,2,1); + title_activationLayerType.show(); + + button_setActivationParameters.show(); + button_addMoreLayer.show(); + + //level next + if(data == "" or data == "AbsVal" or data == "PReLU" or data == "Sigmoid" or data == "TanH") + { +/* m_grid_activationLayerType.remove(label_activationLayerBottom); + m_grid_activationLayerType.remove(label_activationLayerName); + m_grid_activationLayerType.remove(label_activationLayerTop); + m_grid_activationLayerType.remove(label_activationLayerScale); + m_grid_activationLayerType.remove(label_activationLayerShift); + m_grid_activationLayerType.remove(label_activationLayerBase); + m_grid_activationLayerType.remove(label_activationLayerNegativeSlope); + m_grid_activationLayerType.remove(text_activationLayerBottom); + m_grid_activationLayerType.remove(text_activationLayerName); + m_grid_activationLayerType.remove(text_activationLayerTop); + m_grid_activationLayerType.remove(text_activationLayerScale); + m_grid_activationLayerType.remove(text_activationLayerShift); + m_grid_activationLayerType.remove(text_activationLayerBase); + m_grid_activationLayerType.remove(text_activationLayerNegativeSlope); +// m_grid_activationLayerType.remove(button_setActivationParameters); +// m_grid_activationLayerType.remove(button_addMoreLayer); +*/ + label_activationLayerBottom.hide(); + label_activationLayerName.hide(); + label_activationLayerTop.hide(); + label_activationLayerScale.hide(); + label_activationLayerShift.hide(); + label_activationLayerBase.hide(); + label_activationLayerNegativeSlope.hide(); + text_activationLayerBottom.hide(); + text_activationLayerName.hide(); + text_activationLayerTop.hide(); + text_activationLayerScale.hide(); + text_activationLayerShift.hide(); + text_activationLayerBase.hide(); + text_activationLayerNegativeSlope.hide(); + + label_activationLayerBottom.set_text("Bottom Layer Name: "); + label_activationLayerBottom.set_line_wrap(); + label_activationLayerBottom.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_activationLayerType.attach(label_activationLayerBottom,0,1,2,1); + label_activationLayerBottom.show(); + + text_activationLayerBottom.set_max_length(100); + text_activationLayerBottom.set_text(""); + text_activationLayerBottom.select_region(0, text_activationLayerBottom.get_text_length()); +// m_grid_activationLayerType.attach(text_activationLayerBottom,2,1,1,1); + text_activationLayerBottom.show(); + + label_activationLayerTop.set_text("Top Layer Name: "); + label_activationLayerTop.set_line_wrap(); + label_activationLayerTop.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_activationLayerType.attach(label_activationLayerTop,0,2,2,1); + label_activationLayerTop.show(); + + text_activationLayerTop.set_max_length(100); + text_activationLayerTop.set_text(""); + text_activationLayerTop.select_region(0, text_activationLayerTop.get_text_length()); +// m_grid_activationLayerType.attach(text_activationLayerTop,2,2,1,1); + text_activationLayerTop.show(); + + label_activationLayerName.set_text("Current Layer Name: "); + label_activationLayerName.set_line_wrap(); + label_activationLayerName.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_activationLayerType.attach(label_activationLayerName,0,3,2,1); + label_activationLayerName.show(); + + text_activationLayerName.set_max_length(100); + text_activationLayerName.set_text(""); + text_activationLayerName.select_region(0, text_activationLayerName.get_text_length()); +// m_grid_activationLayerType.attach(text_activationLayerName,2,3,1,1); + text_activationLayerName.show(); + + } + else if(data == "ReLU") + { +/* m_grid_activationLayerType.remove(label_activationLayerBottom); + m_grid_activationLayerType.remove(label_activationLayerName); + m_grid_activationLayerType.remove(label_activationLayerTop); + m_grid_activationLayerType.remove(label_activationLayerScale); + m_grid_activationLayerType.remove(label_activationLayerShift); + m_grid_activationLayerType.remove(label_activationLayerBase); + m_grid_activationLayerType.remove(label_activationLayerNegativeSlope); + m_grid_activationLayerType.remove(text_activationLayerBottom); + m_grid_activationLayerType.remove(text_activationLayerName); + m_grid_activationLayerType.remove(text_activationLayerTop); + m_grid_activationLayerType.remove(text_activationLayerScale); + m_grid_activationLayerType.remove(text_activationLayerShift); + m_grid_activationLayerType.remove(text_activationLayerBase); + m_grid_activationLayerType.remove(text_activationLayerNegativeSlope); +// m_grid_activationLayerType.remove(button_setActivationParameters); +// m_grid_activationLayerType.remove(button_addMoreLayer); +*/ + label_activationLayerBottom.hide(); + label_activationLayerName.hide(); + label_activationLayerTop.hide(); + label_activationLayerScale.hide(); + label_activationLayerShift.hide(); + label_activationLayerBase.hide(); + label_activationLayerNegativeSlope.hide(); + text_activationLayerBottom.hide(); + text_activationLayerName.hide(); + text_activationLayerTop.hide(); + text_activationLayerScale.hide(); + text_activationLayerShift.hide(); + text_activationLayerBase.hide(); + text_activationLayerNegativeSlope.hide(); + + label_activationLayerBottom.set_text("Bottom Layer Name: "); + label_activationLayerBottom.set_line_wrap(); + label_activationLayerBottom.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_activationLayerType.attach(label_activationLayerBottom,0,1,2,1); + label_activationLayerBottom.show(); + + text_activationLayerBottom.set_max_length(100); + text_activationLayerBottom.set_text(""); + text_activationLayerBottom.select_region(0, text_activationLayerBottom.get_text_length()); +// m_grid_activationLayerType.attach(text_activationLayerBottom,2,1,1,1); + text_activationLayerBottom.show(); + + label_activationLayerTop.set_text("Top Layer Name: "); + label_activationLayerTop.set_line_wrap(); + label_activationLayerTop.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_activationLayerType.attach(label_activationLayerTop,0,2,2,1); + label_activationLayerTop.show(); + + text_activationLayerTop.set_max_length(100); + text_activationLayerTop.set_text(""); + text_activationLayerTop.select_region(0, text_activationLayerTop.get_text_length()); +// m_grid_activationLayerType.attach(text_activationLayerTop,2,2,1,1); + text_activationLayerTop.show(); + + label_activationLayerName.set_text("Current Layer Name: "); + label_activationLayerName.set_line_wrap(); + label_activationLayerName.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_activationLayerType.attach(label_activationLayerName,0,3,2,1); + label_activationLayerName.show(); + + text_activationLayerName.set_max_length(100); + text_activationLayerName.set_text(""); + text_activationLayerName.select_region(0, text_activationLayerName.get_text_length()); +// m_grid_activationLayerType.attach(text_activationLayerName,2,3,1,1); + text_activationLayerName.show(); + + label_activationLayerNegativeSlope.set_text("Relu Param Negative Slope: "); + label_activationLayerNegativeSlope.set_line_wrap(); + label_activationLayerNegativeSlope.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_activationLayerType.attach(label_activationLayerNegativeSlope,0,4,2,1); + label_activationLayerNegativeSlope.show(); + + text_activationLayerNegativeSlope.set_max_length(100); + text_activationLayerNegativeSlope.set_text("0"); + text_activationLayerNegativeSlope.select_region(0, text_activationLayerNegativeSlope.get_text_length()); +// m_grid_activationLayerType.attach(text_activationLayerNegativeSlope,2,4,1,1); + text_activationLayerNegativeSlope.show(); +/* + button_setActivationParameters.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "setActivationParameters")); + m_grid_activationLayerType.attach(button_setActivationParameters,0,5,2,1); + button_setActivationParameters.show(); + + button_addMoreLayer.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "addMoreLayer")); + m_grid_activationLayerType.attach(button_addMoreLayer,2,5,1,1); + button_addMoreLayer.show(); +*/ } + else if(data == "Exp" or data == "Log") + { +/* m_grid_activationLayerType.remove(label_activationLayerBottom); + m_grid_activationLayerType.remove(label_activationLayerName); + m_grid_activationLayerType.remove(label_activationLayerTop); + m_grid_activationLayerType.remove(label_activationLayerScale); + m_grid_activationLayerType.remove(label_activationLayerShift); + m_grid_activationLayerType.remove(label_activationLayerBase); + m_grid_activationLayerType.remove(label_activationLayerNegativeSlope); + m_grid_activationLayerType.remove(text_activationLayerBottom); + m_grid_activationLayerType.remove(text_activationLayerName); + m_grid_activationLayerType.remove(text_activationLayerTop); + m_grid_activationLayerType.remove(text_activationLayerScale); + m_grid_activationLayerType.remove(text_activationLayerShift); + m_grid_activationLayerType.remove(text_activationLayerBase); + m_grid_activationLayerType.remove(text_activationLayerNegativeSlope); +// m_grid_activationLayerType.remove(button_setActivationParameters); +// m_grid_activationLayerType.remove(button_addMoreLayer); +*/ + label_activationLayerBottom.hide(); + label_activationLayerName.hide(); + label_activationLayerTop.hide(); + label_activationLayerScale.hide(); + label_activationLayerShift.hide(); + label_activationLayerBase.hide(); + label_activationLayerNegativeSlope.hide(); + text_activationLayerBottom.hide(); + text_activationLayerName.hide(); + text_activationLayerTop.hide(); + text_activationLayerScale.hide(); + text_activationLayerShift.hide(); + text_activationLayerBase.hide(); + text_activationLayerNegativeSlope.hide(); + + label_activationLayerBottom.set_text("Bottom Layer Name: "); + label_activationLayerBottom.set_line_wrap(); + label_activationLayerBottom.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_activationLayerType.attach(label_activationLayerBottom,0,1,2,1); + label_activationLayerBottom.show(); + + text_activationLayerBottom.set_max_length(100); + text_activationLayerBottom.set_text(""); + text_activationLayerBottom.select_region(0, text_activationLayerBottom.get_text_length()); +// m_grid_activationLayerType.attach(text_activationLayerBottom,2,1,1,1); + text_activationLayerBottom.show(); + + label_activationLayerTop.set_text("Top Layer Name: "); + label_activationLayerTop.set_line_wrap(); + label_activationLayerTop.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_activationLayerType.attach(label_activationLayerTop,0,2,2,1); + label_activationLayerTop.show(); + + text_activationLayerTop.set_max_length(100); + text_activationLayerTop.set_text(""); + text_activationLayerTop.select_region(0, text_activationLayerTop.get_text_length()); +// m_grid_activationLayerType.attach(text_activationLayerTop,2,2,1,1); + text_activationLayerTop.show(); + + label_activationLayerName.set_text("Current Layer Name: "); + label_activationLayerName.set_line_wrap(); + label_activationLayerName.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_activationLayerType.attach(label_activationLayerName,0,3,2,1); + label_activationLayerName.show(); + + text_activationLayerName.set_max_length(100); + text_activationLayerName.set_text(""); + text_activationLayerName.select_region(0, text_activationLayerName.get_text_length()); +// m_grid_activationLayerType.attach(text_activationLayerName,2,3,1,1); + text_activationLayerName.show(); + + label_activationLayerScale.set_text("Layer Parameter Scale: "); + label_activationLayerScale.set_line_wrap(); + label_activationLayerScale.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_activationLayerType.attach(label_activationLayerScale,0,4,2,1); + label_activationLayerScale.show(); + + text_activationLayerScale.set_max_length(100); + text_activationLayerScale.set_text("1"); + text_activationLayerScale.select_region(0, text_activationLayerScale.get_text_length()); +// m_grid_activationLayerType.attach(text_activationLayerScale,2,4,1,1); + text_activationLayerScale.show(); + + label_activationLayerShift.set_text("Layer Parameter Shift: "); + label_activationLayerShift.set_line_wrap(); + label_activationLayerShift.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_activationLayerType.attach(label_activationLayerShift,0,5,2,1); + label_activationLayerShift.show(); + + text_activationLayerShift.set_max_length(100); + text_activationLayerShift.set_text("0"); + text_activationLayerShift.select_region(0, text_activationLayerShift.get_text_length()); +// m_grid_activationLayerType.attach(text_activationLayerShift,2,5,1,1); + text_activationLayerShift.show(); + + label_activationLayerBase.set_text("Layer Parameter Base: "); + label_activationLayerBase.set_line_wrap(); + label_activationLayerBase.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_activationLayerType.attach(label_activationLayerBase,0,6,2,1); + label_activationLayerBase.show(); + + text_activationLayerBase.set_max_length(100); + text_activationLayerBase.set_text("-1"); + text_activationLayerBase.select_region(0, text_activationLayerBase.get_text_length()); +// m_grid_activationLayerType.attach(text_activationLayerBase,2,6,1,1); + text_activationLayerBase.show(); +/* + button_setActivationParameters.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "setActivationParameters")); + m_grid_activationLayerType.attach(button_setActivationParameters,0,7,2,1); + button_setActivationParameters.show(); + + button_addMoreLayer.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "addMoreLayer")); + m_grid_activationLayerType.attach(button_addMoreLayer,2,7,1,1); + button_addMoreLayer.show(); +*/ } + else if(data == "Power") + { +/* m_grid_activationLayerType.remove(label_activationLayerBottom); + m_grid_activationLayerType.remove(label_activationLayerName); + m_grid_activationLayerType.remove(label_activationLayerTop); + m_grid_activationLayerType.remove(label_activationLayerScale); + m_grid_activationLayerType.remove(label_activationLayerShift); + m_grid_activationLayerType.remove(label_activationLayerBase); + m_grid_activationLayerType.remove(label_activationLayerNegativeSlope); + m_grid_activationLayerType.remove(text_activationLayerBottom); + m_grid_activationLayerType.remove(text_activationLayerName); + m_grid_activationLayerType.remove(text_activationLayerTop); + m_grid_activationLayerType.remove(text_activationLayerScale); + m_grid_activationLayerType.remove(text_activationLayerShift); + m_grid_activationLayerType.remove(text_activationLayerBase); + m_grid_activationLayerType.remove(text_activationLayerNegativeSlope); +// m_grid_activationLayerType.remove(button_setActivationParameters); +// m_grid_activationLayerType.remove(button_addMoreLayer); +*/ + label_activationLayerBottom.hide(); + label_activationLayerName.hide(); + label_activationLayerTop.hide(); + label_activationLayerScale.hide(); + label_activationLayerShift.hide(); + label_activationLayerBase.hide(); + label_activationLayerNegativeSlope.hide(); + text_activationLayerBottom.hide(); + text_activationLayerName.hide(); + text_activationLayerTop.hide(); + text_activationLayerScale.hide(); + text_activationLayerShift.hide(); + text_activationLayerBase.hide(); + text_activationLayerNegativeSlope.hide(); + + label_activationLayerBottom.set_text("Bottom Layer Name: "); + label_activationLayerBottom.set_line_wrap(); + label_activationLayerBottom.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_activationLayerType.attach(label_activationLayerBottom,0,1,2,1); + label_activationLayerBottom.show(); + + text_activationLayerBottom.set_max_length(100); + text_activationLayerBottom.set_text(""); + text_activationLayerBottom.select_region(0, text_activationLayerBottom.get_text_length()); +// m_grid_activationLayerType.attach(text_activationLayerBottom,2,1,1,1); + text_activationLayerBottom.show(); + + label_activationLayerTop.set_text("Top Layer Name: "); + label_activationLayerTop.set_line_wrap(); + label_activationLayerTop.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_activationLayerType.attach(label_activationLayerTop,0,2,2,1); + label_activationLayerTop.show(); + + text_activationLayerTop.set_max_length(100); + text_activationLayerTop.set_text(""); + text_activationLayerTop.select_region(0, text_activationLayerTop.get_text_length()); +// m_grid_activationLayerType.attach(text_activationLayerTop,2,2,1,1); + text_activationLayerTop.show(); + + label_activationLayerName.set_text("Current Layer Name: "); + label_activationLayerName.set_line_wrap(); + label_activationLayerName.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_activationLayerType.attach(label_activationLayerName,0,3,2,1); + label_activationLayerName.show(); + + text_activationLayerName.set_max_length(100); + text_activationLayerName.set_text(""); + text_activationLayerName.select_region(0, text_activationLayerName.get_text_length()); +// m_grid_activationLayerType.attach(text_activationLayerName,2,3,1,1); + text_activationLayerName.show(); + + label_activationLayerScale.set_text("Layer Parameter Scale: "); + label_activationLayerScale.set_line_wrap(); + label_activationLayerScale.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_activationLayerType.attach(label_activationLayerScale,0,4,2,1); + label_activationLayerScale.show(); + + text_activationLayerScale.set_max_length(100); + text_activationLayerScale.set_text("1"); + text_activationLayerScale.select_region(0, text_activationLayerScale.get_text_length()); +// m_grid_activationLayerType.attach(text_activationLayerScale,2,4,1,1); + text_activationLayerScale.show(); + + label_activationLayerShift.set_text("Layer Parameter Shift: "); + label_activationLayerShift.set_line_wrap(); + label_activationLayerShift.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_activationLayerType.attach(label_activationLayerShift,0,5,2,1); + label_activationLayerShift.show(); + + text_activationLayerShift.set_max_length(100); + text_activationLayerShift.set_text("0"); + text_activationLayerShift.select_region(0, text_activationLayerShift.get_text_length()); +// m_grid_activationLayerType.attach(text_activationLayerShift,2,5,1,1); + text_activationLayerShift.show(); + + label_activationLayerBase.set_text("Layer Parameter Power: "); + label_activationLayerBase.set_line_wrap(); + label_activationLayerBase.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_activationLayerType.attach(label_activationLayerBase,0,6,2,1); + label_activationLayerBase.show(); + + text_activationLayerBase.set_max_length(100); + text_activationLayerBase.set_text("1"); + text_activationLayerBase.select_region(0, text_activationLayerBase.get_text_length()); +// m_grid_activationLayerType.attach(text_activationLayerBase,2,6,1,1); + text_activationLayerBase.show(); +/* + button_setActivationParameters.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "setActivationParameters")); + m_grid_activationLayerType.attach(button_setActivationParameters,0,7,2,1); + button_setActivationParameters.show(); + + button_addMoreLayer.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "addMoreLayer")); + m_grid_activationLayerType.attach(button_addMoreLayer,2,7,1,1); + button_addMoreLayer.show(); +*/ } +/* else + { + m_grid_activationLayerType.remove(label_activationLayerBottom); + m_grid_activationLayerType.remove(label_activationLayerName); + m_grid_activationLayerType.remove(label_activationLayerTop); + m_grid_activationLayerType.remove(label_activationLayerScale); + m_grid_activationLayerType.remove(label_activationLayerShift); + m_grid_activationLayerType.remove(label_activationLayerBase); + m_grid_activationLayerType.remove(label_activationLayerNegativeSlope); + m_grid_activationLayerType.remove(text_activationLayerBottom); + m_grid_activationLayerType.remove(text_activationLayerName); + m_grid_activationLayerType.remove(text_activationLayerTop); + m_grid_activationLayerType.remove(text_activationLayerScale); + m_grid_activationLayerType.remove(text_activationLayerShift); + m_grid_activationLayerType.remove(text_activationLayerBase); + m_grid_activationLayerType.remove(text_activationLayerNegativeSlope); + m_grid_activationLayerType.remove(button_setActivationParameters); + m_grid_activationLayerType.remove(button_addMoreLayer); + + + button_setActivationParameters.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "setActivationParameters")); + m_grid_activationLayerType.attach(button_setActivationParameters,0,1,7,1); + button_setActivationParameters.show(); + + button_addMoreLayer.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "addMoreLayer")); + m_grid_activationLayerType.attach(button_addMoreLayer,2,1,7,1); + button_addMoreLayer.show(); + } +*/ +// m_sw_activationLayerType.add(m_grid_activationLayerType); + m_sw_activationLayerType.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); + m_grid_activationLayerType.show(); +// show_all_children(); + m_sw_activationLayerType.show(); +} diff --git a/detectors/include/od/detectors/global2D/training/ConvTrainer.h b/detectors/include/od/detectors/global2D/training/ConvTrainer.h new file mode 100644 index 00000000..02894858 --- /dev/null +++ b/detectors/include/od/detectors/global2D/training/ConvTrainer.h @@ -0,0 +1,42 @@ +#pragma once +#include "od/common/pipeline/Trainer.h" +#include "od/common/utils/Utils.h" +#include "od/detectors/global2D/training/Solver.h" +#include "od/detectors/global2D/training/Network.h" +#include <opencv2/opencv.hpp> + +#include <cstdlib> +#include <vector> +#include <fstream> +#include <string> +#include <iostream> +#include <stdio.h> +#include <caffe/caffe.hpp> +#include <caffe/util/io.hpp> +#include <caffe/blob.hpp> +#include <caffe/solver.hpp> +#include <caffe/sgd_solvers.hpp> + +namespace od +{ + namespace g2d + { + class ConvTrainer : public Trainer + { + public: + ConvTrainer(const std::string & training_input_location_ = std::string(""), + const std::string & trained_data_location_ = std::string("")); + int train(); + void init(){} + void setSolverLocation(const std::string & location); + void setSolverProperties(int argc, char *argv[]); + void startTraining(); + void createCustomNetwork(int argc, char *argv[]); + + private: + std::string solverLocation; + }; + + } +} + diff --git a/detectors/include/od/detectors/global2D/training/CriticalWindow.h b/detectors/include/od/detectors/global2D/training/CriticalWindow.h new file mode 100644 index 00000000..6e713e6c --- /dev/null +++ b/detectors/include/od/detectors/global2D/training/CriticalWindow.h @@ -0,0 +1,1240 @@ +#include "od/detectors/global2D/training/Network.h" + +void NetworkCreator::showWindow_criticalLayerType(Glib::ustring data) +{ + + remove(); + set_title("Critical Layer"); + set_border_width(10); + add(m_sw_criticalLayerType); + m_grid_criticalLayerType.set_column_spacing (10); + m_grid_criticalLayerType.set_row_spacing (50); + + //level 0 + if(data == "") + title_criticalLayerType.set_text("Set the Properties of Critical Layer type: Accuracy"); + else + title_criticalLayerType.set_text("Set the Properties of Critical Layer type: " + data); + title_criticalLayerType.set_line_wrap(); + title_criticalLayerType.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(title_criticalLayerType,0,0,2,1); + title_criticalLayerType.show(); + + button_setCriticalParameters.show(); + button_addMoreLayer2.show(); + + if(data == "" or data == "Accuracy" or data == "Softmax") + { + label_criticalLayerBottom1.hide(); + text_criticalLayerBottom1.hide(); + label_criticalLayerBottom2.hide(); + text_criticalLayerBottom2.hide(); + label_criticalLayerTop.hide(); + text_criticalLayerTop.hide(); + label_criticalLayerName.hide(); + text_criticalLayerName.hide(); + label_criticalLayerFilterLr.hide(); + text_criticalLayerFilterLr.hide(); + label_criticalLayerFilterDm.hide(); + text_criticalLayerFilterDm.hide(); + label_criticalLayerBiasLr.hide(); + text_criticalLayerBiasLr.hide(); + label_criticalLayerBiasDm.hide(); + text_criticalLayerBiasDm.hide(); + label_criticalLayerNumOutput.hide(); + text_criticalLayerNumOutput.hide(); + label_criticalLayerKernelW.hide(); + text_criticalLayerKernelW.hide(); + label_criticalLayerKernelH.hide(); + text_criticalLayerKernelH.hide(); + label_criticalLayerStrideW.hide(); + text_criticalLayerStrideW.hide(); + label_criticalLayerStrideH.hide(); + text_criticalLayerStrideH.hide(); + label_criticalLayerPadW.hide(); + text_criticalLayerPadW.hide(); + label_criticalLayerPadH.hide(); + text_criticalLayerPadH.hide(); + label_criticalLayerWeightFiller.hide(); + rbutton_criticalLayerWeightFillerConstant.hide(); + rbutton_criticalLayerWeightFillerUniform.hide(); + rbutton_criticalLayerWeightGaussian.hide(); + rbutton_criticalLayerWeightFillerPositiveUnitBall.hide(); + rbutton_criticalLayerWeightFillerXavier.hide(); + rbutton_criticalLayerWeightFillerMSRA.hide(); + rbutton_criticalLayerWeightFillerBilinear.hide(); + label_criticalLayerWeightFillerConstantValue.hide(); + text_criticalLayerWeightFillerConstantValue.hide(); + label_criticalLayerWeightFillerUniformMin.hide(); + label_criticalLayerWeightFillerUniformMax.hide(); + text_criticalLayerWeightFillerUniformMin.hide(); + text_criticalLayerWeightFillerUniformMax.hide(); + label_criticalLayerWeightFillerGaussianMean.hide(); + text_criticalLayerWeightFillerGaussianMean.hide(); + label_criticalLayerWeightFillerGaussianStd.hide(); + text_criticalLayerWeightFillerGaussianStd.hide(); + label_criticalLayerWeightFillerXavierVariance.hide(); + rbutton_criticalLayerWeightFillerXavierIn.hide(); + rbutton_criticalLayerWeightFillerXavierOut.hide(); + rbutton_criticalLayerWeightFillerXavierAvg.hide(); + label_criticalLayerWeightFillerMSRAVariance.hide(); + rbutton_criticalLayerWeightFillerMSRAIn.hide(); + rbutton_criticalLayerWeightFillerMSRAOut.hide(); + rbutton_criticalLayerWeightFillerMSRAAvg.hide(); + label_criticalLayerDropoutRatio.hide(); + text_criticalLayerDropoutRatio.hide(); + label_criticalLayerPool.hide(); + label_criticalLayerBias.hide(); + text_criticalLayerBias.hide(); + rbutton_criticalLayerPoolMax.hide(); + rbutton_criticalLayerPoolAve.hide(); + + + label_criticalLayerBottom1.set_text("Bottom1 Layer Name: "); + label_criticalLayerBottom1.set_line_wrap(); + label_criticalLayerBottom1.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerBottom1,0,1,2,1); + label_criticalLayerBottom1.show(); + + text_criticalLayerBottom1.set_max_length(100); + text_criticalLayerBottom1.set_text(""); + text_criticalLayerBottom1.select_region(0, text_criticalLayerBottom1.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerBottom1,2,1,1,1); + text_criticalLayerBottom1.show(); + + label_criticalLayerBottom2.set_text("Bottom2 Layer Name: "); + label_criticalLayerBottom2.set_line_wrap(); + label_criticalLayerBottom2.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerBottom2,0,1,2,1); + label_criticalLayerBottom2.show(); + + text_criticalLayerBottom2.set_max_length(100); + text_criticalLayerBottom2.set_text(""); + text_criticalLayerBottom2.select_region(0, text_criticalLayerBottom2.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerBottom2,2,1,1,1); + text_criticalLayerBottom2.show(); + + label_criticalLayerTop.set_text("Top Layer Name: "); + label_criticalLayerTop.set_line_wrap(); + label_criticalLayerTop.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerTop,0,3,2,1); + label_criticalLayerTop.show(); + + text_criticalLayerTop.set_max_length(100); + text_criticalLayerTop.set_text(""); + text_criticalLayerTop.select_region(0, text_criticalLayerTop.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerTop,2,3,1,1); + text_criticalLayerTop.show(); + + label_criticalLayerName.set_text("Current Layer Name: "); + label_criticalLayerName.set_line_wrap(); + label_criticalLayerName.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerName,0,4,2,1); + label_criticalLayerName.show(); + + text_criticalLayerName.set_max_length(100); + text_criticalLayerName.set_text(""); + text_criticalLayerName.select_region(0, text_criticalLayerName.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerName,2,4,1,1); + text_criticalLayerName.show(); + } + else if( data == "Concat") + { + label_criticalLayerBottom1.hide(); + text_criticalLayerBottom1.hide(); + label_criticalLayerBottom2.hide(); + text_criticalLayerBottom2.hide(); + label_criticalLayerTop.hide(); + text_criticalLayerTop.hide(); + label_criticalLayerName.hide(); + text_criticalLayerName.hide(); + label_criticalLayerFilterLr.hide(); + text_criticalLayerFilterLr.hide(); + label_criticalLayerFilterDm.hide(); + text_criticalLayerFilterDm.hide(); + label_criticalLayerBiasLr.hide(); + text_criticalLayerBiasLr.hide(); + label_criticalLayerBiasDm.hide(); + text_criticalLayerBiasDm.hide(); + label_criticalLayerNumOutput.hide(); + text_criticalLayerNumOutput.hide(); + label_criticalLayerKernelW.hide(); + text_criticalLayerKernelW.hide(); + label_criticalLayerKernelH.hide(); + text_criticalLayerKernelH.hide(); + label_criticalLayerStrideW.hide(); + text_criticalLayerStrideW.hide(); + label_criticalLayerStrideH.hide(); + text_criticalLayerStrideH.hide(); + label_criticalLayerPadW.hide(); + text_criticalLayerPadW.hide(); + label_criticalLayerPadH.hide(); + text_criticalLayerPadH.hide(); + label_criticalLayerWeightFiller.hide(); + rbutton_criticalLayerWeightFillerConstant.hide(); + rbutton_criticalLayerWeightFillerUniform.hide(); + rbutton_criticalLayerWeightGaussian.hide(); + rbutton_criticalLayerWeightFillerPositiveUnitBall.hide(); + rbutton_criticalLayerWeightFillerXavier.hide(); + rbutton_criticalLayerWeightFillerMSRA.hide(); + rbutton_criticalLayerWeightFillerBilinear.hide(); + label_criticalLayerWeightFillerConstantValue.hide(); + text_criticalLayerWeightFillerConstantValue.hide(); + label_criticalLayerWeightFillerUniformMin.hide(); + label_criticalLayerWeightFillerUniformMax.hide(); + text_criticalLayerWeightFillerUniformMin.hide(); + text_criticalLayerWeightFillerUniformMax.hide(); + label_criticalLayerWeightFillerGaussianMean.hide(); + text_criticalLayerWeightFillerGaussianMean.hide(); + label_criticalLayerWeightFillerGaussianStd.hide(); + text_criticalLayerWeightFillerGaussianStd.hide(); + label_criticalLayerWeightFillerXavierVariance.hide(); + rbutton_criticalLayerWeightFillerXavierIn.hide(); + rbutton_criticalLayerWeightFillerXavierOut.hide(); + rbutton_criticalLayerWeightFillerXavierAvg.hide(); + label_criticalLayerWeightFillerMSRAVariance.hide(); + rbutton_criticalLayerWeightFillerMSRAIn.hide(); + rbutton_criticalLayerWeightFillerMSRAOut.hide(); + rbutton_criticalLayerWeightFillerMSRAAvg.hide();\ + label_criticalLayerDropoutRatio.hide(); + text_criticalLayerDropoutRatio.hide(); + label_criticalLayerPool.hide(); + label_criticalLayerBias.hide(); + text_criticalLayerBias.hide(); + rbutton_criticalLayerPoolMax.hide(); + rbutton_criticalLayerPoolAve.hide(); + + + label_criticalLayerBottom1.set_text("Facility will be added soon"); + label_criticalLayerBottom1.set_line_wrap(); + label_criticalLayerBottom1.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerBottom1,0,1,2,1); + label_criticalLayerBottom1.show(); + } + else if( data == "Convolution" or data == "Deconvolution") + { + label_criticalLayerBottom1.hide(); + text_criticalLayerBottom1.hide(); + label_criticalLayerBottom2.hide(); + text_criticalLayerBottom2.hide(); + label_criticalLayerTop.hide(); + text_criticalLayerTop.hide(); + label_criticalLayerName.hide(); + text_criticalLayerName.hide(); + label_criticalLayerFilterLr.hide(); + text_criticalLayerFilterLr.hide(); + label_criticalLayerFilterDm.hide(); + text_criticalLayerFilterDm.hide(); + label_criticalLayerBiasLr.hide(); + text_criticalLayerBiasLr.hide(); + label_criticalLayerBiasDm.hide(); + text_criticalLayerBiasDm.hide(); + label_criticalLayerNumOutput.hide(); + text_criticalLayerNumOutput.hide(); + label_criticalLayerKernelW.hide(); + text_criticalLayerKernelW.hide(); + label_criticalLayerKernelH.hide(); + text_criticalLayerKernelH.hide(); + label_criticalLayerStrideW.hide(); + text_criticalLayerStrideW.hide(); + label_criticalLayerStrideH.hide(); + text_criticalLayerStrideH.hide(); + label_criticalLayerPadW.hide(); + text_criticalLayerPadW.hide(); + label_criticalLayerPadH.hide(); + text_criticalLayerPadH.hide(); + label_criticalLayerWeightFiller.hide(); + rbutton_criticalLayerWeightFillerConstant.hide(); + rbutton_criticalLayerWeightFillerUniform.hide(); + rbutton_criticalLayerWeightGaussian.hide(); + rbutton_criticalLayerWeightFillerPositiveUnitBall.hide(); + rbutton_criticalLayerWeightFillerXavier.hide(); + rbutton_criticalLayerWeightFillerMSRA.hide(); + rbutton_criticalLayerWeightFillerBilinear.hide(); + label_criticalLayerWeightFillerConstantValue.hide(); + text_criticalLayerWeightFillerConstantValue.hide(); + label_criticalLayerWeightFillerUniformMin.hide(); + label_criticalLayerWeightFillerUniformMax.hide(); + text_criticalLayerWeightFillerUniformMin.hide(); + text_criticalLayerWeightFillerUniformMax.hide(); + label_criticalLayerWeightFillerGaussianMean.hide(); + text_criticalLayerWeightFillerGaussianMean.hide(); + label_criticalLayerWeightFillerGaussianStd.hide(); + text_criticalLayerWeightFillerGaussianStd.hide(); + label_criticalLayerWeightFillerXavierVariance.hide(); + rbutton_criticalLayerWeightFillerXavierIn.hide(); + rbutton_criticalLayerWeightFillerXavierOut.hide(); + rbutton_criticalLayerWeightFillerXavierAvg.hide(); + label_criticalLayerWeightFillerMSRAVariance.hide(); + rbutton_criticalLayerWeightFillerMSRAIn.hide(); + rbutton_criticalLayerWeightFillerMSRAOut.hide(); + rbutton_criticalLayerWeightFillerMSRAAvg.hide(); + label_criticalLayerDropoutRatio.hide(); + text_criticalLayerDropoutRatio.hide(); + label_criticalLayerPool.hide(); + label_criticalLayerBias.hide(); + text_criticalLayerBias.hide(); + rbutton_criticalLayerPoolMax.hide(); + rbutton_criticalLayerPoolAve.hide(); + + label_criticalLayerBottom1.set_text("Bottom1 Layer Name: "); + label_criticalLayerBottom1.set_line_wrap(); + label_criticalLayerBottom1.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerBottom1,0,1,2,1); + label_criticalLayerBottom1.show(); + + text_criticalLayerBottom1.set_max_length(100); + text_criticalLayerBottom1.set_text(""); + text_criticalLayerBottom1.select_region(0, text_criticalLayerBottom1.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerBottom1,2,1,1,1); + text_criticalLayerBottom1.show(); + + label_criticalLayerTop.set_text("Top Layer Name: "); + label_criticalLayerTop.set_line_wrap(); + label_criticalLayerTop.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerTop,0,3,2,1); + label_criticalLayerTop.show(); + + text_criticalLayerTop.set_max_length(100); + text_criticalLayerTop.set_text(""); + text_criticalLayerTop.select_region(0, text_criticalLayerTop.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerTop,2,3,1,1); + text_criticalLayerTop.show(); + + label_criticalLayerName.set_text("Current Layer Name: "); + label_criticalLayerName.set_line_wrap(); + label_criticalLayerName.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerName,0,4,2,1); + label_criticalLayerName.show(); + + text_criticalLayerName.set_max_length(100); + text_criticalLayerName.set_text(""); + text_criticalLayerName.select_region(0, text_criticalLayerName.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerName,2,4,1,1); + text_criticalLayerName.show(); + + label_criticalLayerFilterLr.set_text("Set filter lr_mult:\n(Leave unchanged if not needed) "); + label_criticalLayerFilterLr.set_line_wrap(); + label_criticalLayerFilterLr.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerFilterLr,0,5,2,1); + label_criticalLayerFilterLr.show(); + + text_criticalLayerFilterLr.set_max_length(100); + text_criticalLayerFilterLr.set_text("1"); + text_criticalLayerFilterLr.select_region(0, text_criticalLayerFilterLr.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerFilterLr,2,5,1,1); + text_criticalLayerFilterLr.show(); + + label_criticalLayerFilterDm.set_text("Set filter decay_mult:\n(Leave unchanged if not needed) "); + label_criticalLayerFilterDm.set_line_wrap(); + label_criticalLayerFilterDm.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerFilterDm,0,6,2,1); + label_criticalLayerFilterDm.show(); + + text_criticalLayerFilterDm.set_max_length(100); + text_criticalLayerFilterDm.set_text("1"); + text_criticalLayerFilterDm.select_region(0, text_criticalLayerFilterDm.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerFilterDm,2,6,1,1); + text_criticalLayerFilterDm.show(); + + label_criticalLayerBiasLr.set_text("Set bias lr_mult:\n(Leave unchanged if not needed) "); + label_criticalLayerBiasLr.set_line_wrap(); + label_criticalLayerBiasLr.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerBiasLr,0,7,2,1); + label_criticalLayerBiasLr.show(); + + text_criticalLayerBiasLr.set_max_length(100); + text_criticalLayerBiasLr.set_text("2"); + text_criticalLayerBiasLr.select_region(0, text_criticalLayerBiasLr.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerBiasLr,2,7,1,1); + text_criticalLayerBiasLr.show(); + + label_criticalLayerBiasDm.set_text("Set bias decay_mult:\n(Leave unchanged if not needed) "); + label_criticalLayerBiasDm.set_line_wrap(); + label_criticalLayerBiasDm.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerBiasDm,0,8,2,1); + label_criticalLayerBiasDm.show(); + + text_criticalLayerBiasDm.set_max_length(100); + text_criticalLayerBiasDm.set_text("0"); + text_criticalLayerBiasDm.select_region(0, text_criticalLayerBiasDm.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerBiasDm,2,8,1,1); + text_criticalLayerBiasDm.show(); + + label_criticalLayerNumOutput.set_text("Set param num_output: "); + label_criticalLayerNumOutput.set_line_wrap(); + label_criticalLayerNumOutput.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerNumOutput,0,9,2,1); + label_criticalLayerNumOutput.show(); + + text_criticalLayerNumOutput.set_max_length(100); + text_criticalLayerNumOutput.set_text("64"); + text_criticalLayerNumOutput.select_region(0, text_criticalLayerNumOutput.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerNumOutput,2,9,1,1); + text_criticalLayerNumOutput.show(); + + label_criticalLayerKernelW.set_text("Set param kernel_w: "); + label_criticalLayerKernelW.set_line_wrap(); + label_criticalLayerKernelW.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerKernelW,0,10,2,1); + label_criticalLayerKernelW.show(); + + text_criticalLayerKernelW.set_max_length(100); + text_criticalLayerKernelW.set_text("3"); + text_criticalLayerKernelW.select_region(0, text_criticalLayerKernelW.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerKernelW,2,10,1,1); + text_criticalLayerKernelW.show(); + + label_criticalLayerKernelH.set_text("Set param kernel_h: "); + label_criticalLayerKernelH.set_line_wrap(); + label_criticalLayerKernelH.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerKernelH,0,11,2,1); + label_criticalLayerKernelH.show(); + + text_criticalLayerKernelH.set_max_length(100); + text_criticalLayerKernelH.set_text("3"); + text_criticalLayerKernelH.select_region(0, text_criticalLayerKernelH.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerKernelH,2,11,1,1); + text_criticalLayerKernelH.show(); + + label_criticalLayerStrideW.set_text("Set param stride_w: "); + label_criticalLayerStrideW.set_line_wrap(); + label_criticalLayerStrideW.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerStrideW,0,12,2,1); + label_criticalLayerStrideW.show(); + + text_criticalLayerStrideW.set_max_length(100); + text_criticalLayerStrideW.set_text("1"); + text_criticalLayerStrideW.select_region(0, text_criticalLayerStrideW.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerStrideW,2,12,1,1); + text_criticalLayerStrideW.show(); + + label_criticalLayerStrideH.set_text("Set param stride_h: "); + label_criticalLayerStrideH.set_line_wrap(); + label_criticalLayerStrideH.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerStrideH,0,13,2,1); + label_criticalLayerStrideH.show(); + + text_criticalLayerStrideH.set_max_length(100); + text_criticalLayerStrideH.set_text("1"); + text_criticalLayerStrideH.select_region(0, text_criticalLayerStrideH.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerStrideH,2,13,1,1); + text_criticalLayerStrideH.show(); + + label_criticalLayerPadW.set_text("Set param pad_w: "); + label_criticalLayerPadW.set_line_wrap(); + label_criticalLayerPadW.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerPadW,0,14,2,1); + label_criticalLayerPadW.show(); + + text_criticalLayerPadW.set_max_length(100); + text_criticalLayerPadW.set_text("1"); + text_criticalLayerPadW.select_region(0, text_criticalLayerPadW.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerPadW,2,14,1,1); + text_criticalLayerPadW.show(); + + label_criticalLayerPadH.set_text("Set param pad_h: "); + label_criticalLayerPadH.set_line_wrap(); + label_criticalLayerPadH.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerPadH,0,14,2,1); + label_criticalLayerPadH.show(); + + text_criticalLayerPadH.set_max_length(100); + text_criticalLayerPadH.set_text("1"); + text_criticalLayerPadH.select_region(0, text_criticalLayerPadH.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerPadH,2,14,1,1); + text_criticalLayerPadH.show(); + + label_criticalLayerWeightFiller.set_text("Set Weight Filler : "); + label_criticalLayerWeightFiller.set_line_wrap(); + label_criticalLayerWeightFiller.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerWeightFiller,0,16,2,1); + label_criticalLayerWeightFiller.show(); + +// Gtk::RadioButton::Group group1 = rbutton_criticalLayerWeightFillerConstant.get_group(); +// rbutton_criticalLayerWeightFillerUniform.set_group(group1); +// rbutton_criticalLayerWeightGaussian.set_group(group1); +// rbutton_criticalLayerWeightFillerPositiveUnitBall.set_group(group1); +// rbutton_criticalLayerWeightFillerXavier.set_group(group1); +// rbutton_criticalLayerWeightFillerMSRA.set_group(group1); +// rbutton_criticalLayerWeightFillerBilinear.set_group(group1); +// rbutton_criticalLayerWeightFillerConstant.set_active(); +// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerConstant,2,17,1,1); + rbutton_criticalLayerWeightFillerConstant.show(); +// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerUniform,2,18,1,1); + rbutton_criticalLayerWeightFillerUniform.show(); +// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightGaussian,2,19,1,1); + rbutton_criticalLayerWeightGaussian.show(); +// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerPositiveUnitBall,2,20,1,1); + rbutton_criticalLayerWeightFillerPositiveUnitBall.show(); +// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavier,2,21,1,1); + rbutton_criticalLayerWeightFillerXavier.show(); +// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerMSRA,2,22,1,1); + rbutton_criticalLayerWeightFillerMSRA.show(); +// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerBilinear,2,23,1,1); + rbutton_criticalLayerWeightFillerBilinear.show(); + + label_criticalLayerWeightFillerConstantValue.set_text("value: "); + label_criticalLayerWeightFillerConstantValue.set_line_wrap(); + label_criticalLayerWeightFillerConstantValue.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerConstantValue,4,17,1,1); + label_criticalLayerWeightFillerConstantValue.show(); + + text_criticalLayerWeightFillerConstantValue.set_max_length(100); + text_criticalLayerWeightFillerConstantValue.set_text("0.5"); + text_criticalLayerWeightFillerConstantValue.select_region(0, text_criticalLayerWeightFillerConstantValue.get_text_length()); +// m_grid_criticalLayerWeightFillerConstantValue.attach(text_criticalLayerWeightFillerConstantValue,5,15,1,1); + text_criticalLayerWeightFillerConstantValue.show(); + + label_criticalLayerWeightFillerUniformMin.set_text("min: "); + label_criticalLayerWeightFillerUniformMin.set_line_wrap(); + label_criticalLayerWeightFillerUniformMin.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerUniformMin,4,18,1,1); + label_criticalLayerWeightFillerUniformMin.show(); + + text_criticalLayerWeightFillerUniformMin.set_max_length(100); + text_criticalLayerWeightFillerUniformMin.set_text("0"); + text_criticalLayerWeightFillerUniformMin.select_region(0, text_criticalLayerWeightFillerUniformMin.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerWeightFillerUniformMin,5,18,1,1); + text_criticalLayerWeightFillerUniformMin.show(); + + label_criticalLayerWeightFillerUniformMax.set_text("max: "); + label_criticalLayerWeightFillerUniformMax.set_line_wrap(); + label_criticalLayerWeightFillerUniformMax.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerUniformMax,4,19,1,1); + label_criticalLayerWeightFillerUniformMax.show(); + + text_criticalLayerWeightFillerUniformMax.set_max_length(100); + text_criticalLayerWeightFillerUniformMax.set_text("1"); + text_criticalLayerWeightFillerUniformMax.select_region(0, text_criticalLayerWeightFillerUniformMax.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerWeightFillerUniformMax,5,19,1,1); + text_criticalLayerWeightFillerUniformMax.show(); + + label_criticalLayerWeightFillerGaussianMean.set_text("mean: "); + label_criticalLayerWeightFillerGaussianMean.set_line_wrap(); + label_criticalLayerWeightFillerGaussianMean.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerGaussianMean,4,19,1,1); + label_criticalLayerWeightFillerGaussianMean.show(); + + text_criticalLayerWeightFillerGaussianMean.set_max_length(100); + text_criticalLayerWeightFillerGaussianMean.set_text("0"); + text_criticalLayerWeightFillerGaussianMean.select_region(0, text_criticalLayerWeightFillerGaussianMean.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerWeightFillerGaussianMean,5,19,1,1); + text_criticalLayerWeightFillerGaussianMean.show(); + + label_criticalLayerWeightFillerGaussianStd.set_text("std: "); + label_criticalLayerWeightFillerGaussianStd.set_line_wrap(); + label_criticalLayerWeightFillerGaussianStd.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerGaussianStd,6,19,1,1); + label_criticalLayerWeightFillerGaussianStd.show(); + + text_criticalLayerWeightFillerGaussianStd.set_max_length(100); + text_criticalLayerWeightFillerGaussianStd.set_text("0.1"); + text_criticalLayerWeightFillerGaussianStd.select_region(0, text_criticalLayerWeightFillerGaussianStd.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerWeightFillerGaussianStd,7,19,1,1); + text_criticalLayerWeightFillerGaussianStd.show(); + + label_criticalLayerWeightFillerXavierVariance.set_text("variance_norm: "); + label_criticalLayerWeightFillerXavierVariance.set_line_wrap(); + label_criticalLayerWeightFillerXavierVariance.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerXavierVariance,4,20,1,1); + label_criticalLayerWeightFillerXavierVariance.show(); + +// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavierIn,5,20,1,1); + rbutton_criticalLayerWeightFillerXavierIn.show(); +// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavierOut,6,20,1,1); + rbutton_criticalLayerWeightFillerXavierOut.show(); +// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavierAvg,7,20,1,1); + rbutton_criticalLayerWeightFillerXavierAvg.show(); + + label_criticalLayerWeightFillerMSRAVariance.set_text("variance_norm: "); + label_criticalLayerWeightFillerMSRAVariance.set_line_wrap(); + label_criticalLayerWeightFillerMSRAVariance.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerMSRAVariance,4,20,1,1); + label_criticalLayerWeightFillerMSRAVariance.show(); + +// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavierIn,5,20,1,1); + rbutton_criticalLayerWeightFillerMSRAIn.show(); +// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavierOut,6,20,1,1); + rbutton_criticalLayerWeightFillerMSRAOut.show(); +// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavierAvg,7,20,1,1); + rbutton_criticalLayerWeightFillerMSRAAvg.show(); + + label_criticalLayerBias.set_text("Set bias value: "); + label_criticalLayerBias.set_line_wrap(); + label_criticalLayerBias.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerBias,0,24,2,1); + label_criticalLayerBias.show(); + + text_criticalLayerBias.set_max_length(100); + text_criticalLayerBias.set_text("0.1"); + text_criticalLayerBias.select_region(0, text_criticalLayerBias.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerBias,2,24,1,1); + text_criticalLayerBias.show(); + } + else if(data == "InnerProduct") + { + label_criticalLayerBottom1.hide(); + text_criticalLayerBottom1.hide(); + label_criticalLayerBottom2.hide(); + text_criticalLayerBottom2.hide(); + label_criticalLayerTop.hide(); + text_criticalLayerTop.hide(); + label_criticalLayerName.hide(); + text_criticalLayerName.hide(); + label_criticalLayerFilterLr.hide(); + text_criticalLayerFilterLr.hide(); + label_criticalLayerFilterDm.hide(); + text_criticalLayerFilterDm.hide(); + label_criticalLayerBiasLr.hide(); + text_criticalLayerBiasLr.hide(); + label_criticalLayerBiasDm.hide(); + text_criticalLayerBiasDm.hide(); + label_criticalLayerNumOutput.hide(); + text_criticalLayerNumOutput.hide(); + label_criticalLayerKernelW.hide(); + text_criticalLayerKernelW.hide(); + label_criticalLayerKernelH.hide(); + text_criticalLayerKernelH.hide(); + label_criticalLayerStrideW.hide(); + text_criticalLayerStrideW.hide(); + label_criticalLayerStrideH.hide(); + text_criticalLayerStrideH.hide(); + label_criticalLayerPadW.hide(); + text_criticalLayerPadW.hide(); + label_criticalLayerPadH.hide(); + text_criticalLayerPadH.hide(); + label_criticalLayerWeightFiller.hide(); + rbutton_criticalLayerWeightFillerConstant.hide(); + rbutton_criticalLayerWeightFillerUniform.hide(); + rbutton_criticalLayerWeightGaussian.hide(); + rbutton_criticalLayerWeightFillerPositiveUnitBall.hide(); + rbutton_criticalLayerWeightFillerXavier.hide(); + rbutton_criticalLayerWeightFillerMSRA.hide(); + rbutton_criticalLayerWeightFillerBilinear.hide(); + label_criticalLayerWeightFillerConstantValue.hide(); + text_criticalLayerWeightFillerConstantValue.hide(); + label_criticalLayerWeightFillerUniformMin.hide(); + label_criticalLayerWeightFillerUniformMax.hide(); + text_criticalLayerWeightFillerUniformMin.hide(); + text_criticalLayerWeightFillerUniformMax.hide(); + label_criticalLayerWeightFillerGaussianMean.hide(); + text_criticalLayerWeightFillerGaussianMean.hide(); + label_criticalLayerWeightFillerGaussianStd.hide(); + text_criticalLayerWeightFillerGaussianStd.hide(); + label_criticalLayerWeightFillerXavierVariance.hide(); + rbutton_criticalLayerWeightFillerXavierIn.hide(); + rbutton_criticalLayerWeightFillerXavierOut.hide(); + rbutton_criticalLayerWeightFillerXavierAvg.hide(); + label_criticalLayerWeightFillerMSRAVariance.hide(); + rbutton_criticalLayerWeightFillerMSRAIn.hide(); + rbutton_criticalLayerWeightFillerMSRAOut.hide(); + rbutton_criticalLayerWeightFillerMSRAAvg.hide(); + label_criticalLayerDropoutRatio.hide(); + text_criticalLayerDropoutRatio.hide(); + label_criticalLayerPool.hide(); + label_criticalLayerBias.hide(); + text_criticalLayerBias.hide(); + rbutton_criticalLayerPoolMax.hide(); + rbutton_criticalLayerPoolAve.hide(); + + label_criticalLayerBottom1.set_text("Bottom1 Layer Name: "); + label_criticalLayerBottom1.set_line_wrap(); + label_criticalLayerBottom1.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerBottom1,0,1,2,1); + label_criticalLayerBottom1.show(); + + text_criticalLayerBottom1.set_max_length(100); + text_criticalLayerBottom1.set_text(""); + text_criticalLayerBottom1.select_region(0, text_criticalLayerBottom1.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerBottom1,2,1,1,1); + text_criticalLayerBottom1.show(); + + label_criticalLayerTop.set_text("Top Layer Name: "); + label_criticalLayerTop.set_line_wrap(); + label_criticalLayerTop.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerTop,0,3,2,1); + label_criticalLayerTop.show(); + + text_criticalLayerTop.set_max_length(100); + text_criticalLayerTop.set_text(""); + text_criticalLayerTop.select_region(0, text_criticalLayerTop.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerTop,2,3,1,1); + text_criticalLayerTop.show(); + + label_criticalLayerName.set_text("Current Layer Name: "); + label_criticalLayerName.set_line_wrap(); + label_criticalLayerName.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerName,0,4,2,1); + label_criticalLayerName.show(); + + text_criticalLayerName.set_max_length(100); + text_criticalLayerName.set_text(""); + text_criticalLayerName.select_region(0, text_criticalLayerName.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerName,2,4,1,1); + text_criticalLayerName.show(); + + label_criticalLayerFilterLr.set_text("Set filter lr_mult:\n(Leave unchanged if not needed) "); + label_criticalLayerFilterLr.set_line_wrap(); + label_criticalLayerFilterLr.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerFilterLr,0,5,2,1); + label_criticalLayerFilterLr.show(); + + text_criticalLayerFilterLr.set_max_length(100); + text_criticalLayerFilterLr.set_text("1"); + text_criticalLayerFilterLr.select_region(0, text_criticalLayerFilterLr.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerFilterLr,2,5,1,1); + text_criticalLayerFilterLr.show(); + + label_criticalLayerFilterDm.set_text("Set filter decay_mult:\n(Leave unchanged if not needed) "); + label_criticalLayerFilterDm.set_line_wrap(); + label_criticalLayerFilterDm.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerFilterDm,0,6,2,1); + label_criticalLayerFilterDm.show(); + + text_criticalLayerFilterDm.set_max_length(100); + text_criticalLayerFilterDm.set_text("1"); + text_criticalLayerFilterDm.select_region(0, text_criticalLayerFilterDm.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerFilterDm,2,6,1,1); + text_criticalLayerFilterDm.show(); + + label_criticalLayerBiasLr.set_text("Set bias lr_mult:\n(Leave unchanged if not needed) "); + label_criticalLayerBiasLr.set_line_wrap(); + label_criticalLayerBiasLr.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerBiasLr,0,7,2,1); + label_criticalLayerBiasLr.show(); + + text_criticalLayerBiasLr.set_max_length(100); + text_criticalLayerBiasLr.set_text("2"); + text_criticalLayerBiasLr.select_region(0, text_criticalLayerBiasLr.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerBiasLr,2,7,1,1); + text_criticalLayerBiasLr.show(); + + label_criticalLayerBiasDm.set_text("Set bias decay_mult:\n(Leave unchanged if not needed) "); + label_criticalLayerBiasDm.set_line_wrap(); + label_criticalLayerBiasDm.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerBiasDm,0,8,2,1); + label_criticalLayerBiasDm.show(); + + text_criticalLayerBiasDm.set_max_length(100); + text_criticalLayerBiasDm.set_text("0"); + text_criticalLayerBiasDm.select_region(0, text_criticalLayerBiasDm.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerBiasDm,2,8,1,1); + text_criticalLayerBiasDm.show(); + + label_criticalLayerNumOutput.set_text("Set param num_output: "); + label_criticalLayerNumOutput.set_line_wrap(); + label_criticalLayerNumOutput.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerNumOutput,0,9,2,1); + label_criticalLayerNumOutput.show(); + + text_criticalLayerNumOutput.set_max_length(100); + text_criticalLayerNumOutput.set_text("64"); + text_criticalLayerNumOutput.select_region(0, text_criticalLayerNumOutput.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerNumOutput,2,9,1,1); + text_criticalLayerNumOutput.show(); + + + label_criticalLayerWeightFiller.set_text("Set Weight Filler : "); + label_criticalLayerWeightFiller.set_line_wrap(); + label_criticalLayerWeightFiller.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerWeightFiller,0,16,2,1); + label_criticalLayerWeightFiller.show(); + +// Gtk::RadioButton::Group group1 = rbutton_criticalLayerWeightFillerConstant.get_group(); +// rbutton_criticalLayerWeightFillerUniform.set_group(group1); +// rbutton_criticalLayerWeightGaussian.set_group(group1); +// rbutton_criticalLayerWeightFillerPositiveUnitBall.set_group(group1); +// rbutton_criticalLayerWeightFillerXavier.set_group(group1); +// rbutton_criticalLayerWeightFillerMSRA.set_group(group1); +// rbutton_criticalLayerWeightFillerBilinear.set_group(group1); +// rbutton_criticalLayerWeightFillerConstant.set_active(); +// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerConstant,2,17,1,1); + rbutton_criticalLayerWeightFillerConstant.show(); +// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerUniform,2,18,1,1); + rbutton_criticalLayerWeightFillerUniform.show(); +// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightGaussian,2,19,1,1); + rbutton_criticalLayerWeightGaussian.show(); +// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerPositiveUnitBall,2,20,1,1); + rbutton_criticalLayerWeightFillerPositiveUnitBall.show(); +// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavier,2,21,1,1); + rbutton_criticalLayerWeightFillerXavier.show(); +// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerMSRA,2,22,1,1); + rbutton_criticalLayerWeightFillerMSRA.show(); +// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerBilinear,2,23,1,1); + rbutton_criticalLayerWeightFillerBilinear.show(); + + label_criticalLayerWeightFillerConstantValue.set_text("value: "); + label_criticalLayerWeightFillerConstantValue.set_line_wrap(); + label_criticalLayerWeightFillerConstantValue.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerConstantValue,4,17,1,1); + label_criticalLayerWeightFillerConstantValue.show(); + + text_criticalLayerWeightFillerConstantValue.set_max_length(100); + text_criticalLayerWeightFillerConstantValue.set_text("0.5"); + text_criticalLayerWeightFillerConstantValue.select_region(0, text_criticalLayerWeightFillerConstantValue.get_text_length()); +// m_grid_criticalLayerWeightFillerConstantValue.attach(text_criticalLayerWeightFillerConstantValue,5,15,1,1); + text_criticalLayerWeightFillerConstantValue.show(); + + label_criticalLayerWeightFillerUniformMin.set_text("min: "); + label_criticalLayerWeightFillerUniformMin.set_line_wrap(); + label_criticalLayerWeightFillerUniformMin.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerUniformMin,4,18,1,1); + label_criticalLayerWeightFillerUniformMin.show(); + + text_criticalLayerWeightFillerUniformMin.set_max_length(100); + text_criticalLayerWeightFillerUniformMin.set_text("0"); + text_criticalLayerWeightFillerUniformMin.select_region(0, text_criticalLayerWeightFillerUniformMin.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerWeightFillerUniformMin,5,18,1,1); + text_criticalLayerWeightFillerUniformMin.show(); + + label_criticalLayerWeightFillerUniformMax.set_text("max: "); + label_criticalLayerWeightFillerUniformMax.set_line_wrap(); + label_criticalLayerWeightFillerUniformMax.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerUniformMax,4,19,1,1); + label_criticalLayerWeightFillerUniformMax.show(); + + text_criticalLayerWeightFillerUniformMax.set_max_length(100); + text_criticalLayerWeightFillerUniformMax.set_text("1"); + text_criticalLayerWeightFillerUniformMax.select_region(0, text_criticalLayerWeightFillerUniformMax.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerWeightFillerUniformMax,5,19,1,1); + text_criticalLayerWeightFillerUniformMax.show(); + + label_criticalLayerWeightFillerGaussianMean.set_text("mean: "); + label_criticalLayerWeightFillerGaussianMean.set_line_wrap(); + label_criticalLayerWeightFillerGaussianMean.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerGaussianMean,4,19,1,1); + label_criticalLayerWeightFillerGaussianMean.show(); + + text_criticalLayerWeightFillerGaussianMean.set_max_length(100); + text_criticalLayerWeightFillerGaussianMean.set_text("0"); + text_criticalLayerWeightFillerGaussianMean.select_region(0, text_criticalLayerWeightFillerGaussianMean.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerWeightFillerGaussianMean,5,19,1,1); + text_criticalLayerWeightFillerGaussianMean.show(); + + label_criticalLayerWeightFillerGaussianStd.set_text("std: "); + label_criticalLayerWeightFillerGaussianStd.set_line_wrap(); + label_criticalLayerWeightFillerGaussianStd.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerGaussianStd,6,19,1,1); + label_criticalLayerWeightFillerGaussianStd.show(); + + text_criticalLayerWeightFillerGaussianStd.set_max_length(100); + text_criticalLayerWeightFillerGaussianStd.set_text("0.1"); + text_criticalLayerWeightFillerGaussianStd.select_region(0, text_criticalLayerWeightFillerGaussianStd.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerWeightFillerGaussianStd,7,19,1,1); + text_criticalLayerWeightFillerGaussianStd.show(); + + label_criticalLayerWeightFillerXavierVariance.set_text("variance_norm: "); + label_criticalLayerWeightFillerXavierVariance.set_line_wrap(); + label_criticalLayerWeightFillerXavierVariance.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerXavierVariance,4,20,1,1); + label_criticalLayerWeightFillerXavierVariance.show(); + +// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavierIn,5,20,1,1); + rbutton_criticalLayerWeightFillerXavierIn.show(); +// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavierOut,6,20,1,1); + rbutton_criticalLayerWeightFillerXavierOut.show(); +// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavierAvg,7,20,1,1); + rbutton_criticalLayerWeightFillerXavierAvg.show(); + + label_criticalLayerWeightFillerMSRAVariance.set_text("variance_norm: "); + label_criticalLayerWeightFillerMSRAVariance.set_line_wrap(); + label_criticalLayerWeightFillerMSRAVariance.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerMSRAVariance,4,20,1,1); + label_criticalLayerWeightFillerMSRAVariance.show(); + +// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavierIn,5,20,1,1); + rbutton_criticalLayerWeightFillerMSRAIn.show(); +// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavierOut,6,20,1,1); + rbutton_criticalLayerWeightFillerMSRAOut.show(); +// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavierAvg,7,20,1,1); + rbutton_criticalLayerWeightFillerMSRAAvg.show(); + + label_criticalLayerBias.set_text("Set bias value: "); + label_criticalLayerBias.set_line_wrap(); + label_criticalLayerBias.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerBias,0,24,2,1); + label_criticalLayerBias.show(); + + text_criticalLayerBias.set_max_length(100); + text_criticalLayerBias.set_text("0.1"); + text_criticalLayerBias.select_region(0, text_criticalLayerBias.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerBias,2,24,1,1); + text_criticalLayerBias.show(); + } + else if( data == "Dropout") + { + label_criticalLayerBottom1.hide(); + text_criticalLayerBottom1.hide(); + label_criticalLayerBottom2.hide(); + text_criticalLayerBottom2.hide(); + label_criticalLayerTop.hide(); + text_criticalLayerTop.hide(); + label_criticalLayerName.hide(); + text_criticalLayerName.hide(); + label_criticalLayerFilterLr.hide(); + text_criticalLayerFilterLr.hide(); + label_criticalLayerFilterDm.hide(); + text_criticalLayerFilterDm.hide(); + label_criticalLayerBiasLr.hide(); + text_criticalLayerBiasLr.hide(); + label_criticalLayerBiasDm.hide(); + text_criticalLayerBiasDm.hide(); + label_criticalLayerNumOutput.hide(); + text_criticalLayerNumOutput.hide(); + label_criticalLayerKernelW.hide(); + text_criticalLayerKernelW.hide(); + label_criticalLayerKernelH.hide(); + text_criticalLayerKernelH.hide(); + label_criticalLayerStrideW.hide(); + text_criticalLayerStrideW.hide(); + label_criticalLayerStrideH.hide(); + text_criticalLayerStrideH.hide(); + label_criticalLayerPadW.hide(); + text_criticalLayerPadW.hide(); + label_criticalLayerPadH.hide(); + text_criticalLayerPadH.hide(); + label_criticalLayerWeightFiller.hide(); + rbutton_criticalLayerWeightFillerConstant.hide(); + rbutton_criticalLayerWeightFillerUniform.hide(); + rbutton_criticalLayerWeightGaussian.hide(); + rbutton_criticalLayerWeightFillerPositiveUnitBall.hide(); + rbutton_criticalLayerWeightFillerXavier.hide(); + rbutton_criticalLayerWeightFillerMSRA.hide(); + rbutton_criticalLayerWeightFillerBilinear.hide(); + label_criticalLayerWeightFillerConstantValue.hide(); + text_criticalLayerWeightFillerConstantValue.hide(); + label_criticalLayerWeightFillerUniformMin.hide(); + label_criticalLayerWeightFillerUniformMax.hide(); + text_criticalLayerWeightFillerUniformMin.hide(); + text_criticalLayerWeightFillerUniformMax.hide(); + label_criticalLayerWeightFillerGaussianMean.hide(); + text_criticalLayerWeightFillerGaussianMean.hide(); + label_criticalLayerWeightFillerGaussianStd.hide(); + text_criticalLayerWeightFillerGaussianStd.hide(); + label_criticalLayerWeightFillerXavierVariance.hide(); + rbutton_criticalLayerWeightFillerXavierIn.hide(); + rbutton_criticalLayerWeightFillerXavierOut.hide(); + rbutton_criticalLayerWeightFillerXavierAvg.hide(); + label_criticalLayerWeightFillerMSRAVariance.hide(); + rbutton_criticalLayerWeightFillerMSRAIn.hide(); + rbutton_criticalLayerWeightFillerMSRAOut.hide(); + rbutton_criticalLayerWeightFillerMSRAAvg.hide(); + label_criticalLayerDropoutRatio.hide(); + text_criticalLayerDropoutRatio.hide(); + label_criticalLayerPool.hide(); + label_criticalLayerBias.hide(); + text_criticalLayerBias.hide(); + rbutton_criticalLayerPoolMax.hide(); + rbutton_criticalLayerPoolAve.hide(); + + label_criticalLayerBottom1.set_text("Bottom1 Layer Name: "); + label_criticalLayerBottom1.set_line_wrap(); + label_criticalLayerBottom1.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerBottom1,0,1,2,1); + label_criticalLayerBottom1.show(); + + text_criticalLayerBottom1.set_max_length(100); + text_criticalLayerBottom1.set_text(""); + text_criticalLayerBottom1.select_region(0, text_criticalLayerBottom1.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerBottom1,2,1,1,1); + text_criticalLayerBottom1.show(); + + label_criticalLayerTop.set_text("Top Layer Name: "); + label_criticalLayerTop.set_line_wrap(); + label_criticalLayerTop.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerTop,0,3,2,1); + label_criticalLayerTop.show(); + + text_criticalLayerTop.set_max_length(100); + text_criticalLayerTop.set_text(""); + text_criticalLayerTop.select_region(0, text_criticalLayerTop.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerTop,2,3,1,1); + text_criticalLayerTop.show(); + + label_criticalLayerName.set_text("Current Layer Name: "); + label_criticalLayerName.set_line_wrap(); + label_criticalLayerName.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerName,0,4,2,1); + label_criticalLayerName.show(); + + text_criticalLayerName.set_max_length(100); + text_criticalLayerName.set_text(""); + text_criticalLayerName.select_region(0, text_criticalLayerName.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerName,2,4,1,1); + text_criticalLayerName.show(); + + label_criticalLayerDropoutRatio.set_text("Set Dropout Ratio: "); + label_criticalLayerDropoutRatio.set_line_wrap(); + label_criticalLayerDropoutRatio.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerDropoutRatio,0,22,2,1); + label_criticalLayerDropoutRatio.show(); + + text_criticalLayerDropoutRatio.set_max_length(100); + text_criticalLayerDropoutRatio.set_text("0.5"); + text_criticalLayerDropoutRatio.select_region(0, text_criticalLayerDropoutRatio.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerDropoutRatio,2,22,1,1); + text_criticalLayerDropoutRatio.show(); + } + else if(data == "Pooling") + { + label_criticalLayerBottom1.hide(); + text_criticalLayerBottom1.hide(); + label_criticalLayerBottom2.hide(); + text_criticalLayerBottom2.hide(); + label_criticalLayerTop.hide(); + text_criticalLayerTop.hide(); + label_criticalLayerName.hide(); + text_criticalLayerName.hide(); + label_criticalLayerFilterLr.hide(); + text_criticalLayerFilterLr.hide(); + label_criticalLayerFilterDm.hide(); + text_criticalLayerFilterDm.hide(); + label_criticalLayerBiasLr.hide(); + text_criticalLayerBiasLr.hide(); + label_criticalLayerBiasDm.hide(); + text_criticalLayerBiasDm.hide(); + label_criticalLayerNumOutput.hide(); + text_criticalLayerNumOutput.hide(); + label_criticalLayerKernelW.hide(); + text_criticalLayerKernelW.hide(); + label_criticalLayerKernelH.hide(); + text_criticalLayerKernelH.hide(); + label_criticalLayerStrideW.hide(); + text_criticalLayerStrideW.hide(); + label_criticalLayerStrideH.hide(); + text_criticalLayerStrideH.hide(); + label_criticalLayerPadW.hide(); + text_criticalLayerPadW.hide(); + label_criticalLayerPadH.hide(); + text_criticalLayerPadH.hide(); + label_criticalLayerWeightFiller.hide(); + rbutton_criticalLayerWeightFillerConstant.hide(); + rbutton_criticalLayerWeightFillerUniform.hide(); + rbutton_criticalLayerWeightGaussian.hide(); + rbutton_criticalLayerWeightFillerPositiveUnitBall.hide(); + rbutton_criticalLayerWeightFillerXavier.hide(); + rbutton_criticalLayerWeightFillerMSRA.hide(); + rbutton_criticalLayerWeightFillerBilinear.hide(); + label_criticalLayerWeightFillerConstantValue.hide(); + text_criticalLayerWeightFillerConstantValue.hide(); + label_criticalLayerWeightFillerUniformMin.hide(); + label_criticalLayerWeightFillerUniformMax.hide(); + text_criticalLayerWeightFillerUniformMin.hide(); + text_criticalLayerWeightFillerUniformMax.hide(); + label_criticalLayerWeightFillerGaussianMean.hide(); + text_criticalLayerWeightFillerGaussianMean.hide(); + label_criticalLayerWeightFillerGaussianStd.hide(); + text_criticalLayerWeightFillerGaussianStd.hide(); + label_criticalLayerWeightFillerXavierVariance.hide(); + rbutton_criticalLayerWeightFillerXavierIn.hide(); + rbutton_criticalLayerWeightFillerXavierOut.hide(); + rbutton_criticalLayerWeightFillerXavierAvg.hide(); + label_criticalLayerWeightFillerMSRAVariance.hide(); + rbutton_criticalLayerWeightFillerMSRAIn.hide(); + rbutton_criticalLayerWeightFillerMSRAOut.hide(); + rbutton_criticalLayerWeightFillerMSRAAvg.hide(); + label_criticalLayerDropoutRatio.hide(); + text_criticalLayerDropoutRatio.hide(); + label_criticalLayerPool.hide(); + label_criticalLayerBias.hide(); + text_criticalLayerBias.hide(); + rbutton_criticalLayerPoolMax.hide(); + rbutton_criticalLayerPoolAve.hide(); + + + label_criticalLayerBottom1.set_text("Bottom1 Layer Name: "); + label_criticalLayerBottom1.set_line_wrap(); + label_criticalLayerBottom1.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerBottom1,0,1,2,1); + label_criticalLayerBottom1.show(); + + text_criticalLayerBottom1.set_max_length(100); + text_criticalLayerBottom1.set_text(""); + text_criticalLayerBottom1.select_region(0, text_criticalLayerBottom1.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerBottom1,2,1,1,1); + text_criticalLayerBottom1.show(); + + label_criticalLayerTop.set_text("Top Layer Name: "); + label_criticalLayerTop.set_line_wrap(); + label_criticalLayerTop.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerTop,0,3,2,1); + label_criticalLayerTop.show(); + + text_criticalLayerTop.set_max_length(100); + text_criticalLayerTop.set_text(""); + text_criticalLayerTop.select_region(0, text_criticalLayerTop.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerTop,2,3,1,1); + text_criticalLayerTop.show(); + + label_criticalLayerName.set_text("Current Layer Name: "); + label_criticalLayerName.set_line_wrap(); + label_criticalLayerName.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerName,0,4,2,1); + label_criticalLayerName.show(); + + text_criticalLayerName.set_max_length(100); + text_criticalLayerName.set_text(""); + text_criticalLayerName.select_region(0, text_criticalLayerName.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerName,2,4,1,1); + text_criticalLayerName.show(); + + label_criticalLayerKernelW.set_text("Set param kernel_w: "); + label_criticalLayerKernelW.set_line_wrap(); + label_criticalLayerKernelW.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerKernelW,0,10,2,1); + label_criticalLayerKernelW.show(); + + text_criticalLayerKernelW.set_max_length(100); + text_criticalLayerKernelW.set_text("3"); + text_criticalLayerKernelW.select_region(0, text_criticalLayerKernelW.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerKernelW,2,10,1,1); + text_criticalLayerKernelW.show(); + + label_criticalLayerKernelH.set_text("Set param kernel_h: "); + label_criticalLayerKernelH.set_line_wrap(); + label_criticalLayerKernelH.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerKernelH,0,11,2,1); + label_criticalLayerKernelH.show(); + + text_criticalLayerKernelH.set_max_length(100); + text_criticalLayerKernelH.set_text("3"); + text_criticalLayerKernelH.select_region(0, text_criticalLayerKernelH.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerKernelH,2,11,1,1); + text_criticalLayerKernelH.show(); + + label_criticalLayerStrideW.set_text("Set param stride_w: "); + label_criticalLayerStrideW.set_line_wrap(); + label_criticalLayerStrideW.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerStrideW,0,12,2,1); + label_criticalLayerStrideW.show(); + + text_criticalLayerStrideW.set_max_length(100); + text_criticalLayerStrideW.set_text("1"); + text_criticalLayerStrideW.select_region(0, text_criticalLayerStrideW.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerStrideW,2,12,1,1); + text_criticalLayerStrideW.show(); + + label_criticalLayerStrideH.set_text("Set param stride_h: "); + label_criticalLayerStrideH.set_line_wrap(); + label_criticalLayerStrideH.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerStrideH,0,13,2,1); + label_criticalLayerStrideH.show(); + + text_criticalLayerStrideH.set_max_length(100); + text_criticalLayerStrideH.set_text("1"); + text_criticalLayerStrideH.select_region(0, text_criticalLayerStrideH.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerStrideH,2,13,1,1); + text_criticalLayerStrideH.show(); + + label_criticalLayerPadW.set_text("Set param pad_w: "); + label_criticalLayerPadW.set_line_wrap(); + label_criticalLayerPadW.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerPadW,0,14,2,1); + label_criticalLayerPadW.show(); + + text_criticalLayerPadW.set_max_length(100); + text_criticalLayerPadW.set_text("1"); + text_criticalLayerPadW.select_region(0, text_criticalLayerPadW.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerPadW,2,14,1,1); + text_criticalLayerPadW.show(); + + label_criticalLayerPadH.set_text("Set param pad_h: "); + label_criticalLayerPadH.set_line_wrap(); + label_criticalLayerPadH.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerPadH,0,14,2,1); + label_criticalLayerPadH.show(); + + text_criticalLayerPadH.set_max_length(100); + text_criticalLayerPadH.set_text("1"); + text_criticalLayerPadH.select_region(0, text_criticalLayerPadH.get_text_length()); +// m_grid_criticalLayerType.attach(text_criticalLayerPadH,2,14,1,1); + text_criticalLayerPadH.show(); + + label_criticalLayerPool.set_text("Set Pool Type: "); + label_criticalLayerPool.set_line_wrap(); + label_criticalLayerPool.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_criticalLayerType.attach(label_criticalLayerPool,0,23,2,1); + label_criticalLayerPool.show(); + + Gtk::RadioButton::Group group4 = rbutton_criticalLayerPoolMax.get_group(); + rbutton_criticalLayerPoolAve.set_group(group4); + rbutton_criticalLayerPoolMax.set_active(); +// m_grid_criticalLayerType.attach(rbutton_criticalLayerPoolMax,2,23,1,1); + rbutton_criticalLayerPoolMax.show(); +// m_grid_criticalLayerType.attach(rbutton_criticalLayerPoolAve,3,23,1,1); + rbutton_criticalLayerPoolAve.show(); + + } + else + { + label_criticalLayerBottom1.hide(); + text_criticalLayerBottom1.hide(); + label_criticalLayerBottom2.hide(); + text_criticalLayerBottom2.hide(); + label_criticalLayerTop.hide(); + text_criticalLayerTop.hide(); + label_criticalLayerName.hide(); + text_criticalLayerName.hide(); + label_criticalLayerFilterLr.hide(); + text_criticalLayerFilterLr.hide(); + label_criticalLayerFilterDm.hide(); + text_criticalLayerFilterDm.hide(); + label_criticalLayerBiasLr.hide(); + text_criticalLayerBiasLr.hide(); + label_criticalLayerBiasDm.hide(); + text_criticalLayerBiasDm.hide(); + label_criticalLayerNumOutput.hide(); + text_criticalLayerNumOutput.hide(); + label_criticalLayerKernelW.hide(); + text_criticalLayerKernelW.hide(); + label_criticalLayerKernelH.hide(); + text_criticalLayerKernelH.hide(); + label_criticalLayerStrideW.hide(); + text_criticalLayerStrideW.hide(); + label_criticalLayerStrideH.hide(); + text_criticalLayerStrideH.hide(); + label_criticalLayerPadW.hide(); + text_criticalLayerPadW.hide(); + label_criticalLayerPadH.hide(); + text_criticalLayerPadH.hide(); + label_criticalLayerWeightFiller.hide(); + rbutton_criticalLayerWeightFillerConstant.hide(); + rbutton_criticalLayerWeightFillerUniform.hide(); + rbutton_criticalLayerWeightGaussian.hide(); + rbutton_criticalLayerWeightFillerPositiveUnitBall.hide(); + rbutton_criticalLayerWeightFillerXavier.hide(); + rbutton_criticalLayerWeightFillerMSRA.hide(); + rbutton_criticalLayerWeightFillerBilinear.hide(); + label_criticalLayerWeightFillerConstantValue.hide(); + text_criticalLayerWeightFillerConstantValue.hide(); + label_criticalLayerWeightFillerUniformMin.hide(); + label_criticalLayerWeightFillerUniformMax.hide(); + text_criticalLayerWeightFillerUniformMin.hide(); + text_criticalLayerWeightFillerUniformMax.hide(); + label_criticalLayerWeightFillerGaussianMean.hide(); + text_criticalLayerWeightFillerGaussianMean.hide(); + label_criticalLayerWeightFillerGaussianStd.hide(); + text_criticalLayerWeightFillerGaussianStd.hide(); + label_criticalLayerWeightFillerXavierVariance.hide(); + rbutton_criticalLayerWeightFillerXavierIn.hide(); + rbutton_criticalLayerWeightFillerXavierOut.hide(); + rbutton_criticalLayerWeightFillerXavierAvg.hide(); + label_criticalLayerWeightFillerMSRAVariance.hide(); + rbutton_criticalLayerWeightFillerMSRAIn.hide(); + rbutton_criticalLayerWeightFillerMSRAOut.hide(); + rbutton_criticalLayerWeightFillerMSRAAvg.hide(); + label_criticalLayerDropoutRatio.hide(); + text_criticalLayerDropoutRatio.hide(); + label_criticalLayerPool.hide(); + label_criticalLayerBias.hide(); + text_criticalLayerBias.hide(); + rbutton_criticalLayerPoolMax.hide(); + rbutton_criticalLayerPoolAve.hide(); + } + m_sw_criticalLayerType.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); + m_grid_criticalLayerType.show(); +// show_all_children(); + m_sw_criticalLayerType.show(); + +} diff --git a/detectors/include/od/detectors/global2D/training/DisplayWindow.h b/detectors/include/od/detectors/global2D/training/DisplayWindow.h new file mode 100644 index 00000000..3566ccd2 --- /dev/null +++ b/detectors/include/od/detectors/global2D/training/DisplayWindow.h @@ -0,0 +1,14 @@ +#include "od/detectors/global2D/training/Network.h" + +void NetworkCreator::showWindow_displayWindow() +{ + remove(); + set_title("Display Entire Network"); + set_border_width(10); + add(box_fullCnnLayerMatter); + buffer_fullCnnLayerMatter->set_text(fullCnnLayerMatter); + textView_fullCnnLayerMatter.set_buffer(buffer_fullCnnLayerMatter); + show_all_children(); +} + + diff --git a/detectors/include/od/detectors/global2D/training/ExtraWindow.h b/detectors/include/od/detectors/global2D/training/ExtraWindow.h new file mode 100644 index 00000000..17c0e38e --- /dev/null +++ b/detectors/include/od/detectors/global2D/training/ExtraWindow.h @@ -0,0 +1,24 @@ +#include "od/detectors/global2D/training/Network.h" + +void NetworkCreator::showWindow_extraLayerType(Glib::ustring data) +{ + remove(); + set_title("Extra Layer"); + set_border_width(10); + add(m_sw_extraLayerType); + m_grid_extraLayerType.set_column_spacing (10); + m_grid_extraLayerType.set_row_spacing (50); + + title_extraLayerType.set_text("Will be updated soon"); + title_extraLayerType.set_line_wrap(); + title_extraLayerType.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(title_extraLayerType,0,0,2,1); + title_extraLayerType.show(); + + button_addMoreLayer5.show(); + + m_sw_extraLayerType.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); + m_grid_extraLayerType.show(); +// show_all_children(); + m_sw_extraLayerType.show(); +} diff --git a/detectors/include/od/detectors/global2D/training/LossWindow.h b/detectors/include/od/detectors/global2D/training/LossWindow.h new file mode 100644 index 00000000..51aaf9ee --- /dev/null +++ b/detectors/include/od/detectors/global2D/training/LossWindow.h @@ -0,0 +1,127 @@ +#include "od/detectors/global2D/training/Network.h" + +void NetworkCreator::showWindow_lossLayerType(Glib::ustring data) +{ + remove(); + set_title("Loss Layer"); + set_border_width(10); + add(m_sw_lossLayerType); + m_grid_lossLayerType.set_column_spacing (10); + m_grid_lossLayerType.set_row_spacing (50); + + //level 0 + if(data == "" or data == "SoftmaxWithLoss") + title_lossLayerType.set_text("Set the Properties of Loss Layer type: SoftmaxWithLoss"); + else + title_lossLayerType.set_text("Will be updated soon"); + title_lossLayerType.set_line_wrap(); + title_lossLayerType.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_lossLayerType.attach(title_lossLayerType,0,0,2,1); + title_lossLayerType.show(); + + + button_addMoreLayer4.show(); + + if(data == "" or data == "SoftmaxWithLoss") + { + label_lossLayerBottom1.hide(); + text_lossLayerBottom1.hide(); + label_lossLayerBottom2.hide(); + text_lossLayerBottom2.hide(); + label_lossLayerTop.hide(); + text_lossLayerTop.hide(); + label_lossLayerName.hide(); + text_lossLayerName.hide(); + label_lossLayerNormalize.hide(); + text_lossLayerNormalize.hide(); + button_setLossParameters.hide(); + label_lossLayerNormalize.hide(); + + label_lossLayerBottom1.set_text("Bottom1 Layer Name: "); + label_lossLayerBottom1.set_line_wrap(); + label_lossLayerBottom1.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_lossLayerType.attach(label_lossLayerBottom1,0,1,2,1); + label_lossLayerBottom1.show(); + + text_lossLayerBottom1.set_max_length(100); + text_lossLayerBottom1.set_text(""); + text_lossLayerBottom1.select_region(0, text_lossLayerBottom1.get_text_length()); +// m_grid_lossLayerType.attach(text_lossLayerBottom1,2,1,1,1); + text_lossLayerBottom1.show(); + + label_lossLayerBottom2.set_text("Bottom2 Layer Name: "); + label_lossLayerBottom2.set_line_wrap(); + label_lossLayerBottom2.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_lossLayerType.attach(label_lossLayerBottom2,0,2,2,1); + label_lossLayerBottom2.show(); + + text_lossLayerBottom2.set_max_length(100); + text_lossLayerBottom2.set_text(""); + text_lossLayerBottom2.select_region(0, text_lossLayerBottom2.get_text_length()); +// m_grid_lossLayerType.attach(text_lossLayerBottom2,2,2,1,1); + text_lossLayerBottom2.show(); + + label_lossLayerTop.set_text("Top Layer Name: "); + label_lossLayerTop.set_line_wrap(); + label_lossLayerTop.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_lossLayerType.attach(label_lossLayerTop,0,3,2,1); + label_lossLayerTop.show(); + + text_lossLayerTop.set_max_length(100); + text_lossLayerTop.set_text(""); + text_lossLayerTop.select_region(0, text_lossLayerTop.get_text_length()); +// m_grid_lossLayerType.attach(text_lossLayerTop,2,3,1,1); + text_lossLayerTop.show(); + + label_lossLayerName.set_text("Current Layer Name: "); + label_lossLayerName.set_line_wrap(); + label_lossLayerName.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_lossLayerType.attach(label_lossLayerName,0,4,2,1); + label_lossLayerName.show(); + + text_lossLayerName.set_max_length(100); + text_lossLayerName.set_text(""); + text_lossLayerName.select_region(0, text_lossLayerName.get_text_length()); +// m_grid_lossLayerType.attach(text_lossLayerName,2,4,1,1); + text_lossLayerName.show(); + + label_lossLayerNormalize.set_text("Normalize: \n(bool value)"); + label_lossLayerNormalize.set_line_wrap(); + label_lossLayerNormalize.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_lossLayerType.attach(label_lossLayerNormalize,0,5,2,1); + label_lossLayerNormalize.show(); + + text_lossLayerNormalize.set_max_length(100); + text_lossLayerNormalize.set_text(""); + text_lossLayerNormalize.select_region(0, text_lossLayerNormalize.get_text_length()); +// m_grid_lossLayerType.attach(text_lossLayerNormalize,2,5,1,1); + text_lossLayerNormalize.show(); + + label_lossLayerNormalize.set_text("Normalization: \n(select type)"); + label_lossLayerNormalize.set_line_wrap(); + label_lossLayerNormalize.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_lossLayerType.attach(label_lossLayerNormalize,0,6,2,1); + label_lossLayerNormalize.show(); + + button_setLossParameters.show(); + } + else + { + label_lossLayerBottom1.hide(); + text_lossLayerBottom1.hide(); + label_lossLayerBottom2.hide(); + text_lossLayerBottom2.hide(); + label_lossLayerTop.hide(); + text_lossLayerTop.hide(); + label_lossLayerName.hide(); + text_lossLayerName.hide(); + label_lossLayerNormalize.hide(); + text_lossLayerNormalize.hide(); + button_setLossParameters.hide(); + } + + m_sw_lossLayerType.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); + m_grid_lossLayerType.show(); +// show_all_children(); + m_sw_lossLayerType.show(); +} diff --git a/detectors/include/od/detectors/global2D/training/MainWindow.h b/detectors/include/od/detectors/global2D/training/MainWindow.h new file mode 100644 index 00000000..8d9813fe --- /dev/null +++ b/detectors/include/od/detectors/global2D/training/MainWindow.h @@ -0,0 +1,146 @@ +#include "od/detectors/global2D/training/Network.h" + +void NetworkCreator::showWindow_main() +{ + remove(); + set_title("Network Creator"); + set_border_width(10); + add(m_sw1); + m_grid1.set_column_spacing (10); + m_grid1.set_row_spacing (50); +// m_sw1.add(m_grid1); + m_sw1.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); +// m_grid1.show(); + show_all_children(); + m_sw1.show(); +} + + +void NetworkCreator::on_cell_data_extra(const Gtk::TreeModel::const_iterator& iter) +{ + // level 1 + auto row_activationLayerType = *iter; + const Glib::ustring extra_activationLayerType = row_activationLayerType[column_activationLayerType.m_col_extra]; + if(extra_activationLayerType.empty()) + cell_activationLayerType.property_text() = "(none)"; + else + cell_activationLayerType.property_text() = "-" + extra_activationLayerType + "-"; + cell_activationLayerType.property_foreground() = "green"; + + // level 2 + auto row_criticalLayerType = *iter; + const Glib::ustring extra_criticalLayerType = row_criticalLayerType[column_criticalLayerType.m_col_extra]; + if(extra_criticalLayerType.empty()) + cell_criticalLayerType.property_text() = "(none)"; + else + cell_criticalLayerType.property_text() = "-" + extra_criticalLayerType + "-"; + cell_criticalLayerType.property_foreground() = "red"; + + // level 3 + auto row_normalizationLayerType = *iter; + const Glib::ustring extra_normalizationLayerType = row_normalizationLayerType[column_normalizationLayerType.m_col_extra]; + if(extra_normalizationLayerType.empty()) + cell_normalizationLayerType.property_text() = "(none)"; + else + cell_normalizationLayerType.property_text() = "-" + extra_normalizationLayerType + "-"; + cell_normalizationLayerType.property_foreground() = "blue"; + + // level 4 + auto row_lossLayerType = *iter; + const Glib::ustring extra_lossLayerType = row_lossLayerType[column_lossLayerType.m_col_extra]; + if(extra_lossLayerType.empty()) + cell_lossLayerType.property_text() = "(none)"; + else + cell_lossLayerType.property_text() = "-" + extra_lossLayerType + "-"; + cell_lossLayerType.property_foreground() = "brown"; + + // level 5 + auto row_extraLayerType = *iter; + const Glib::ustring extra_extraLayerType = row_extraLayerType[column_extraLayerType.m_col_extra]; + if(extra_extraLayerType.empty()) + cell_extraLayerType.property_text() = "(none)"; + else + cell_extraLayerType.property_text() = "-" + extra_extraLayerType + "-"; + cell_extraLayerType.property_foreground() = "purple"; + +} + +void NetworkCreator::on_combo_changed() +{ + Gtk::TreeModel::iterator iter_activationLayerType = combo_activationLayerType.get_active(); + Gtk::TreeModel::iterator iter_criticalLayerType = combo_criticalLayerType.get_active(); + Gtk::TreeModel::iterator iter_normalizationLayerType = combo_normalizationLayerType.get_active(); + Gtk::TreeModel::iterator iter_lossLayerType = combo_lossLayerType.get_active(); + Gtk::TreeModel::iterator iter_extraLayerType = combo_extraLayerType.get_active(); + + + // level 1 + if(iter_activationLayerType) + { + Gtk::TreeModel::Row row_activationLayerType = *iter_activationLayerType; + if(row_activationLayerType) + { + int id_activationLayerType = row_activationLayerType[column_activationLayerType.m_col_id]; + Glib::ustring name_activationLayerType = row_activationLayerType[column_activationLayerType.m_col_name]; +// std::cout << " ID=" << id_activationLayerType << ", name=" << name_activationLayerType << std::endl; + activationLayerTypeData = name_activationLayerType; + } + + } + + //level 2 + if(iter_criticalLayerType) + { + Gtk::TreeModel::Row row_criticalLayerType = *iter_criticalLayerType; + if(row_criticalLayerType) + { + int id_criticalLayerType = row_criticalLayerType[column_criticalLayerType.m_col_id]; + Glib::ustring name_criticalLayerType = row_criticalLayerType[column_criticalLayerType.m_col_name]; +// std::cout << " ID=" << id_criticalLayerType << ", name=" << name_criticalLayerType << std::endl; + criticalLayerTypeData = name_criticalLayerType; + } + } + + // level 3 + if(iter_normalizationLayerType) + { + Gtk::TreeModel::Row row_normalizationLayerType = *iter_normalizationLayerType; + if(row_normalizationLayerType) + { + int id_normalizationLayerType = row_normalizationLayerType[column_normalizationLayerType.m_col_id]; + Glib::ustring name_normalizationLayerType = row_normalizationLayerType[column_normalizationLayerType.m_col_name]; +// std::cout << " ID=" << id_normalizationLayerType << ", name=" << name_normalizationLayerType << std::endl; + normalizationLayerTypeData = name_normalizationLayerType; + } + } + + // level 4 + if(iter_lossLayerType) + { + Gtk::TreeModel::Row row_lossLayerType = *iter_lossLayerType; + if(row_lossLayerType) + { + int id_lossLayerType = row_lossLayerType[column_lossLayerType.m_col_id]; + Glib::ustring name_lossLayerType = row_lossLayerType[column_lossLayerType.m_col_name]; +// std::cout << " ID=" << id_lossLayerType << ", name=" << name_lossLayerType << std::endl; + lossLayerTypeData = name_lossLayerType; + } + } + + // level 5 + if(iter_extraLayerType) + { + Gtk::TreeModel::Row row_extraLayerType = *iter_extraLayerType; + if(row_extraLayerType) + { + int id_extraLayerType = row_extraLayerType[column_extraLayerType.m_col_id]; + Glib::ustring name_extraLayerType = row_extraLayerType[column_extraLayerType.m_col_name]; +// std::cout << " ID=" << id_extraLayerType << ", name=" << name_extraLayerType << std::endl; + extraLayerTypeData = name_extraLayerType; + } + } + + else + std::cout << "invalid iter" << std::endl; +} + diff --git a/detectors/include/od/detectors/global2D/training/Network.h b/detectors/include/od/detectors/global2D/training/Network.h new file mode 100644 index 00000000..571bae80 --- /dev/null +++ b/detectors/include/od/detectors/global2D/training/Network.h @@ -0,0 +1,239 @@ +#pragma once + +#include <gtkmm/grid.h> +#include <gtkmm/entry.h> +#include <gtkmm/button.h> +#include <gtkmm/radiobutton.h> +#include <gtkmm/messagedialog.h> +#include <gtkmm/window.h> +#include <gtkmm/scrolledwindow.h> +#include <gtkmm/application.h> +#include <gtkmm/comboboxtext.h> +#include <gtkmm/liststore.h> +#include <gtkmm/textview.h> +#include <iostream> +#include <fstream> +#include <sstream> +#include <vector> + +class NetworkCreator : public Gtk::Window +{ + public: + NetworkCreator(); + virtual ~NetworkCreator(); + + + protected: + void on_button_clicked(Glib::ustring data); + void on_combo_changed(); + void on_cell_data_extra(const Gtk::TreeModel::const_iterator& iter); + void showWindow_main(); + void showWindow_activationLayerType(Glib::ustring data); + void showWindow_displayWindow(); + void showWindow_criticalLayerType(Glib::ustring data); + void showWindow_normalizationLayerType(Glib::ustring data); + void showWindow_lossLayerType(Glib::ustring data); + void showWindow_extraLayerType(Glib::ustring data); + + // Child widgets: + Gtk::Grid m_grid1, + m_grid_activationLayerType, + m_grid_criticalLayerType, + m_grid_normalizationLayerType, + m_grid_lossLayerType, + m_grid_extraLayerType; + Gtk::ScrolledWindow m_sw1, + m_sw_activationLayerType, + m_sw_criticalLayerType, + m_sw_fullCnnLayerMatter, + m_sw_normalizationLayerType, + m_sw_lossLayerType, + m_sw_extraLayerType; + Gtk::Button button_networkFileName, + button_activationLayerType, + button_addMoreLayer, + button_setActivationParameters, + button_displayCnnLayers, + button_editMore, + button_deleteLayerAtEnd, + button_criticalLayerType, + button_setCriticalParameters, + button_addMoreLayer2, + button_normalizationLayerType, + button_lossLayerType, + button_extraLayerType, + button_addMoreLayer3, + button_setNormalizationParameters, + button_addMoreLayer4, + button_setLossParameters, + button_addMoreLayer5, + button_saveFile; + Gtk::Entry text_networkFileName, + text_activationLayerTop, + text_activationLayerBottom, + text_activationLayerName, + text_activationLayerType, + text_activationLayerScale, + text_activationLayerShift, + text_activationLayerBase, + text_activationLayerNegativeSlope, + text_criticalLayerTop, + text_criticalLayerBottom1, + text_criticalLayerBottom2, + text_criticalLayerName, + text_criticalLayerFilterLr, + text_criticalLayerFilterDm, + text_criticalLayerBiasLr, + text_criticalLayerBiasDm, + text_criticalLayerNumOutput, + text_criticalLayerKernelW, + text_criticalLayerKernelH, + text_criticalLayerStrideW, + text_criticalLayerStrideH, + text_criticalLayerPadW, + text_criticalLayerPadH, + text_criticalLayerWeightFillerConstantValue, + text_criticalLayerWeightFillerUniformMin, + text_criticalLayerWeightFillerUniformMax, + text_criticalLayerWeightFillerGaussianMean, + text_criticalLayerWeightFillerGaussianStd, + text_criticalLayerDropoutRatio, + text_criticalLayerBias, + text_normalizationLayerTop, + text_normalizationLayerBottom, + text_normalizationLayerName, + text_normalizationLayerlocalSize, + text_normalizationLayerAlpha, + text_normalizationLayerBeta, + text_normalizationLayerK, + text_normalizationLayerAcrossChannel, + text_normalizationLayerNormalizeVariance, + text_normalizationLayerEps, + text_lossLayerTop, + text_lossLayerBottom1, + text_lossLayerBottom2, + text_lossLayerName, + text_lossLayerNormalize; + Gtk::Label label_networkFileName, + label_activationLayerType, + label_criticalLayerType, + label_normalizationLayerType, + label_lossLayerType, + label_extraLayerType, + title_activationLayerType, + label_activationLayerTop, + label_activationLayerBottom, + label_activationLayerName, + label_activationLayerScale, + label_activationLayerShift, + label_activationLayerBase, + label_activationLayerNegativeSlope, + title_criticalLayerType, + label_criticalLayerTop, + label_criticalLayerBottom1, + label_criticalLayerBottom2, + label_criticalLayerName, + label_criticalLayerFilterLr, + label_criticalLayerFilterDm, + label_criticalLayerBiasLr, + label_criticalLayerBiasDm, + label_criticalLayerNumOutput, + label_criticalLayerKernelW, + label_criticalLayerKernelH, + label_criticalLayerStrideW, + label_criticalLayerStrideH, + label_criticalLayerPadW, + label_criticalLayerPadH, + label_criticalLayerWeightFiller, + label_criticalLayerWeightFillerConstantValue, + label_criticalLayerWeightFillerUniformMin, + label_criticalLayerWeightFillerUniformMax, + label_criticalLayerWeightFillerGaussianMean, + label_criticalLayerWeightFillerGaussianStd, + label_criticalLayerWeightFillerXavierVariance, + label_criticalLayerWeightFillerMSRAVariance, + label_criticalLayerDropoutRatio, + label_criticalLayerPool, + label_criticalLayerBias, + title_normalizationLayerType, + label_normalizationLayerTop, + label_normalizationLayerBottom, + label_normalizationLayerName, + label_normalizationLayerlocalSize, + label_normalizationLayerAlpha, + label_normalizationLayerBeta, + label_normalizationLayerK, + label_normalizationLayerNormRegion, + label_normalizationLayerAcrossChannel, + label_normalizationLayerNormalizeVariance, + label_normalizationLayerEps, + title_lossLayerType, + title_extraLayerType, + label_lossLayerTop, + label_lossLayerBottom1, + label_lossLayerBottom2, + label_lossLayerName, + label_lossLayerNormalize, + label_lossLayerNormalization; + Gtk::ComboBox combo_activationLayerType, + combo_criticalLayerType, + combo_normalizationLayerType, + combo_lossLayerType, + combo_extraLayerType; + Gtk::TextView textView_fullCnnLayerMatter; + Glib::RefPtr<Gtk::TextBuffer> buffer_fullCnnLayerMatter; + Gtk::Box box_fullCnnLayerMatter; + Gtk::ButtonBox buttonBox_fullCnnLayerMatter; + Gtk::RadioButton rbutton_criticalLayerWeightFillerConstant, rbutton_criticalLayerWeightFillerUniform, + rbutton_criticalLayerWeightGaussian, rbutton_criticalLayerWeightFillerPositiveUnitBall, + rbutton_criticalLayerWeightFillerXavier, rbutton_criticalLayerWeightFillerMSRA, + rbutton_criticalLayerWeightFillerBilinear, + rbutton_criticalLayerWeightFillerXavierIn, rbutton_criticalLayerWeightFillerXavierOut, + rbutton_criticalLayerWeightFillerXavierAvg, + rbutton_criticalLayerWeightFillerMSRAIn, rbutton_criticalLayerWeightFillerMSRAOut, + rbutton_criticalLayerWeightFillerMSRAAvg, + rbutton_criticalLayerPoolMax, rbutton_criticalLayerPoolAve, + rbutton_normalizationLayerLRNWithin, rbutton_normalizationLayerLRNAcross; + + + //Tree model columns: + class ModelColumns : public Gtk::TreeModel::ColumnRecord + { + public: + ModelColumns(){ add(m_col_id); add(m_col_name); add(m_col_extra);} + Gtk::TreeModelColumn<int> m_col_id; + Gtk::TreeModelColumn<Glib::ustring> m_col_name; + Gtk::TreeModelColumn<Glib::ustring> m_col_extra; + }; + + ModelColumns column_activationLayerType, + column_criticalLayerType, + column_normalizationLayerType, + column_lossLayerType, + column_extraLayerType; + Gtk::CellRendererText cell_activationLayerType, + cell_criticalLayerType, + cell_normalizationLayerType, + cell_lossLayerType, + cell_extraLayerType; + Glib::RefPtr<Gtk::ListStore> ref_activationLayerType, + ref_criticalLayerType, + ref_normalizationLayerType, + ref_lossLayerType, + ref_extraLayerType; + + + private: + Glib::ustring networkFileName; + Glib::ustring activationLayerTypeData, activationLayerTypeMatter; + Glib::ustring criticalLayerTypeData, criticalLayerTypeMatter; + Glib::ustring normalizationLayerTypeData, normalizationLayerTypeMatter; + Glib::ustring lossLayerTypeData, lossLayerTypeMatter; + Glib::ustring extraLayerTypeData, extraLayerTypeMatter; + int numLayers; + Glib::ustring fullCnnLayerMatter; + std::vector<Glib::ustring> fullCnnLayers; + +}; + + diff --git a/detectors/include/od/detectors/global2D/training/Node.h b/detectors/include/od/detectors/global2D/training/Node.h new file mode 100644 index 00000000..cd19ee88 --- /dev/null +++ b/detectors/include/od/detectors/global2D/training/Node.h @@ -0,0 +1,86 @@ +#include "od/detectors/global2D/training/Network.h" +#include <iostream> + +struct Node { + Glib::ustring data; + Node* nextLayer; +}; + + +void initializeLayer(struct Node *headLayer, Glib::ustring data) +{ + headLayer->data = data; + headLayer->nextLayer = NULL; +} + +void appendLayer(struct Node *headLayer, Glib::ustring data) +{ + Node *newLayer = new Node; + newLayer->data = data; + newLayer->nextLayer = NULL; + + Node *currentLayer = headLayer; + while(currentLayer) + { + if(currentLayer->nextLayer == NULL) + { + currentLayer->nextLayer = newLayer; + return; + } + currentLayer = currentLayer->nextLayer; + } +} + + +bool deleteLayer(struct Node **headLayer, Node *deleteLayer) +{ + Node *currentLayer = *headLayer; + if(deleteLayer == *headLayer) + { + *headLayer = currentLayer->nextLayer; + delete deleteLayer; + return true; + } + + while(currentLayer) + { + if(currentLayer->nextLayer == deleteLayer) + { + currentLayer->nextLayer = deleteLayer->nextLayer; + delete deleteLayer; + return true; + } + currentLayer = currentLayer->nextLayer; + } + return false; +} + + +struct Node *searchLayer(struct Node *headLayer, Glib::ustring data) + { + Node *currentLayer = headLayer; + while(currentLayer) + { + if(currentLayer->data == data) + { + return currentLayer; + } + currentLayer = currentLayer->nextLayer; + } + std::cout << "No Layer " << data << " in the CNN." << std::endl; +} + +Glib::ustring displayCNN(struct Node *headLayer) +{ + Node *cnn = headLayer; + Glib::ustring fullCnnLayerMatter = ""; + while(cnn) + { +// std::cout << cnn->data << std::endl; + fullCnnLayerMatter += cnn->data; + cnn = cnn->nextLayer; + } +// std::cout << std::endl; +// std::cout << std::endl; + return fullCnnLayerMatter; +} diff --git a/detectors/include/od/detectors/global2D/training/NormalizationWindow.h b/detectors/include/od/detectors/global2D/training/NormalizationWindow.h new file mode 100644 index 00000000..f48e1ad5 --- /dev/null +++ b/detectors/include/od/detectors/global2D/training/NormalizationWindow.h @@ -0,0 +1,345 @@ +#include "od/detectors/global2D/training/Network.h" + +void NetworkCreator::showWindow_normalizationLayerType(Glib::ustring data) +{ + remove(); + set_title("Normalization Layer"); + set_border_width(10); + add(m_sw_normalizationLayerType); + m_grid_normalizationLayerType.set_column_spacing (10); + m_grid_normalizationLayerType.set_row_spacing (50); + + //level 0 + if(data == "") + title_normalizationLayerType.set_text("Set the Properties of Normalization Layer type: BatchNorm"); + else + title_normalizationLayerType.set_text("Set the Properties of Normalization Layer type: " + data); + title_normalizationLayerType.set_line_wrap(); + title_normalizationLayerType.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_normalizationLayerType.attach(title_normalizationLayerType,0,0,2,1); + title_normalizationLayerType.show(); + + button_setNormalizationParameters.show(); + button_addMoreLayer3.show(); + + if(data == "" or data == "BatchNorm") + { + label_normalizationLayerTop.hide(); + label_normalizationLayerBottom.hide(); + label_normalizationLayerName.hide(); + text_normalizationLayerTop.hide(); + text_normalizationLayerBottom.hide(); + text_normalizationLayerName.hide(); + label_normalizationLayerlocalSize.hide(); + text_normalizationLayerlocalSize.hide(); + label_normalizationLayerAlpha.hide(); + text_normalizationLayerAlpha.hide(); + label_normalizationLayerBeta.hide(); + text_normalizationLayerBeta.hide(); + label_normalizationLayerK.hide(); + text_normalizationLayerK.hide(); + label_normalizationLayerNormRegion.hide(); + rbutton_normalizationLayerLRNWithin.hide(); + rbutton_normalizationLayerLRNAcross.hide(); + label_normalizationLayerAcrossChannel.hide(); + text_normalizationLayerAcrossChannel.hide(); + label_normalizationLayerNormalizeVariance.hide(); + text_normalizationLayerNormalizeVariance.hide(); + label_normalizationLayerEps.hide(); + text_normalizationLayerEps.hide(); + + + label_normalizationLayerBottom.set_text("Bottom Layer Name: "); + label_normalizationLayerBottom.set_line_wrap(); + label_normalizationLayerBottom.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_normalizationLayerType.attach(label_normalizationLayerBottom,0,1,2,1); + label_normalizationLayerBottom.show(); + + text_normalizationLayerBottom.set_max_length(100); + text_normalizationLayerBottom.set_text(""); + text_normalizationLayerBottom.select_region(0, text_normalizationLayerBottom.get_text_length()); +// m_grid_normalizationLayerType.attach(text_normalizationLayerBottom1,2,1,1,1); + text_normalizationLayerBottom.show(); + + label_normalizationLayerTop.set_text("Top Layer Name: "); + label_normalizationLayerTop.set_line_wrap(); + label_normalizationLayerTop.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_normalizationLayerType.attach(label_normalizationLayerTop,0,3,2,1); + label_normalizationLayerTop.show(); + + text_normalizationLayerTop.set_max_length(100); + text_normalizationLayerTop.set_text(""); + text_normalizationLayerTop.select_region(0, text_normalizationLayerTop.get_text_length()); +// m_grid_normalizationLayerType.attach(text_normalizationLayerTop,2,3,1,1); + text_normalizationLayerTop.show(); + + label_normalizationLayerName.set_text("Current Layer Name: "); + label_normalizationLayerName.set_line_wrap(); + label_normalizationLayerName.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_normalizationLayerType.attach(label_normalizationLayerName,0,4,2,1); + label_normalizationLayerName.show(); + + text_normalizationLayerName.set_max_length(100); + text_normalizationLayerName.set_text(""); + text_normalizationLayerName.select_region(0, text_normalizationLayerName.get_text_length()); +// m_grid_normalizationLayerType.attach(text_normalizationLayerName,2,4,1,1); + text_normalizationLayerName.show(); + } + else if(data == "LRN") + { + label_normalizationLayerTop.hide(); + label_normalizationLayerBottom.hide(); + label_normalizationLayerName.hide(); + text_normalizationLayerTop.hide(); + text_normalizationLayerBottom.hide(); + text_normalizationLayerName.hide(); + label_normalizationLayerlocalSize.hide(); + text_normalizationLayerlocalSize.hide(); + label_normalizationLayerAlpha.hide(); + text_normalizationLayerAlpha.hide(); + label_normalizationLayerBeta.hide(); + text_normalizationLayerBeta.hide(); + label_normalizationLayerK.hide(); + text_normalizationLayerK.hide(); + label_normalizationLayerNormRegion.hide(); + rbutton_normalizationLayerLRNWithin.hide(); + rbutton_normalizationLayerLRNAcross.hide(); + label_normalizationLayerAcrossChannel.hide(); + text_normalizationLayerAcrossChannel.hide(); + label_normalizationLayerNormalizeVariance.hide(); + text_normalizationLayerNormalizeVariance.hide(); + label_normalizationLayerEps.hide(); + text_normalizationLayerEps.hide(); + + + label_normalizationLayerBottom.set_text("Bottom Layer Name: "); + label_normalizationLayerBottom.set_line_wrap(); + label_normalizationLayerBottom.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_normalizationLayerType.attach(label_normalizationLayerBottom,0,1,2,1); + label_normalizationLayerBottom.show(); + + text_normalizationLayerBottom.set_max_length(100); + text_normalizationLayerBottom.set_text(""); + text_normalizationLayerBottom.select_region(0, text_normalizationLayerBottom.get_text_length()); +// m_grid_normalizationLayerType.attach(text_normalizationLayerBottom1,2,1,1,1); + text_normalizationLayerBottom.show(); + + label_normalizationLayerTop.set_text("Top Layer Name: "); + label_normalizationLayerTop.set_line_wrap(); + label_normalizationLayerTop.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_normalizationLayerType.attach(label_normalizationLayerTop,0,3,2,1); + label_normalizationLayerTop.show(); + + text_normalizationLayerTop.set_max_length(100); + text_normalizationLayerTop.set_text(""); + text_normalizationLayerTop.select_region(0, text_normalizationLayerTop.get_text_length()); +// m_grid_normalizationLayerType.attach(text_normalizationLayerTop,2,3,1,1); + text_normalizationLayerTop.show(); + + label_normalizationLayerName.set_text("Current Layer Name: "); + label_normalizationLayerName.set_line_wrap(); + label_normalizationLayerName.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_normalizationLayerType.attach(label_normalizationLayerName,0,4,2,1); + label_normalizationLayerName.show(); + + text_normalizationLayerName.set_max_length(100); + text_normalizationLayerName.set_text(""); + text_normalizationLayerName.select_region(0, text_normalizationLayerName.get_text_length()); +// m_grid_normalizationLayerType.attach(text_normalizationLayerName,2,4,1,1); + text_normalizationLayerName.show(); + + label_normalizationLayerlocalSize.set_text("Inner Parameter - local_size: "); + label_normalizationLayerlocalSize.set_line_wrap(); + label_normalizationLayerlocalSize.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_normalizationLayerType.attach(label_normalizationLayerlocalSize,0,5,2,1); + label_normalizationLayerlocalSize.show(); + + text_normalizationLayerlocalSize.set_max_length(100); + text_normalizationLayerlocalSize.set_text("5"); + text_normalizationLayerlocalSize.select_region(0, text_normalizationLayerlocalSize.get_text_length()); +// m_grid_normalizationLayerType.attach(text_normalizationLayerlocalSize,2,5,1,1); + text_normalizationLayerlocalSize.show(); + + label_normalizationLayerAlpha.set_text("Inner Parameter - alpha: "); + label_normalizationLayerAlpha.set_line_wrap(); + label_normalizationLayerAlpha.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_normalizationLayerType.attach(label_normalizationLayerAlpha,0,6,2,1); + label_normalizationLayerAlpha.show(); + + text_normalizationLayerAlpha.set_max_length(100); + text_normalizationLayerAlpha.set_text("0.0001"); + text_normalizationLayerAlpha.select_region(0, text_normalizationLayerAlpha.get_text_length()); +// m_grid_normalizationLayerType.attach(text_normalizationLayerAlpha,2,6,1,1); + text_normalizationLayerAlpha.show(); + + label_normalizationLayerBeta.set_text("Inner Parameter - beta: "); + label_normalizationLayerBeta.set_line_wrap(); + label_normalizationLayerBeta.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_normalizationLayerType.attach(label_normalizationLayerBeta,0,7,2,1); + label_normalizationLayerBeta.show(); + + text_normalizationLayerBeta.set_max_length(100); + text_normalizationLayerBeta.set_text("0.0001"); + text_normalizationLayerBeta.select_region(0, text_normalizationLayerBeta.get_text_length()); +// m_grid_normalizationLayerType.attach(text_normalizationLayerBeta,2,7,1,1); + text_normalizationLayerBeta.show(); + + label_normalizationLayerK.set_text("Inner Parameter - k: "); + label_normalizationLayerK.set_line_wrap(); + label_normalizationLayerK.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_normalizationLayerType.attach(label_normalizationLayerK,0,8,2,1); + label_normalizationLayerK.show(); + + text_normalizationLayerK.set_max_length(100); + text_normalizationLayerK.set_text("1"); + text_normalizationLayerK.select_region(0, text_normalizationLayerK.get_text_length()); +// m_grid_normalizationLayerType.attach(text_normalizationLayerK,2,8,1,1); + text_normalizationLayerK.show(); + + label_normalizationLayerNormRegion.set_text("Inner Parameter - norm_region: "); + label_normalizationLayerNormRegion.set_line_wrap(); + label_normalizationLayerNormRegion.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_normalizationLayerType.attach(label_normalizationLayerNormRegion,0,9,2,1); + label_normalizationLayerNormRegion.show(); + + Gtk::RadioButton::Group group5 = rbutton_normalizationLayerLRNWithin.get_group(); + rbutton_normalizationLayerLRNAcross.set_group(group5); + rbutton_normalizationLayerLRNWithin.set_active(); +// m_grid_normalizationLayerType.attach(rbutton_normalizationLayerLRNWithin,2,9,1,1); + rbutton_normalizationLayerLRNWithin.show(); +// m_grid_normalizationLayerType.attach(rbutton_normalizationLayerLRNAcross,3,9,1,1); + rbutton_normalizationLayerLRNAcross.show(); + + } + else if(data == "MVN") + { + label_normalizationLayerTop.hide(); + label_normalizationLayerBottom.hide(); + label_normalizationLayerName.hide(); + text_normalizationLayerTop.hide(); + text_normalizationLayerBottom.hide(); + text_normalizationLayerName.hide(); + label_normalizationLayerlocalSize.hide(); + text_normalizationLayerlocalSize.hide(); + label_normalizationLayerAlpha.hide(); + text_normalizationLayerAlpha.hide(); + label_normalizationLayerBeta.hide(); + text_normalizationLayerBeta.hide(); + label_normalizationLayerK.hide(); + text_normalizationLayerK.hide(); + label_normalizationLayerNormRegion.hide(); + rbutton_normalizationLayerLRNWithin.hide(); + rbutton_normalizationLayerLRNAcross.hide(); + label_normalizationLayerAcrossChannel.hide(); + text_normalizationLayerAcrossChannel.hide(); + label_normalizationLayerNormalizeVariance.hide(); + text_normalizationLayerNormalizeVariance.hide(); + label_normalizationLayerEps.hide(); + text_normalizationLayerEps.hide(); + + + label_normalizationLayerBottom.set_text("Bottom Layer Name: "); + label_normalizationLayerBottom.set_line_wrap(); + label_normalizationLayerBottom.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_normalizationLayerType.attach(label_normalizationLayerBottom,0,1,2,1); + label_normalizationLayerBottom.show(); + + text_normalizationLayerBottom.set_max_length(100); + text_normalizationLayerBottom.set_text(""); + text_normalizationLayerBottom.select_region(0, text_normalizationLayerBottom.get_text_length()); +// m_grid_normalizationLayerType.attach(text_normalizationLayerBottom1,2,1,1,1); + text_normalizationLayerBottom.show(); + + label_normalizationLayerTop.set_text("Top Layer Name: "); + label_normalizationLayerTop.set_line_wrap(); + label_normalizationLayerTop.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_normalizationLayerType.attach(label_normalizationLayerTop,0,3,2,1); + label_normalizationLayerTop.show(); + + text_normalizationLayerTop.set_max_length(100); + text_normalizationLayerTop.set_text(""); + text_normalizationLayerTop.select_region(0, text_normalizationLayerTop.get_text_length()); +// m_grid_normalizationLayerType.attach(text_normalizationLayerTop,2,3,1,1); + text_normalizationLayerTop.show(); + + label_normalizationLayerName.set_text("Current Layer Name: "); + label_normalizationLayerName.set_line_wrap(); + label_normalizationLayerName.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_normalizationLayerType.attach(label_normalizationLayerName,0,4,2,1); + label_normalizationLayerName.show(); + + text_normalizationLayerName.set_max_length(100); + text_normalizationLayerName.set_text(""); + text_normalizationLayerName.select_region(0, text_normalizationLayerName.get_text_length()); +// m_grid_normalizationLayerType.attach(text_normalizationLayerName,2,4,1,1); + text_normalizationLayerName.show(); + + label_normalizationLayerAcrossChannel.set_text("Inner Parameter - across_channels: \n(boolean- 0 or 1)"); + label_normalizationLayerAcrossChannel.set_line_wrap(); + label_normalizationLayerAcrossChannel.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_normalizationLayerType.attach(label_normalizationLayerAcrossChannel,0,10,2,1); + label_normalizationLayerAcrossChannel.show(); + + text_normalizationLayerAcrossChannel.set_max_length(100); + text_normalizationLayerAcrossChannel.set_text("0"); + text_normalizationLayerAcrossChannel.select_region(0, text_normalizationLayerAcrossChannel.get_text_length()); +// m_grid_normalizationLayerType.attach(text_normalizationLayerAcrossChannel,2,10,1,1); + text_normalizationLayerAcrossChannel.show(); + + label_normalizationLayerNormalizeVariance.set_text("Inner Parameter - normalize_variance: \n(boolean- 0 or 1)"); + label_normalizationLayerNormalizeVariance.set_line_wrap(); + label_normalizationLayerNormalizeVariance.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_normalizationLayerType.attach(label_normalizationLayerNormalizeVariance,0,11,2,1); + label_normalizationLayerNormalizeVariance.show(); + + text_normalizationLayerNormalizeVariance.set_max_length(100); + text_normalizationLayerNormalizeVariance.set_text("0"); + text_normalizationLayerNormalizeVariance.select_region(0, text_normalizationLayerNormalizeVariance.get_text_length()); +// m_grid_normalizationLayerType.attach(text_normalizationLayerNormalizeVariance,2,11,1,1); + text_normalizationLayerNormalizeVariance.show(); + + label_normalizationLayerEps.set_text("Inner Parameter - eps: "); + label_normalizationLayerEps.set_line_wrap(); + label_normalizationLayerEps.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_normalizationLayerType.attach(label_normalizationLayerEps,0,12,2,1); + label_normalizationLayerEps.show(); + + text_normalizationLayerEps.set_max_length(100); + text_normalizationLayerEps.set_text("100"); + text_normalizationLayerEps.select_region(0, text_normalizationLayerEps.get_text_length()); +// m_grid_normalizationLayerType.attach(text_normalizationLayerEps,2,12,1,1); + text_normalizationLayerEps.show(); + } + else + { + + label_normalizationLayerTop.hide(); + label_normalizationLayerBottom.hide(); + label_normalizationLayerName.hide(); + text_normalizationLayerTop.hide(); + text_normalizationLayerBottom.hide(); + text_normalizationLayerName.hide(); + label_normalizationLayerlocalSize.hide(); + text_normalizationLayerlocalSize.hide(); + label_normalizationLayerAlpha.hide(); + text_normalizationLayerAlpha.hide(); + label_normalizationLayerBeta.hide(); + text_normalizationLayerBeta.hide(); + label_normalizationLayerK.hide(); + text_normalizationLayerK.hide(); + label_normalizationLayerNormRegion.hide(); + rbutton_normalizationLayerLRNWithin.hide(); + rbutton_normalizationLayerLRNAcross.hide(); + label_normalizationLayerAcrossChannel.hide(); + text_normalizationLayerAcrossChannel.hide(); + label_normalizationLayerNormalizeVariance.hide(); + text_normalizationLayerNormalizeVariance.hide(); + label_normalizationLayerEps.hide(); + text_normalizationLayerEps.hide(); + } + + m_sw_normalizationLayerType.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); + m_grid_normalizationLayerType.show(); +// show_all_children(); + m_sw_normalizationLayerType.show(); +} diff --git a/detectors/include/od/detectors/global2D/training/Solver.h b/detectors/include/od/detectors/global2D/training/Solver.h new file mode 100644 index 00000000..d628a143 --- /dev/null +++ b/detectors/include/od/detectors/global2D/training/Solver.h @@ -0,0 +1,131 @@ +#pragma once + +#include <gtkmm/grid.h> +#include <gtkmm/entry.h> +#include <gtkmm/button.h> +#include <gtkmm/radiobutton.h> +#include <gtkmm/messagedialog.h> +#include <gtkmm/window.h> +#include <gtkmm/scrolledwindow.h> + +class SolverProperties : public Gtk::Window +{ + public: + SolverProperties(); + virtual ~SolverProperties(); + Glib::ustring solverFileName; + + protected: + void on_button_clicked(Glib::ustring data); + + // Child widgets: + Gtk::Grid m_grid1; + Gtk::ScrolledWindow m_sw1; + Gtk::Button button_solverFileName, + button_trainNetworkFileName, + button_testNetworkFileName, + button_testIter, + button_testInterval, + button_averageLoss, + button_randomSample, + button_display, + button_snapshot, + button_debugInfo, + button_testComputeLoss, + button_snapshotPrefix, + button_maxIter, + button_type, + button_learningRatePolicy, + button_baseLearningRate, + button_gamma, + button_power, + button_stepSize, + button_stepSizeValue, + button_weightDecay, + button_momentum, + button_saveFile; + Gtk::Entry text_solverFileName, + text_trainNetworkFileName, + text_testNetworkFileName, + text_testIter, + text_testInterval, + text_averageLoss, + text_randomSample, + text_display, + text_snapshot, + text_snapshotPrefix, + text_maxIter, + text_learningRatePolicy, + text_baseLearningRate, + text_gamma, + text_power, + text_stepSize, + text_stepSizeValue, + text_weightDecay, + text_momentum; + Gtk::Label label_solverFileName, + label_trainNetworkFileType, + label_trainNetworkFileName, + label_enableTestNet, + label_testNetworkFileName, + label_enableValidationParameters, + label_testIter, + label_testInterval, + label_enableAverageLoss, + label_averageLoss, + label_enableRandomSample, + label_randomSample, + label_display, + label_enableDebugInfo, + label_snapshot, + label_enableTestComputeLoss, + label_snapshotPrefix, + label_maxIter, + label_type, + label_learningRatePolicy, + label_baseLearningRate, + label_gamma, + label_power, + label_stepSize, + label_stepSizeValue, + label_weightDecay, + label_momentum; + Gtk::RadioButton rbutton_trainNetworkFileType_net, rbutton_trainNetworkFileType_tt, + rbutton_enableTestNet_no, rbutton_enableTestNet_yes, + rbutton_enableValidationParameters_no, rbutton_enableValidationParameters_yes, + rbutton_enableAverageLoss_no, rbutton_enableAverageLoss_yes, + rbutton_enableRandomSample_no, rbutton_enableRandomSample_yes, + rbutton_enableDebugInfo_no, rbutton_enableDebugInfo_yes, + rbutton_enableTestComputeLoss_no, rbutton_enableTestComputeLoss_yes, + rbutton_typeSGD_yes, rbutton_typeAdadelta_yes, rbutton_typeAdagrad_yes, rbutton_typeAdam_yes, + rbutton_typeRMSProp_yes, rbutton_typeNesterov_yes, + rbutton_learningRatePolicyFixed_yes, rbutton_learningRatePolicyExp_yes, + rbutton_learningRatePolicyStep_yes, rbutton_learningRatePolicyInv_yes, + rbutton_learningRatePolicyMultistep_yes, rbutton_learningRatePolicyPoly_yes, + rbutton_learningRatePolicySigmoid_yes; + + private: + + Glib::ustring trainNetworkFileName; + Glib::ustring testNetworkFileName; + Glib::ustring testIter; + Glib::ustring testInterval; + Glib::ustring averageLoss; + Glib::ustring randomSample; + Glib::ustring display; + Glib::ustring debugInfo; + Glib::ustring snapshot; + Glib::ustring testComputeLoss; + Glib::ustring snapshotPrefix; + Glib::ustring maxIter; + Glib::ustring type; + Glib::ustring learningRatePolicy; + Glib::ustring baseLearningRate; + Glib::ustring gamma; + Glib::ustring power; + Glib::ustring stepSize; + Glib::ustring stepSizeValue; + Glib::ustring weightDecay; + Glib::ustring momentum; +}; + diff --git a/detectors/src/global2D/CMakeLists.txt b/detectors/src/global2D/CMakeLists.txt index aac239d9..2bb038dd 100644 --- a/detectors/src/global2D/CMakeLists.txt +++ b/detectors/src/global2D/CMakeLists.txt @@ -11,12 +11,33 @@ if(BUILD_GLOBAL_2D_DETECTION) endif() set(SOURCES - "FaceRecognizer.cpp" - "detection/CascadeDetector.cpp" - "detection/CascadeDetectorImpl.cpp" - + "FaceRecognizer.cpp" + "detection/CascadeDetector.cpp" + "detection/CascadeDetectorImpl.cpp" ) + if(WITH_CAFFE AND Caffe_FOUND) + set(SOURCES ${SOURCES} + "detection/ConvClassification.cpp" + "localization/SelectiveSearchBase.cpp" + "localization/SelectiveSearchModel.cpp" + "training/HOGTrainer.cpp" + ) + set(SUBSYS_DEPS ${SUBSYS_DEPS} ${Caffe_LIBRARIES}) + include_directories(${CAFFE_INCLUDE_DIR}) + if(WITH_GPU) + include_directories(${CUDA_INCLUDE_DIRS}) + endif() + + if(WITH_GTKMM AND GTKMM_FOUND) + set(SOURCES ${SOURCES} "training/Solver.cpp" "training/ConvTrainer.cpp" "training/Network.cpp") + include_directories(${GTKMM_INCLUDE_DIRS}) + link_directories(${GTKMM_LIBRARY_DIRS}) + set(SUBSYS_DEPS ${SUBSYS_DEPS} ${GTKMM_LIBRARIES}) + endif() + + endif() + if(WITH_SVMLIGHT) set(SOURCES ${SOURCES} "training/HOGTrainer.cpp" "detection/HOGDetector.cpp") include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) diff --git a/detectors/src/global2D/detection/ConvClassification.cpp b/detectors/src/global2D/detection/ConvClassification.cpp new file mode 100644 index 00000000..1669b52a --- /dev/null +++ b/detectors/src/global2D/detection/ConvClassification.cpp @@ -0,0 +1,122 @@ +#include "od/detectors/global2D/detection/ConvClassification.h" + +namespace od +{ + namespace g2d + { + + ConvClassification::ConvClassification(const std::string & trained_data_location): + Detector2D(trained_data_location){} + + void ConvClassification::setWeightModelFileLocation(const std::string & location) + { + weightModelFileLoaction = location; + } + + void ConvClassification::setNetworkModelFileLocation(const std::string & location) + { + networkFileLocation = location; + } + + void ConvClassification::setImageFileLocation(const std::string & location) + { + imageFileLocation = location; + } + + std::string ConvClassification::getWeightModelFileLocation() + { + std::cout << "Weight Model File Location = " << weightModelFileLoaction << std::endl; + return weightModelFileLoaction; + } + + std::string ConvClassification::getNetworkModelFileLocation() + { + std::cout << "Network Model File Location = " << networkFileLocation << std::endl; + return networkFileLocation; + } + + std::string ConvClassification::getImageFileLocation() + { + std::cout << "Image File Location = " << imageFileLocation << std::endl; + return imageFileLocation; + } + + void ConvClassification::setTestBlob(int numChannels, int imgHeight, int imgWidth) + { + if (!ReadImageToDatum(imageFileLocation, numChannels, imgHeight, imgWidth, &strucBlob)) + { + std::cout << "Image File Not Found" << std::endl; + exit(-1); + } + caffe::Blob<float>* dataBlob = new caffe::Blob<float>(1, strucBlob.channels(), strucBlob.height(), strucBlob.width()); + + protoBlob.set_num(1); + protoBlob.set_channels(strucBlob.channels()); + protoBlob.set_height(strucBlob.height()); + protoBlob.set_width(strucBlob.width()); + const int data_size = strucBlob.channels() * strucBlob.height() * strucBlob.width(); + int sizeStrucBlob = std::max<int>(strucBlob.data().size(), strucBlob.float_data_size()); + for(int i = 0; i < sizeStrucBlob; ++i) + { + protoBlob.add_data(0.); + } + const std::string & data = strucBlob.data(); + if(data.size() != 0) + { + for (int i = 0; i < sizeStrucBlob; ++i) + { + protoBlob.set_data(i, protoBlob.data(i) + (uint8_t)data[i]); + } + } + + dataBlob->FromProto(protoBlob); + inputBlob.push_back(dataBlob); + + } + + void ConvClassification::classify() + { +#if(WITH_GPU) + caffe::Caffe::SetDevice(0); + caffe::Caffe::set_mode(caffe::Caffe::GPU); +#else + caffe::Caffe::set_mode(caffe::Caffe::CPU); +#endif + caffe::Net<float> net(networkFileLocation, caffe::TEST); + net.CopyTrainedLayersFrom(weightModelFileLoaction); + + float type = 0.0; + const std::vector<caffe::Blob<float>*>& result = net.Forward(inputBlob, &type); + float max = 0; + float max_i = 0; + for(int i = 0; i < 10; ++i) + { + float value = result[0]->cpu_data()[i]; + if (max < value) + { + max = value; + max_i = i; + } + } + std::cout << std::endl << std::endl << "****** OUTPUT *******" << std::endl; + std::cout << "classified image is digit " << max_i << std::endl << std::endl; + } + + void ConvClassification::init() + { + } + + shared_ptr<Detections2D> ConvClassification::detectOmni(shared_ptr<SceneImage> scene) + { + return make_shared<Detections2D>(); + } + + shared_ptr<Detections> ConvClassification::detect(shared_ptr<SceneImage> scene) + { + return make_shared<Detections>(); + } + + + + } +} diff --git a/detectors/src/global2D/localization/SelectiveSearchBase.cpp b/detectors/src/global2D/localization/SelectiveSearchBase.cpp new file mode 100644 index 00000000..e8bc35b4 --- /dev/null +++ b/detectors/src/global2D/localization/SelectiveSearchBase.cpp @@ -0,0 +1,145 @@ +#include "od/detectors/global2D/localization/SelectiveSearchBase.h" + +namespace od +{ + namespace g2d + { + + SelectiveSearchBase::SelectiveSearchBase(const std::string & trained_data_location): + Detector2D(trained_data_location){} + + void SelectiveSearchBase::acquireImages(const std::string & imageLocation, int imgWidth, int imgHeight) + { + inputImageHeight = imgHeight; + inputImageWidth = imgWidth; + img = cv::imread(imageLocation, CV_LOAD_IMAGE_COLOR); + cv::Size size(imgWidth, imgHeight); + cv::resize(img, img, size); + cluster = cv::imread(imageLocation, CV_LOAD_IMAGE_COLOR); + cv::resize(cluster, cluster, size); + outputImg = cv::imread(imageLocation, CV_LOAD_IMAGE_COLOR); + cv::resize(outputImg, outputImg, size); + } + + + cv::Mat SelectiveSearchBase::preProcessImg(const cv::Mat & image) + { + std::vector<cv::Mat> channels; + cv::Mat img_hist_equalized; + cv::cvtColor(image, img_hist_equalized, CV_BGR2YCrCb); + cv::split(img_hist_equalized,channels); + cv::equalizeHist(channels[0], channels[0]); + cv::merge(channels,img_hist_equalized); + cv::cvtColor(img_hist_equalized, img_hist_equalized, CV_YCrCb2BGR); + return img_hist_equalized; + } + + std::vector<std::vector<int> > SelectiveSearchBase::getSuperPixels(const cv::Mat & im, int & totalMasks, float Sigma, float K, + float Min_size, const std::string & imageLocation) + { + std::string img_location(imageLocation + std::string("img.ppm")); + imwrite(img_location, im); + float sigma = Sigma; + float k = K; + int min_size = Min_size; + image<rgb> *input = loadPPM(img_location.c_str()); + int num_ccs; + image<rgb> *seg = segment_image(input, sigma, k, min_size, &num_ccs); + image<uchar> *gr = imageRGBtoGRAY(seg); + int num = imRef(seg,0,0).r; + + std::ofstream myfile; + myfile.open("segmented.txt"); + for(int R = 0; R < seg->height(); R++) + { + for(int C = 0; C < seg->width(); C++) + { + int numR, numG, numB, numGr; + numR = imRef(seg,C,R).r; + numG = imRef(seg,C,R).g; + numB = imRef(seg,C,R).b; + numGr = imRef(gr,C,R); + myfile << numR << " " << numG << " " << numB << " " << numGr << std::endl; + } + } + std::string output_location("../images/img.ppm"); + savePPM(seg, output_location.c_str()); + myfile.close(); + + int H = 0; + int W = 0; + num = im.rows*im.cols; + std::vector<std::vector<int> > mask; + mask.resize(im.rows, std::vector<int>(im.cols, 0)); + int maskValue = 0; + int v = 0; + + std::ifstream infile("segmented.txt"); + int R,G,B,GR; + + std::vector<std::vector<int> > mem; + mem.resize(num, std::vector<int>(3, 0)); + std::vector<int> val; + val.resize(num, 0); + int insertion_mem_index = 1; + std::vector<int> query = {R, G, B}; + + for(int i = 0; i < num; i++) + { + infile >> R >> G >> B >> GR; + if(i==0) + { + mem[i][0] = R; + mem[i][1] = G; + mem[i][2] = B; + val[i] = maskValue; + mask[H][W] = val[0]; + } + else + { + auto pos = find(mem.begin(), mem.end(), query); + if(pos != mem.end()) + { + mask[H][W] = val[pos-mem.begin()]; + } + else + { + mem[insertion_mem_index][0] = R; + mem[insertion_mem_index][1] = G; + mem[insertion_mem_index][2] = B; + maskValue++; + insertion_mem_index++; + val[insertion_mem_index] = maskValue; + mask[H][W] = maskValue; + } + } + W = W + 1; + if(W == im.cols) + { + H = H + 1; + W = 0; + } + } + + totalMasks = maskValue; + return mask; + } + + + void SelectiveSearchBase::init() + { + } + + shared_ptr<Detections2D> SelectiveSearchBase::detectOmni(shared_ptr<SceneImage> scene) + { + return make_shared<Detections2D>(); + } + + shared_ptr<Detections> SelectiveSearchBase::detect(shared_ptr<SceneImage> scene) + { + return make_shared<Detections>(); + } + + } +} + diff --git a/detectors/src/global2D/localization/SelectiveSearchModel.cpp b/detectors/src/global2D/localization/SelectiveSearchModel.cpp new file mode 100644 index 00000000..3e392f72 --- /dev/null +++ b/detectors/src/global2D/localization/SelectiveSearchModel.cpp @@ -0,0 +1,604 @@ +#include "od/detectors/global2D/localization/SelectiveSearchModel.h" + +namespace od +{ + namespace g2d + { + SelectiveSearchModel::SelectiveSearchModel(const std::string & trained_data_location): + Detector2D(trained_data_location_){} + + void SelectiveSearchModel::setLabel(int l) + { + label = l; + } + + void SelectiveSearchModel::init() + { + } + + shared_ptr<Detections2D> SelectiveSearchModel::detectOmni(shared_ptr<SceneImage> scene) + { + return make_shared<Detections2D>(); + } + + shared_ptr<Detections> SelectiveSearchModel::detect(shared_ptr<SceneImage> scene) + { + return make_shared<Detections>(); + } + + shared_ptr<Detections> SelectiveSearchModel::detectOmni(shared_ptr<Scene> scene) + { + std::cout << "not implemented, use with shared_ptr<SceneImage>" <<std::endl; + return nullptr; + } + + cv::Mat get_hess_hist_xx(const cv::Mat & regionMask, int histSize, float hist_range_min, float hist_range_max) + { + cv::Mat kernel_filter_1(7,7, CV_32FC1,1); + kernel_filter_1 = (cv::Mat_<double>(7,7) << 1.57130243e-04, 7.17839338e-04, 0, -1.76805171e-03, 0, 7.17839338e-04, 1.57130243e-04, + 1.91423823e-03, 8.74507340e-03, 0, -2.15392793e-02, 0, 8.74507340e-03, 1.91423823e-03, + 8.57902057e-03, 3.91926999e-02, 0, -9.65323526e-02, 0, 3.91926999e-02, 8.57902057e-03, + 1.41444137e-02, 6.46178379e-02, 0, -1.59154943e-01, 0, 6.46178379e-02, 1.41444137e-02, + 8.57902057e-03, 3.91926999e-02, 0, -9.65323526e-02, 0, 3.91926999e-02, 8.57902057e-03, + 1.91423823e-03, 8.74507340e-03, 0, -2.15392793e-02, 0, 8.74507340e-03, 1.91423823e-03, + 1.57130243e-04, 7.17839338e-04, 0, -1.76805171e-03, 0, 7.17839338e-04, 1.57130243e-04 + ); + cv::Mat image_filtered_xx; + cv::filter2D(regionMask, image_filtered_xx, -1, kernel_filter_1, cv::Point(-1,-1), 0, cv::BORDER_DEFAULT ); + cv::Mat xx_hist; + float range[] = { hist_range_min, hist_range_max } ; + const float * histRange = { range }; + bool uniform = true; + bool accumulate = false; + cv::calcHist(&image_filtered_xx, 1, 0, cv::Mat(), xx_hist, 1, &histSize, &histRange, uniform, accumulate ); + return xx_hist; + } + + cv::Mat get_hess_hist_xy(const cv::Mat & regionMask, int histSize, float hist_range_min, float hist_range_max) + { + cv::Mat kernel_filter_2(7,7, CV_32FC1,1); + kernel_filter_2 = (cv::Mat_<double>(7,7) << 0.00017677, 0.00143568, 0.00321713, 0, -0.00321713, -0.00143568, -0.00017677, + 0.00143568, 0.0116601, 0.02612847, 0, -0.02612847, -0.0116601, -0.00143568, + 0.00321713, 0.02612847, 0.05854983, 0, -0.05854983, -0.02612847, -0.00321713, + 0, 0, 0, 0, 0, 0, 0, + -0.00321713, -0.02612847, -0.05854983, 0, 0.05854983, 0.02612847, 0.00321713, + -0.00143568, -0.0116601, -0.02612847, 0, 0.02612847, 0.0116601, 0.00143568, + -0.00017677, -0.00143568, -0.00321713, 0, 0.00321713, 0.00143568, 0.00017677 + ); + cv::Mat image_filtered_xy; + cv::filter2D(regionMask, image_filtered_xy, -1, kernel_filter_2, cv::Point(-1,-1), 0, cv::BORDER_DEFAULT ); + cv::Mat xy_hist; + float range[] = { hist_range_min, hist_range_max } ; + const float * histRange = { range }; + bool uniform = true; bool accumulate = false; + cv::calcHist( &image_filtered_xy, 1, 0, cv::Mat(), xy_hist, 1, &histSize, &histRange, uniform, accumulate ); + return xy_hist; + } + + + + cv::Mat get_hess_hist_yy(const cv::Mat & regionMask, int histSize, float hist_range_min, float hist_range_max) + { + cv::Mat kernel_filter_3(7,7, CV_32FC1,1); + kernel_filter_3 = (cv::Mat_<double>(7,7) << + 1.57130243e-04, 1.91423823e-03, 8.57902057e-03, 1.41444137e-02, 8.57902057e-03, 1.91423823e-03, 1.57130243e-04, + 7.17839338e-04, 8.74507340e-03, 3.91926999e-02, 6.46178379e-02, 3.91926999e-02, 8.74507340e-03, 7.17839338e-04, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + -1.76805171e-03, -2.15392793e-02, -9.65323526e-02, -1.59154943e-01, -9.65323526e-02, -2.15392793e-02, -1.76805171e-03, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 7.17839338e-04, 8.74507340e-03, 3.91926999e-02, 6.46178379e-02, 3.91926999e-02, 8.74507340e-03, 7.17839338e-04, + 1.57130243e-04, 1.91423823e-03, 8.57902057e-03, 1.41444137e-02, 8.57902057e-03, 1.91423823e-03, 1.57130243e-04 + ); + cv::Mat image_filtered_yy; + cv::filter2D(regionMask, image_filtered_yy, -1, kernel_filter_3, cv::Point(-1,-1), 0, cv::BORDER_DEFAULT ); + cv::Mat yy_hist; + float range[] = { hist_range_min, hist_range_max } ; + const float * histRange = { range }; + bool uniform = true; + bool accumulate = false; + + cv::calcHist(&image_filtered_yy, 1, 0, cv::Mat(), yy_hist, 1, &histSize, &histRange, uniform, accumulate ); + return yy_hist; + } + + cv::Mat get_orientation_hist(const cv::Mat & regionMask, int histSize, float hist_range_min, float hist_range_max) + { + cv::Mat kernel_filter_1(3,3, CV_32FC1,1); + kernel_filter_1 = (cv::Mat_<double>(3,3) << 1, 1, 1, 1, -8, 1, 1, 1, 1); + cv::Mat kernel_filter_2(3,3, CV_32FC1,1); + kernel_filter_2 = (cv::Mat_<double>(3,3) << 0,0,0,0,1,0,0,0,0); + cv::Mat kernel_filter_3(3,3, CV_32FC1,1); + kernel_filter_3 = (cv::Mat_<double>(3,3) << 1,2,1,0,0,0,-1,-2,-1); + cv::Mat kernel_filter_4(3,3, CV_32FC1,1); + kernel_filter_4 = (cv::Mat_<double>(3,3) << 1,0,-1,2,0,-2,1,0,-1); + cv::Mat image_filtered_v1; + cv::Mat image_filtered_v2; + cv::Mat image_filtered_v3; + cv::Mat image_filtered_v4; + int temp30; + //filtering + cv::filter2D(regionMask, image_filtered_v1, -1, kernel_filter_1, cv::Point(-1,-1), 0, cv::BORDER_DEFAULT ); + cv::filter2D(regionMask, image_filtered_v2, -1, kernel_filter_2, cv::Point(-1,-1), 0, cv::BORDER_DEFAULT ); + cv::filter2D(regionMask, image_filtered_v3, -1, kernel_filter_3, cv::Point(-1,-1), 0, cv::BORDER_DEFAULT ); + cv::filter2D(regionMask, image_filtered_v4, -1, kernel_filter_4, cv::Point(-1,-1), 0, cv::BORDER_DEFAULT ); + + //Orientation New + float temp_5; + float temp_6; + float temp_7_theta; + float temp_8_theta_dash; + int temp_9_theta_dash_quantized; + float quantized_1[12] = {0,1,2,3,4,5,6,7,8,9,10,11}; + int quantized_count_1 = 0; + float orientation_image_matrix[regionMask.rows][regionMask.cols]; + cv::Mat orientation_image = regionMask.clone(); + for(int r = 0; r < regionMask.rows; r++) + { + for(int c = 0; c < regionMask.cols; c++) + { + orientation_image_matrix[r][c] = 0; + } + } + for(int r = 0; r < regionMask.rows; r++) + { + for(int c = 0; c < regionMask.cols; c++) + { + temp_5 = image_filtered_v3.at<schar>(r,c); + temp_6 = image_filtered_v4.at<schar>(r,c); + if(temp_6 != 0 && temp_5 != 0) + { + temp_7_theta = atan(temp_5/temp_6); + } + else if(temp_6 == 0 && temp_5 > 0) + { + temp_7_theta = M_PI/2; + } + else if(temp_6 == 0 && temp_5 < 0) + { + temp_7_theta = -M_PI/2; + } + else if(temp_6 == 0 && temp_5 == 0) + { + temp_7_theta = 0; + } + else if(temp_6 != 0 && temp_5 == 0) + { + temp_7_theta = 0; + } + + if(temp_5 >= 0 && temp_6 >= 0) + { + temp_8_theta_dash = temp_7_theta; + } + else if(temp_5 < 0 && temp_6 >= 0) + { + temp_8_theta_dash = temp_7_theta + M_PI; + } + else if(temp_5 < 0 && temp_6 < 0) + { + temp_8_theta_dash = temp_7_theta + M_PI; + } + else if(temp_5 >= 0 && temp_6 < 0) + { + temp_8_theta_dash = temp_7_theta + 2*M_PI; + } + temp_9_theta_dash_quantized = floor((temp_8_theta_dash*11)/(2*M_PI)); + orientation_image_matrix[r][c] = temp_9_theta_dash_quantized; + orientation_image.at<uchar>(r,c) = temp_9_theta_dash_quantized; + } + } + /* for(int r = 0; r < img.rows; r++) + { + for(int c = 0; c < img.cols; c++) + { + cout << orientation_image_matrix[r][c] << " "; + } + cout << endl; + } + */ + float range[] = { hist_range_min, hist_range_max } ; + const float * histRange = { range }; + bool uniform = true; bool accumulate = false; + cv::Mat orientation_image_hist; + + /// Compute the histograms: + cv::calcHist( &orientation_image, 1, 0, cv::Mat(), orientation_image_hist, 1, &histSize, &histRange, uniform, accumulate ); + return orientation_image_hist; + } + + cv::Mat get_diff_exci_hist(const cv::Mat & regionMask, int histSize, float hist_range_min, float hist_range_max) + { + cv::Mat kernel_filter_1(3,3, CV_32FC1,1); + kernel_filter_1 = (cv::Mat_<double>(3,3) << 1, 1, 1, 1, -8, 1, 1, 1, 1); + cv::Mat kernel_filter_2(3,3, CV_32FC1,1); + kernel_filter_2 = (cv::Mat_<double>(3,3) << 0,0,0,0,1,0,0,0,0); + cv::Mat kernel_filter_3(3,3, CV_32FC1,1); + kernel_filter_3 = (cv::Mat_<double>(3,3) << 1,2,1,0,0,0,-1,-2,-1); + cv::Mat kernel_filter_4(3,3, CV_32FC1,1); + kernel_filter_4 = (cv::Mat_<double>(3,3) << 1,0,-1,2,0,-2,1,0,-1); + cv::Mat image_filtered_v1; + cv::Mat image_filtered_v2; + cv::Mat image_filtered_v3; + cv::Mat image_filtered_v4; + int temp30; + //filtering + cv::filter2D(regionMask, image_filtered_v1, -1, kernel_filter_1, cv::Point(-1,-1), 0, cv::BORDER_DEFAULT ); + cv::filter2D(regionMask, image_filtered_v2, -1, kernel_filter_2, cv::Point(-1,-1), 0, cv::BORDER_DEFAULT ); + cv::filter2D(regionMask, image_filtered_v3, -1, kernel_filter_3, cv::Point(-1,-1), 0, cv::BORDER_DEFAULT ); + cv::filter2D(regionMask, image_filtered_v4, -1, kernel_filter_4, cv::Point(-1,-1), 0, cv::BORDER_DEFAULT ); + + //Differential Excitation New + float temp_1; + float temp_2; + float temp_3_alpha; + float temp_4_quantized_alpha; + int quantized[8] = {0,1,2,3,4,5,6,7}; + int quantized_count = 0; + int differential_excitation_image_matrix[regionMask.rows][regionMask.cols]; + cv::Mat differential_excitation_image = regionMask.clone(); + + for(int r = 0; r < regionMask.rows; r++) + { + for(int c = 0; c < regionMask.cols; c++) + { + differential_excitation_image_matrix[r][c] = 0; + } + } + for(int r = 0; r < regionMask.rows; r++) + { + for(int c = 0; c < regionMask.cols; c++) + { + temp_1 = image_filtered_v1.at<schar>(r,c); + temp_2 = image_filtered_v2.at<schar>(r,c); + if(temp_2 != 0) + { + temp_3_alpha = atan(temp_1/temp_2); + } + else if(temp_2 == 0 && temp_1 > 0) + { + temp_3_alpha = M_PI/2; + } + else if(temp_2 == 0 && temp_1 < 0) + { + temp_3_alpha = -M_PI/2; + } + else if(temp_2 == 0 && temp_1 == 0) + { + temp_3_alpha = 0; + } + temp_4_quantized_alpha = floor(((temp_3_alpha + M_PI/2)/M_PI)*7); + differential_excitation_image_matrix[r][c] = temp_4_quantized_alpha; + differential_excitation_image.at<uchar>(r,c) = temp_4_quantized_alpha; + } + } + /* for(int r = 0; r < img.rows; r++) + { + for(int c = 0; c < img.cols; c++) + { + cout << differential_excitation_image_matrix[r][c] << " "; + } + cout << endl; + } + */ + float range[] = { hist_range_min, hist_range_max } ; + const float* histRange = { range }; + bool uniform = true; + bool accumulate = false; + cv::Mat differential_excitation_hist; + + /// Compute the histograms: + cv::calcHist(&differential_excitation_image, 1, 0, cv::Mat(), differential_excitation_hist, 1, + &histSize, &histRange, uniform, accumulate ); + return differential_excitation_hist; + } + + void refineRegions(std::vector<std::vector<int> > sp, int total_masks, SelectiveSearchModel regions[], int min_height, int min_width) + { + for(int i = 0; i < total_masks; i++) + { + regions[i].setLabel(i); + regions[i].min_x = 100000; + regions[i].min_y = 100000; + regions[i].max_x = -1; + regions[i].max_y = -1; + } + for(int r = 0; r < sp.size(); r++) + { + for(int c = 0; c < sp[0].size(); c++) + { + regions[sp[r][c]].size++; + if(regions[sp[r][c]].min_x > c) + regions[sp[r][c]].min_x = c; + if(regions[sp[r][c]].min_y > r) + regions[sp[r][c]].min_y = r; + if(regions[sp[r][c]].max_x < c) + regions[sp[r][c]].max_x = c; + if(regions[sp[r][c]].max_y < r) + regions[sp[r][c]].max_y = r; + } + } + for(int i = 0; i < total_masks; i++) + { + if((regions[i].max_x - regions[i].min_x > min_width) && (regions[i].max_y - regions[i].min_y > min_height)) + regions[i].validity = true; + else + regions[i].validity = false; + } + } + + + + void createModel(std::vector<std::vector<int> > sp, int total_masks, const cv::Mat & grayMask, SelectiveSearchModel regions[], int histSize, + float hist_range_min, float hist_range_max) + { + cv::Mat regionMask = grayMask.clone(); + + for(int i = 0; i < total_masks; i++) + { + regionMask = grayMask.clone(); + // cout << i << endl; + for(int r = 0; r < sp.size(); r++) + { + for(int c = 0; c < sp[0].size(); c++) + { + if(sp[r][c] != i) + regionMask.at<uchar>(r,c) = 0; + } + } + if(regions[i].size<200) + regions[i].validity = false; + // cout << i << " hessian" << endl; + //Hessian Matrix + regions[i].xx_hist = get_hess_hist_xx(regionMask, histSize, hist_range_min, hist_range_max); + regions[i].xy_hist = get_hess_hist_xy(regionMask, histSize, hist_range_min, hist_range_max); + regions[i].yy_hist = get_hess_hist_yy(regionMask, histSize, hist_range_min, hist_range_max); + + // cout << i << " orien" << endl; + //orientation Matrix + regions[i].orientation_image_hist = get_orientation_hist(regionMask, histSize, hist_range_min, hist_range_max); + + // cout << i << " diff" << endl; + //Differential Excitation Matrix + regions[i].differential_excitation_hist = get_diff_exci_hist(regionMask, histSize, hist_range_min, hist_range_max); + + // cout << i << " color" << endl; + //Color Histogram + float range[] = {hist_range_min, hist_range_max}; + const float* histRange = {range}; + bool uniform = true; + bool accumulate = false; + cv::calcHist(&grayMask, 1, 0, cv::Mat(), regions[i].color_hist, 1, &histSize, &histRange, uniform, accumulate); + + + } + } + + bool checkNeighbors(const SelectiveSearchModel & a, const SelectiveSearchModel & b) + { + if( + ((a.min_x < b.min_x) && (b.min_x < a.max_x) && (a.min_y < b.min_y) && (b.min_y < a.max_y)) + || + ((a.min_x < b.max_x) && (b.max_x < a.max_x) && (a.min_y < b.max_y) && (b.max_y < a.max_y)) + || + ((a.min_x < b.min_x) && (b.min_x < a.max_x) && (a.min_y < b.max_y) && (b.max_y < a.max_y)) + || + ((a.min_x < b.max_x) && (b.min_x < a.max_x) && (a.min_y < b.max_y) && (b.max_y < a.max_y)) + ) + { + + return true; + } + + return false; + } + + std::vector<std::vector<int> > findNeighbors(SelectiveSearchModel regions[], int total_masks, const cv::Mat & regionMask, + const std::vector<std::vector<int> > & sp) + { + std::vector<std::vector<int> > neighbors; + std::vector<int> rows; + rows.push_back(0); + rows.push_back(0); + int num = 0; + for(int i = 1; i < total_masks-1; i++) + { + for(int j = i+1; j < i+20; j++) + { + if(j < total_masks-2) + { + if(checkNeighbors(regions[i], regions[j]) && regions[i].validity && regions[j].validity) + { + /* cout << i << " " << j << endl; + cout << regions[i].min_x << " " << regions[i].min_y << " " << regions[i].max_x << " " << regions[i].max_y << endl; + cout << regions[j].min_x << " " << regions[j].min_y << " " << regions[j].max_x << " " << regions[j].max_y << endl; + for (int r = 0; r < regionMask.rows; r++) + { + for(int c = 0; c < regionMask.cols; c++) + { + if(sp[r][c] != i) + { + regionMask.at<Vec3b>(r,c)[0] = 0; + regionMask.at<Vec3b>(r,c)[1] = 0; + regionMask.at<Vec3b>(r,c)[2] = 0; + } + else + { + regionMask.at<Vec3b>(r,c)[0] = 0; + regionMask.at<Vec3b>(r,c)[1] = 0; + regionMask.at<Vec3b>(r,c)[2] = 255; + } + } + } + for (int r = 0; r < regionMask.rows; r++) + { + for(int c = 0; c < regionMask.cols; c++) + { + if(sp[r][c] != j and regionMask.at<Vec3b>(r,c)[2] != 255) + { + regionMask.at<Vec3b>(r,c)[0] = 0; + regionMask.at<Vec3b>(r,c)[1] = 0; + regionMask.at<Vec3b>(r,c)[2] = 0; + } + else + { + regionMask.at<Vec3b>(r,c)[0] = 0; + regionMask.at<Vec3b>(r,c)[1] = 255; + regionMask.at<Vec3b>(r,c)[2] = 0; + } + } + } + imshow("regionMask", regionMask); + waitKey(0); + */ + rows[0] = i; + rows[1] = j; + neighbors.push_back(rows); + } + } + } + } + return neighbors; + } + + float calcSimilarities(const SelectiveSearchModel & a, const SelectiveSearchModel & b, float spSize) + { + double sim = 0.0; + sim += compareHist( a.xx_hist, b.xx_hist, 1); + sim += compareHist( a.xy_hist, b.xy_hist, 1); + sim += compareHist( a.yy_hist, b.yy_hist, 1); + sim += compareHist( a.orientation_image_hist, b.orientation_image_hist, 1); + sim += compareHist( a.differential_excitation_hist, b.differential_excitation_hist, 1); + sim += compareHist( a.color_hist, b.color_hist, 1); + sim += 100 * ((a.size + b.size)/spSize); + double bbsize = ((std::max(a.max_x, b.max_x) - std::min(a.min_x, b.min_x)) * (std::max(a.max_y, b.max_y) - std::min(a.min_y, b.min_y))); + sim += 100 * ((bbsize - a.size - b.size) / spSize); + return sim; + } + + void mergeRegions(int value, SelectiveSearchModel regions[], const std::vector<std::vector<int> > & sp_neighbors, const cv::Mat &grayMask, + const std::vector<std::vector<int> > & sp, int minRegionSize, int histSize, float hist_range_min, float hist_range_max) + { + /* + void setBoundaries(int ix, int iy, int ax, int ay); + int label; + int min_x; + int max_x; + int min_y; + int max_y; + int size; + Mat xx_hist, xy_hist, yy_hist, orientation_image_hist,differential_excitation_hist, color_hist; + vector <int> neighbors; + bool validity; + */ + regions[sp_neighbors[value][0]].validity = true; + regions[sp_neighbors[value][1]].validity = false; + regions[sp_neighbors[value][0]].min_x = std::min(regions[sp_neighbors[value][0]].min_x, regions[sp_neighbors[value][1]].min_x); + regions[sp_neighbors[value][0]].max_y = std::max(regions[sp_neighbors[value][0]].max_y, regions[sp_neighbors[value][1]].max_y); + regions[sp_neighbors[value][0]].min_x = std::min(regions[sp_neighbors[value][0]].min_x, regions[sp_neighbors[value][1]].min_x); + regions[sp_neighbors[value][0]].max_y = std::max(regions[sp_neighbors[value][0]].max_y, regions[sp_neighbors[value][1]].max_y); + regions[sp_neighbors[value][0]].size = regions[sp_neighbors[value][0]].size + regions[sp_neighbors[value][1]].size; + + cv::Mat regionMask = grayMask.clone(); + int i = sp_neighbors[value][0]; + for(int r = 0; r < sp.size(); r++) + { + for(int c = 0; c < sp[0].size(); c++) + { + if(sp[r][c] != i) + regionMask.at<uchar>(r,c) = 0; + } + } + if(regions[i].size<minRegionSize) + regions[i].validity = false; + // cout << i << " hessian " << regionMask.channels() << endl; + //Hessian Matrix + regions[i].xx_hist = get_hess_hist_xx(regionMask, histSize, hist_range_min, hist_range_max); + regions[i].xy_hist = get_hess_hist_xy(regionMask, histSize, hist_range_min, hist_range_max); + regions[i].yy_hist = get_hess_hist_yy(regionMask, histSize, hist_range_min, hist_range_max); + + // cout << i << " orien" << endl; + //orientation Matrix + regions[i].orientation_image_hist = get_orientation_hist(regionMask, histSize, hist_range_min, hist_range_max); + + // cout << i << " diff" << endl; + //Differential Excitation Matrix + regions[i].differential_excitation_hist = get_diff_exci_hist(regionMask, histSize, hist_range_min, hist_range_max); + + // cout << i << " color" << endl; + //Color Histogram + float range[] = { hist_range_min, hist_range_max } ; + const float* histRange = { range }; + bool uniform = true; + bool accumulate = false; + cv::calcHist(&grayMask, 1, 0, cv::Mat(), regions[i].color_hist, 1, &histSize, &histRange, uniform, accumulate); + } + + bool checkRounds(int totals, SelectiveSearchModel regions[], int numRounds) + { + int num = 0; + for(int i = 0; i < totals; i++) + { + if(regions[i].validity == false) + { + num++; + } + } + // cout << "num = " << num << endl; + if(num > numRounds) + return true; + else + return false; + } + + std::vector<std::vector<int> > extractROIs(int total_masks, SelectiveSearchModel regions[], int numRounds, float spSize, + const std::vector<std::vector<int> > & sp, const cv::Mat & img, const cv::Mat & gray_mask, + int minRegionSize, int histSize, float hist_range_min, float hist_range_max) + { + std::vector<std::vector<int> > pts; + int totals = total_masks; + int value = 10; + std::vector<std::vector<int> > sp_neighbors; + + while(checkRounds(totals, regions, numRounds) && value > 0) + { + sp_neighbors = findNeighbors(regions, total_masks, img, sp); + std::vector<float> similarities; + for(int i = 0; i < sp_neighbors.size(); i++) + { + + float sim = calcSimilarities(regions[sp_neighbors[i][0]],regions[sp_neighbors[i][1]], spSize); + // cout << i << " " << sp_neighbors[i][0] << " " << sp_neighbors[i][1] << " " << sim << endl; + similarities.push_back(sim); + } + + //finding closest two regions + value = min_element(similarities.begin(), similarities.end()) - similarities.begin(); + // cout << "in here" << endl; + + //merging + mergeRegions(value, regions, sp_neighbors, gray_mask, sp, minRegionSize, histSize, hist_range_min, hist_range_max); + // cout << "value = " << value << endl; + for(int i = 0; i < total_masks; i++) + { + if(regions[i].validity == true) + { + std::vector<int> temp; + temp.push_back(regions[i].min_x); + temp.push_back(regions[i].min_y); + temp.push_back(regions[i].max_x); + temp.push_back(regions[i].max_y); + pts.push_back(temp); + // rectangle(outputImg, Point(regions[i].min_x, regions[i].min_y), Point(regions[i].max_x, regions[i].max_y), Scalar(0, 0, 255)); + } + } + } + std::sort(pts.begin(), pts.end()); + pts.erase(std::unique(pts.begin(), pts.end()), pts.end()); + std::cout << "Total bounding boxes = " << pts.size() << std::endl; + return pts; + } + + } +} + + diff --git a/detectors/src/global2D/training/ConvTrainer.cpp b/detectors/src/global2D/training/ConvTrainer.cpp new file mode 100644 index 00000000..e60bf9be --- /dev/null +++ b/detectors/src/global2D/training/ConvTrainer.cpp @@ -0,0 +1,53 @@ +#include "od/detectors/global2D/training/ConvTrainer.h" + +namespace od +{ + namespace g2d + { + + ConvTrainer::ConvTrainer(const std::string & training_input_location, + const std::string & trained_data_location): + Trainer(training_input_location, trained_data_location){} + + int ConvTrainer::train() + { + return 1; + } + + void ConvTrainer::setSolverLocation(const std::string & location) + { + solverLocation = location; + } + + void ConvTrainer::setSolverProperties(int argc, char *argv[]) + { + auto app = Gtk::Application::create(argc, argv, std::string("org.gtkmm.example")); + SolverProperties solverProperties; + solverProperties.set_default_geometry(10000, 10000); + app->run(solverProperties); + solverLocation = solverProperties.solverFileName; + } + + void ConvTrainer::createCustomNetwork(int argc, char *argv[]) + { + auto app = Gtk::Application::create(argc, argv, std::string("org.gtkmm.example")); + NetworkCreator networkCreator; + networkCreator.set_default_geometry(10000, 10000); + app->run(networkCreator); + } + + void ConvTrainer::startTraining() + { +#if(WITH_GPU) + caffe::Caffe::SetDevice(0); + caffe::Caffe::set_mode(caffe::Caffe::GPU); +#else + caffe::Caffe::set_mode(caffe::Caffe::CPU); +#endif + caffe::SGDSolver<float> s(solverLocation); + s.Solve(); + } + + + } +} diff --git a/detectors/src/global2D/training/Network.cpp b/detectors/src/global2D/training/Network.cpp new file mode 100644 index 00000000..927ba7b2 --- /dev/null +++ b/detectors/src/global2D/training/Network.cpp @@ -0,0 +1,1667 @@ +#include "od/detectors/global2D/training/Network.h" +#include "od/detectors/global2D/training/MainWindow.h" +#include "od/detectors/global2D/training/ActivationWindow.h" +#include "od/detectors/global2D/training/DisplayWindow.h" +#include "od/detectors/global2D/training/CriticalWindow.h" +#include "od/detectors/global2D/training/NormalizationWindow.h" +#include "od/detectors/global2D/training/LossWindow.h" +#include "od/detectors/global2D/training/ExtraWindow.h" +#include "od/detectors/global2D/training/Node.h" + +struct Node * newHead; +struct Node * headLayer = new Node; + +NetworkCreator::NetworkCreator(): + label_networkFileName(""), + button_networkFileName("Update"), + text_networkFileName(), + + button_activationLayerType("Append Activation Layer"), + label_activationLayerType(""), + + button_criticalLayerType("Append Crtitical Layer"), + label_criticalLayerType(""), + + label_normalizationLayerType(""), + + label_lossLayerType(""), + + label_extraLayerType(""), + + button_setActivationParameters("Add This Layer"), + button_addMoreLayer("Add More Layers"), + text_activationLayerTop(), + text_activationLayerBottom(), + text_activationLayerName(), + text_activationLayerType(), + text_activationLayerScale(), + text_activationLayerShift(), + text_activationLayerBase(), + text_activationLayerNegativeSlope(), + label_activationLayerTop(""), + label_activationLayerBottom(""), + label_activationLayerName(""), + label_activationLayerScale(""), + label_activationLayerShift(""), + label_activationLayerBase(""), + label_activationLayerNegativeSlope(""), + + button_setCriticalParameters("Add This Layer"), + button_addMoreLayer2("Add more Layers"), + label_criticalLayerTop(""), + label_criticalLayerBottom1(""), + label_criticalLayerBottom2(""), + label_criticalLayerName(""), + label_criticalLayerFilterLr(""), + label_criticalLayerFilterDm(""), + label_criticalLayerBiasLr(""), + label_criticalLayerBiasDm(""), + label_criticalLayerNumOutput(""), + label_criticalLayerKernelW(""), + label_criticalLayerKernelH(""), + label_criticalLayerStrideW(""), + label_criticalLayerStrideH(""), + label_criticalLayerPadW(""), + label_criticalLayerPadH(""), + label_criticalLayerWeightFiller(""), + label_criticalLayerWeightFillerConstantValue(""), + label_criticalLayerWeightFillerUniformMin(""), + label_criticalLayerWeightFillerUniformMax(""), + label_criticalLayerWeightFillerGaussianMean(""), + label_criticalLayerWeightFillerGaussianStd(""), + label_criticalLayerWeightFillerXavierVariance(""), + label_criticalLayerWeightFillerMSRAVariance(""), + label_criticalLayerDropoutRatio(""), + label_criticalLayerPool(""), + text_criticalLayerTop(), + text_criticalLayerBottom1(), + text_criticalLayerBottom2(), + text_criticalLayerName(), + text_criticalLayerFilterLr(), + text_criticalLayerFilterDm(), + text_criticalLayerBiasLr(), + text_criticalLayerBiasDm(), + text_criticalLayerNumOutput(), + text_criticalLayerKernelW(), + text_criticalLayerKernelH(), + text_criticalLayerStrideW(), + text_criticalLayerStrideH(), + text_criticalLayerPadW(), + text_criticalLayerPadH(), + text_criticalLayerWeightFillerConstantValue(), + text_criticalLayerWeightFillerUniformMin(), + text_criticalLayerWeightFillerUniformMax(), + text_criticalLayerWeightFillerGaussianMean(), + text_criticalLayerWeightFillerGaussianStd(), + text_criticalLayerDropoutRatio(), + rbutton_criticalLayerWeightFillerConstant("Constant"), rbutton_criticalLayerWeightFillerUniform("Uniform"), + rbutton_criticalLayerWeightGaussian("Gaussian"), rbutton_criticalLayerWeightFillerPositiveUnitBall("Positive Unit Ball"), + rbutton_criticalLayerWeightFillerXavier("Xavier"), rbutton_criticalLayerWeightFillerMSRA("MSRA"), + rbutton_criticalLayerWeightFillerBilinear("Bilinear"), + rbutton_criticalLayerWeightFillerXavierIn("FAN_IN"), rbutton_criticalLayerWeightFillerXavierOut("FAN_OUT"), + rbutton_criticalLayerWeightFillerXavierAvg("AVERAGE"), + rbutton_criticalLayerWeightFillerMSRAIn("FAN_IN"), rbutton_criticalLayerWeightFillerMSRAOut("FAN_OUT"), + rbutton_criticalLayerWeightFillerMSRAAvg("AVERAGE"), + rbutton_criticalLayerPoolMax("MAX"), rbutton_criticalLayerPoolAve("AVE"), + + button_normalizationLayerType("Append Normalization Layer"), + button_lossLayerType("Add Loss Layer"), + button_extraLayerType("Append Layer"), + button_addMoreLayer3("Add More Layers"), + button_setNormalizationParameters("Add This Layer"), + text_normalizationLayerTop(), + text_normalizationLayerBottom(), + text_normalizationLayerName(), + label_normalizationLayerTop(""), + label_normalizationLayerBottom(""), + label_normalizationLayerName(""), + text_normalizationLayerlocalSize(), + label_normalizationLayerlocalSize(""), + text_normalizationLayerAlpha(), + text_normalizationLayerBeta(), + text_normalizationLayerK(), + label_normalizationLayerAlpha(""), + label_normalizationLayerBeta(""), + label_normalizationLayerK(""), + label_normalizationLayerNormRegion(""), + rbutton_normalizationLayerLRNWithin("WITHIN_CHANNEL"), rbutton_normalizationLayerLRNAcross("ACCROSS_CHANNEL"), + text_normalizationLayerAcrossChannel(), + text_normalizationLayerNormalizeVariance(), + text_normalizationLayerEps(), + label_normalizationLayerAcrossChannel(""), + label_normalizationLayerNormalizeVariance(""), + label_normalizationLayerEps(""), + text_lossLayerNormalize(), + label_lossLayerNormalize(""), + + button_addMoreLayer4("Add More Layers"), + text_lossLayerTop(), + text_lossLayerBottom1(), + text_lossLayerBottom2(), + text_lossLayerName(), + label_lossLayerTop(""), + label_lossLayerBottom1(""), + label_lossLayerBottom2(""), + label_lossLayerName(""), + button_setLossParameters("Add this Loss Layer"), + label_lossLayerNormalization(""), + + button_addMoreLayer5("Add More Layers"), + title_normalizationLayerType(""), + title_lossLayerType(""), + title_extraLayerType(""), + + button_displayCnnLayers("Display the Network"), + button_editMore("Add more layers"), + box_fullCnnLayerMatter(Gtk::ORIENTATION_VERTICAL), + button_deleteLayerAtEnd("Delete Layer at the end"), + button_saveFile("Save File") +{ + numLayers = 0; + set_title("Network Creator"); + set_border_width(10); + add(m_sw1); + m_grid1.set_column_spacing (10); + m_grid1.set_row_spacing (50); + + //level 0 + + label_networkFileName.set_text("1) Give a proper name to the Network file: "); + label_networkFileName.set_line_wrap(); + label_networkFileName.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_networkFileName,0,0,2,1); + label_networkFileName.show(); + + text_networkFileName.set_max_length(100); + text_networkFileName.set_text("../examples/objectdetector/Mnist_Train/train1.prototxt"); + text_networkFileName.select_region(0, text_networkFileName.get_text_length()); + m_grid1.attach(text_networkFileName,2,0,1,1); + text_networkFileName.show(); + + + button_networkFileName.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "networkFileName")); + m_grid1.attach(button_networkFileName,3,0,1,1); + button_networkFileName.show(); + + //level 1 + + label_activationLayerType.set_text("1) Select activation layer:\n(Append activation layer as next layer) "); + label_activationLayerType.set_line_wrap(); + label_activationLayerType.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_activationLayerType,0,1,2,1); + label_activationLayerType.show(); + + ref_activationLayerType = Gtk::ListStore::create(column_activationLayerType); + combo_activationLayerType.set_model(ref_activationLayerType); + + Gtk::TreeModel::Row row_activationLayerType = *(ref_activationLayerType->append()); + row_activationLayerType[column_activationLayerType.m_col_id] = 1; + row_activationLayerType[column_activationLayerType.m_col_name] = "AbsVal"; + row_activationLayerType[column_activationLayerType.m_col_extra] = "Absolute Value Layer"; + combo_activationLayerType.set_active(row_activationLayerType); + + row_activationLayerType = *(ref_activationLayerType->append()); + row_activationLayerType[column_activationLayerType.m_col_id] = 2; + row_activationLayerType[column_activationLayerType.m_col_name] = "Exp"; + row_activationLayerType[column_activationLayerType.m_col_extra] = "Exponential Layer"; + + + row_activationLayerType = *(ref_activationLayerType->append()); + row_activationLayerType[column_activationLayerType.m_col_id] = 3; + row_activationLayerType[column_activationLayerType.m_col_name] = "Log"; + row_activationLayerType[column_activationLayerType.m_col_extra] = "Log Layer"; + + row_activationLayerType = *(ref_activationLayerType->append()); + row_activationLayerType[column_activationLayerType.m_col_id] = 4; + row_activationLayerType[column_activationLayerType.m_col_name] = "Power"; + row_activationLayerType[column_activationLayerType.m_col_extra] = "Power Layer"; + + row_activationLayerType = *(ref_activationLayerType->append()); + row_activationLayerType[column_activationLayerType.m_col_id] = 5; + row_activationLayerType[column_activationLayerType.m_col_name] = "PReLU"; + row_activationLayerType[column_activationLayerType.m_col_extra] = "PReLU Layer"; + + row_activationLayerType = *(ref_activationLayerType->append()); + row_activationLayerType[column_activationLayerType.m_col_id] = 6; + row_activationLayerType[column_activationLayerType.m_col_name] = "ReLU"; + row_activationLayerType[column_activationLayerType.m_col_extra] = "ReLU Layer"; + + row_activationLayerType = *(ref_activationLayerType->append()); + row_activationLayerType[column_activationLayerType.m_col_id] = 7; + row_activationLayerType[column_activationLayerType.m_col_name] = "Sigmoid"; + row_activationLayerType[column_activationLayerType.m_col_extra] = "Sigmoid Layer"; + + row_activationLayerType = *(ref_activationLayerType->append()); + row_activationLayerType[column_activationLayerType.m_col_id] = 8; + row_activationLayerType[column_activationLayerType.m_col_name] = "TanH"; + row_activationLayerType[column_activationLayerType.m_col_extra] = "Hyperbolic tangent Layer"; + + combo_activationLayerType.pack_start(column_activationLayerType.m_col_id); + combo_activationLayerType.pack_start(column_activationLayerType.m_col_name); + combo_activationLayerType.set_cell_data_func(cell_activationLayerType, sigc::mem_fun(*this, &NetworkCreator::on_cell_data_extra)); + combo_activationLayerType.pack_start(cell_activationLayerType); + + m_grid1.attach(combo_activationLayerType,2,1,2,1); + combo_activationLayerType.signal_changed().connect( sigc::mem_fun(*this, &NetworkCreator::on_combo_changed) ); + + button_activationLayerType.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "activationLayerType")); + m_grid1.attach(button_activationLayerType,4,1,1,1); + button_activationLayerType.show(); + + + // level 2 + + label_criticalLayerType.set_text("2) Select Critical Layer:\n(Append critical layer as next layer) "); + label_criticalLayerType.set_line_wrap(); + label_criticalLayerType.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_criticalLayerType,0,2,2,1); + label_criticalLayerType.show(); + + ref_criticalLayerType = Gtk::ListStore::create(column_criticalLayerType); + combo_criticalLayerType.set_model(ref_criticalLayerType); + + Gtk::TreeModel::Row row_criticalLayerType = *(ref_criticalLayerType->append()); + row_criticalLayerType[column_criticalLayerType.m_col_id] = 1; + row_criticalLayerType[column_criticalLayerType.m_col_name] = "Accuracy"; + row_criticalLayerType[column_criticalLayerType.m_col_extra] = "Accuracy Layer (Validation phase)"; + combo_criticalLayerType.set_active(row_criticalLayerType); + + row_criticalLayerType = *(ref_criticalLayerType->append()); + row_criticalLayerType[column_criticalLayerType.m_col_id] = 2; + row_criticalLayerType[column_criticalLayerType.m_col_name] = "Concat"; + row_criticalLayerType[column_criticalLayerType.m_col_extra] = "Concatenation Layer"; + + row_criticalLayerType = *(ref_criticalLayerType->append()); + row_criticalLayerType[column_criticalLayerType.m_col_id] = 3; + row_criticalLayerType[column_criticalLayerType.m_col_name] = "Convolution"; + row_criticalLayerType[column_criticalLayerType.m_col_extra] = "Convolution Layer"; + + row_criticalLayerType = *(ref_criticalLayerType->append()); + row_criticalLayerType[column_criticalLayerType.m_col_id] = 4; + row_criticalLayerType[column_criticalLayerType.m_col_name] = "Deconvolution"; + row_criticalLayerType[column_criticalLayerType.m_col_extra] = "De-Convolution Layer"; + + row_criticalLayerType = *(ref_criticalLayerType->append()); + row_criticalLayerType[column_criticalLayerType.m_col_id] = 5; + row_criticalLayerType[column_criticalLayerType.m_col_name] = "Dropout"; + row_criticalLayerType[column_criticalLayerType.m_col_extra] = "Dropout Layer"; + + row_criticalLayerType = *(ref_criticalLayerType->append()); + row_criticalLayerType[column_criticalLayerType.m_col_id] = 6; + row_criticalLayerType[column_criticalLayerType.m_col_name] = "InnerProduct"; + row_criticalLayerType[column_criticalLayerType.m_col_extra] = "Inner Product (Fully Connected) Layer"; + + row_criticalLayerType = *(ref_criticalLayerType->append()); + row_criticalLayerType[column_criticalLayerType.m_col_id] = 7; + row_criticalLayerType[column_criticalLayerType.m_col_name] = "Pooling"; + row_criticalLayerType[column_criticalLayerType.m_col_extra] = "Pooling Layer"; + + row_criticalLayerType = *(ref_criticalLayerType->append()); + row_criticalLayerType[column_criticalLayerType.m_col_id] = 8; + row_criticalLayerType[column_criticalLayerType.m_col_name] = "Softmax"; + row_criticalLayerType[column_criticalLayerType.m_col_extra] = "Softmax Classification Layer"; + + combo_criticalLayerType.pack_start(column_criticalLayerType.m_col_id); + combo_criticalLayerType.pack_start(column_criticalLayerType.m_col_name); + combo_criticalLayerType.set_cell_data_func(cell_criticalLayerType, sigc::mem_fun(*this, &NetworkCreator::on_cell_data_extra)); + combo_criticalLayerType.pack_start(cell_criticalLayerType); + + m_grid1.attach(combo_criticalLayerType,2,2,2,1); + combo_criticalLayerType.signal_changed().connect( sigc::mem_fun(*this, &NetworkCreator::on_combo_changed) ); + + button_criticalLayerType.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "criticalLayerType")); + m_grid1.attach(button_criticalLayerType,4,2,1,1); + button_criticalLayerType.show(); + + //level 3 + + label_normalizationLayerType.set_text("3) Select Normalization Layer:\n(Append normalization layer as loss layer) "); + label_normalizationLayerType.set_line_wrap(); + label_normalizationLayerType.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_normalizationLayerType,0,3,2,1); + label_normalizationLayerType.show(); + + ref_normalizationLayerType = Gtk::ListStore::create(column_normalizationLayerType); + combo_normalizationLayerType.set_model(ref_normalizationLayerType); + + Gtk::TreeModel::Row row_normalizationLayerType = *(ref_normalizationLayerType->append()); + row_normalizationLayerType[column_normalizationLayerType.m_col_id] = 1; + row_normalizationLayerType[column_normalizationLayerType.m_col_name] = "BatchNorm"; + row_normalizationLayerType[column_criticalLayerType.m_col_extra] = "Batch Normalization Layer"; + combo_normalizationLayerType.set_active(row_normalizationLayerType); + + row_normalizationLayerType = *(ref_normalizationLayerType->append()); + row_normalizationLayerType[column_normalizationLayerType.m_col_id] = 2; + row_normalizationLayerType[column_normalizationLayerType.m_col_name] = "LRN"; + row_normalizationLayerType[column_normalizationLayerType.m_col_extra] = "Local Response Normalization Layer"; + + row_normalizationLayerType = *(ref_normalizationLayerType->append()); + row_normalizationLayerType[column_normalizationLayerType.m_col_id] = 3; + row_normalizationLayerType[column_normalizationLayerType.m_col_name] = "MVN"; + row_normalizationLayerType[column_normalizationLayerType.m_col_extra] = "Multi Variate Normalization Layer"; + + combo_normalizationLayerType.pack_start(column_normalizationLayerType.m_col_id); + combo_normalizationLayerType.pack_start(column_normalizationLayerType.m_col_name); + combo_normalizationLayerType.set_cell_data_func(cell_normalizationLayerType, sigc::mem_fun(*this, &NetworkCreator::on_cell_data_extra)); + combo_normalizationLayerType.pack_start(cell_normalizationLayerType); + + m_grid1.attach(combo_normalizationLayerType,2,3,2,1); + combo_normalizationLayerType.signal_changed().connect( sigc::mem_fun(*this, &NetworkCreator::on_combo_changed) ); + + button_normalizationLayerType.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "normalizationLayerType")); + m_grid1.attach(button_normalizationLayerType,4,3,1,1); + button_normalizationLayerType.show(); + + //level 4 + + label_lossLayerType.set_text("4) Select Loss Layer:\n(Append loss layer as next layer) "); + label_lossLayerType.set_line_wrap(); + label_lossLayerType.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_lossLayerType,0,4,2,1); + label_lossLayerType.show(); + + ref_lossLayerType = Gtk::ListStore::create(column_lossLayerType); + combo_lossLayerType.set_model(ref_lossLayerType); + + Gtk::TreeModel::Row row_lossLayerType = *(ref_lossLayerType->append()); + row_lossLayerType[column_lossLayerType.m_col_id] = 1; + row_lossLayerType[column_lossLayerType.m_col_name] = "SoftmaxWithLoss"; + row_lossLayerType[column_lossLayerType.m_col_extra] = "Loss Layer with softmax activation"; + combo_lossLayerType.set_active(row_lossLayerType); + + row_lossLayerType = *(ref_lossLayerType->append()); + row_lossLayerType[column_lossLayerType.m_col_id] = 2; + row_lossLayerType[column_lossLayerType.m_col_name] = "HingeLoss"; + row_lossLayerType[column_lossLayerType.m_col_extra] = "Hinge Loss Layer"; + + row_lossLayerType = *(ref_lossLayerType->append()); + row_lossLayerType[column_lossLayerType.m_col_id] = 3; + row_lossLayerType[column_lossLayerType.m_col_name] = "ContrastiveLoss"; + row_lossLayerType[column_lossLayerType.m_col_extra] = "Contrastive Loss Layer"; + + row_lossLayerType = *(ref_lossLayerType->append()); + row_lossLayerType[column_lossLayerType.m_col_id] = 4; + row_lossLayerType[column_lossLayerType.m_col_name] = "EuclideanLoss"; + row_lossLayerType[column_lossLayerType.m_col_extra] = "Euclidean Loss Layer"; + + row_lossLayerType = *(ref_lossLayerType->append()); + row_lossLayerType[column_lossLayerType.m_col_id] = 5; + row_lossLayerType[column_lossLayerType.m_col_name] = "InfogainLoss"; + row_lossLayerType[column_lossLayerType.m_col_extra] = "Infogain Loss Layer"; + + row_lossLayerType = *(ref_lossLayerType->append()); + row_lossLayerType[column_lossLayerType.m_col_id] = 6; + row_lossLayerType[column_lossLayerType.m_col_name] = "MultinomialLogisticLoss"; + row_lossLayerType[column_lossLayerType.m_col_extra] = "Multinomial Logistic Loss Layer"; + + row_lossLayerType = *(ref_lossLayerType->append()); + row_lossLayerType[column_lossLayerType.m_col_id] = 7; + row_lossLayerType[column_lossLayerType.m_col_name] = "EuclideanLoss"; + row_lossLayerType[column_lossLayerType.m_col_extra] = "Euclidean Loss Layer"; + + row_lossLayerType = *(ref_lossLayerType->append()); + row_lossLayerType[column_lossLayerType.m_col_id] = 8; + row_lossLayerType[column_lossLayerType.m_col_name] = "SigmoidCrossEntropyLoss"; + row_lossLayerType[column_lossLayerType.m_col_extra] = "Sigmoid Cross Entropy Loss Layer"; + + combo_lossLayerType.pack_start(column_lossLayerType.m_col_id); + combo_lossLayerType.pack_start(column_lossLayerType.m_col_name); + combo_lossLayerType.set_cell_data_func(cell_lossLayerType, sigc::mem_fun(*this, &NetworkCreator::on_cell_data_extra)); + combo_lossLayerType.pack_start(cell_lossLayerType); + + m_grid1.attach(combo_lossLayerType,2,4,2,1); + combo_lossLayerType.signal_changed().connect( sigc::mem_fun(*this, &NetworkCreator::on_combo_changed) ); + + button_lossLayerType.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "lossLayerType")); + m_grid1.attach(button_lossLayerType,4,4,1,1); + button_lossLayerType.show(); + + + //level 5 + + label_extraLayerType.set_text("6) A few extra layers:\n(Append these layer as next layer) "); + label_extraLayerType.set_line_wrap(); + label_extraLayerType.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_extraLayerType,0,5,2,1); + label_extraLayerType.show(); + + ref_extraLayerType = Gtk::ListStore::create(column_extraLayerType); + combo_extraLayerType.set_model(ref_extraLayerType); + + Gtk::TreeModel::Row row_extraLayerType = *(ref_extraLayerType->append()); + row_extraLayerType[column_extraLayerType.m_col_id] = 1; + row_extraLayerType[column_extraLayerType.m_col_name] = "ArgMax"; + row_extraLayerType[column_extraLayerType.m_col_extra] = "Maximum Argument Layer"; + combo_extraLayerType.set_active(row_extraLayerType); + + row_extraLayerType = *(ref_extraLayerType->append()); + row_extraLayerType[column_extraLayerType.m_col_id] = 2; + row_extraLayerType[column_extraLayerType.m_col_name] = "BNLL"; + row_extraLayerType[column_extraLayerType.m_col_extra] = "Binomial Normal Log Likelihood Layer"; + + row_extraLayerType = *(ref_extraLayerType->append()); + row_extraLayerType[column_extraLayerType.m_col_id] = 3; + row_extraLayerType[column_extraLayerType.m_col_name] = "Eltwise"; + row_extraLayerType[column_extraLayerType.m_col_extra] = "Element Wise Operation Layer"; + + row_extraLayerType = *(ref_extraLayerType->append()); + row_extraLayerType[column_extraLayerType.m_col_id] = 4; + row_extraLayerType[column_extraLayerType.m_col_name] = "Eltwise"; + row_extraLayerType[column_extraLayerType.m_col_extra] = "Element Wise Operation Layer"; + + combo_extraLayerType.pack_start(column_extraLayerType.m_col_id); + combo_extraLayerType.pack_start(column_extraLayerType.m_col_name); + combo_extraLayerType.set_cell_data_func(cell_extraLayerType, sigc::mem_fun(*this, &NetworkCreator::on_cell_data_extra)); + combo_extraLayerType.pack_start(cell_extraLayerType); + + m_grid1.attach(combo_extraLayerType,2,5,2,1); + combo_extraLayerType.signal_changed().connect( sigc::mem_fun(*this, &NetworkCreator::on_combo_changed) ); + + button_extraLayerType.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "extraLayerType")); + m_grid1.attach(button_extraLayerType,4,5,1,1); + button_extraLayerType.show(); + + button_displayCnnLayers.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "displayCnnLayers")); + m_grid1.attach(button_displayCnnLayers,0,6,2,1); + button_displayCnnLayers.show(); + + button_saveFile.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "saveFile")); + m_grid1.attach(button_saveFile,0,7,2,1); + button_saveFile.show(); + + + + m_sw1.add(m_grid1); + m_sw1.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); +// m_grid1.show(); + show_all_children(); + m_sw1.show(); + + + //Activation Window + button_setActivationParameters.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "setActivationParameters")); + m_grid_activationLayerType.attach(button_setActivationParameters,0,7,2,1); + + button_addMoreLayer.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "addMoreLayer")); + m_grid_activationLayerType.attach(button_addMoreLayer,2,7,1,1); + + label_activationLayerBottom.set_text("Bottom Layer Name: "); + label_activationLayerBottom.set_line_wrap(); + label_activationLayerBottom.set_justify(Gtk::JUSTIFY_FILL); + m_grid_activationLayerType.attach(label_activationLayerBottom,0,1,2,1); + label_activationLayerBottom.show(); + + text_activationLayerBottom.set_max_length(100); + text_activationLayerBottom.set_text(""); + text_activationLayerBottom.select_region(0, text_activationLayerBottom.get_text_length()); + m_grid_activationLayerType.attach(text_activationLayerBottom,2,1,1,1); + text_activationLayerBottom.show(); + + label_activationLayerTop.set_text("Top Layer Name: "); + label_activationLayerTop.set_line_wrap(); + label_activationLayerTop.set_justify(Gtk::JUSTIFY_FILL); + m_grid_activationLayerType.attach(label_activationLayerTop,0,2,2,1); + label_activationLayerTop.show(); + + text_activationLayerTop.set_max_length(100); + text_activationLayerTop.set_text(""); + text_activationLayerTop.select_region(0, text_activationLayerTop.get_text_length()); + m_grid_activationLayerType.attach(text_activationLayerTop,2,2,1,1); + text_activationLayerTop.show(); + + label_activationLayerName.set_text("Current Layer Name: "); + label_activationLayerName.set_line_wrap(); + label_activationLayerName.set_justify(Gtk::JUSTIFY_FILL); + m_grid_activationLayerType.attach(label_activationLayerName,0,3,2,1); + label_activationLayerName.show(); + + text_activationLayerName.set_max_length(100); + text_activationLayerName.set_text(""); + text_activationLayerName.select_region(0, text_activationLayerName.get_text_length()); + m_grid_activationLayerType.attach(text_activationLayerName,2,3,1,1); + text_activationLayerName.show(); + + label_activationLayerScale.set_text("Layer Parameter Scale: "); + label_activationLayerScale.set_line_wrap(); + label_activationLayerScale.set_justify(Gtk::JUSTIFY_FILL); + m_grid_activationLayerType.attach(label_activationLayerScale,0,4,2,1); + label_activationLayerScale.show(); + + text_activationLayerScale.set_max_length(100); + text_activationLayerScale.set_text("1"); + text_activationLayerScale.select_region(0, text_activationLayerScale.get_text_length()); + m_grid_activationLayerType.attach(text_activationLayerScale,2,4,1,1); + text_activationLayerScale.show(); + + label_activationLayerShift.set_text("Layer Parameter Shift: "); + label_activationLayerShift.set_line_wrap(); + label_activationLayerShift.set_justify(Gtk::JUSTIFY_FILL); + m_grid_activationLayerType.attach(label_activationLayerShift,0,5,2,1); + label_activationLayerShift.show(); + + text_activationLayerShift.set_max_length(100); + text_activationLayerShift.set_text("0"); + text_activationLayerShift.select_region(0, text_activationLayerShift.get_text_length()); + m_grid_activationLayerType.attach(text_activationLayerShift,2,5,1,1); + text_activationLayerShift.show(); + + label_activationLayerBase.set_text("Layer Parameter Base: "); + label_activationLayerBase.set_line_wrap(); + label_activationLayerBase.set_justify(Gtk::JUSTIFY_FILL); + m_grid_activationLayerType.attach(label_activationLayerBase,0,6,2,1); + label_activationLayerBase.show(); + + text_activationLayerBase.set_max_length(100); + text_activationLayerBase.set_text("-1"); + text_activationLayerBase.select_region(0, text_activationLayerBase.get_text_length()); + m_grid_activationLayerType.attach(text_activationLayerBase,2,6,1,1); + text_activationLayerBase.show(); + + label_activationLayerNegativeSlope.set_text("Relu Param Negative Slope: "); + label_activationLayerNegativeSlope.set_line_wrap(); + label_activationLayerNegativeSlope.set_justify(Gtk::JUSTIFY_FILL); + m_grid_activationLayerType.attach(label_activationLayerNegativeSlope,0,4,2,1); + label_activationLayerNegativeSlope.show(); + + text_activationLayerNegativeSlope.set_max_length(100); + text_activationLayerNegativeSlope.set_text("0"); + text_activationLayerNegativeSlope.select_region(0, text_activationLayerNegativeSlope.get_text_length()); + m_grid_activationLayerType.attach(text_activationLayerNegativeSlope,2,4,1,1); + text_activationLayerNegativeSlope.show(); + + title_activationLayerType.set_text("Set the Properties of Activation Layer type: "); + title_activationLayerType.set_line_wrap(); + title_activationLayerType.set_justify(Gtk::JUSTIFY_FILL); + m_grid_activationLayerType.attach(title_activationLayerType,0,0,2,1); + title_activationLayerType.show(); + + m_sw_activationLayerType.add(m_grid_activationLayerType); + + + //Critical Layer Window + title_criticalLayerType.set_text("Set the Properties of Critical Layer type: "); + title_criticalLayerType.set_line_wrap(); + title_criticalLayerType.set_justify(Gtk::JUSTIFY_FILL); + m_grid_criticalLayerType.attach(title_criticalLayerType,0,0,2,1); + title_criticalLayerType.show(); + + button_setCriticalParameters.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "setCriticalParameters")); + m_grid_criticalLayerType.attach(button_setCriticalParameters,0,25,2,1); + button_addMoreLayer2.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "addMoreLayer2")); + m_grid_criticalLayerType.attach(button_addMoreLayer2,2,25,1,1); + + label_criticalLayerBottom1.set_text("Bottom1 Layer Name: "); + label_criticalLayerBottom1.set_line_wrap(); + label_criticalLayerBottom1.set_justify(Gtk::JUSTIFY_FILL); + m_grid_criticalLayerType.attach(label_criticalLayerBottom1,0,1,2,1); + label_criticalLayerBottom1.show(); + + text_criticalLayerBottom1.set_max_length(100); + text_criticalLayerBottom1.set_text(""); + text_criticalLayerBottom1.select_region(0, text_criticalLayerBottom1.get_text_length()); + m_grid_criticalLayerType.attach(text_criticalLayerBottom1,2,1,1,1); + text_criticalLayerBottom1.show(); + + label_criticalLayerBottom2.set_text("Bottom2 Layer Name: "); + label_criticalLayerBottom2.set_line_wrap(); + label_criticalLayerBottom2.set_justify(Gtk::JUSTIFY_FILL); + m_grid_criticalLayerType.attach(label_criticalLayerBottom2,0,2,2,1); + label_criticalLayerBottom2.show(); + + text_criticalLayerBottom2.set_max_length(100); + text_criticalLayerBottom2.set_text(""); + text_criticalLayerBottom2.select_region(0, text_criticalLayerBottom2.get_text_length()); + m_grid_criticalLayerType.attach(text_criticalLayerBottom2,2,2,1,1); + text_criticalLayerBottom2.show(); + + label_criticalLayerTop.set_text("Top Layer Name: "); + label_criticalLayerTop.set_line_wrap(); + label_criticalLayerTop.set_justify(Gtk::JUSTIFY_FILL); + m_grid_criticalLayerType.attach(label_criticalLayerTop,0,3,2,1); + label_criticalLayerTop.show(); + + text_criticalLayerTop.set_max_length(100); + text_criticalLayerTop.set_text(""); + text_criticalLayerTop.select_region(0, text_criticalLayerTop.get_text_length()); + m_grid_criticalLayerType.attach(text_criticalLayerTop,2,3,1,1); + text_criticalLayerTop.show(); + + label_criticalLayerName.set_text("Current Layer Name: "); + label_criticalLayerName.set_line_wrap(); + label_criticalLayerName.set_justify(Gtk::JUSTIFY_FILL); + m_grid_criticalLayerType.attach(label_criticalLayerName,0,4,2,1); + label_criticalLayerName.show(); + + text_criticalLayerName.set_max_length(100); + text_criticalLayerName.set_text(""); + text_criticalLayerName.select_region(0, text_criticalLayerName.get_text_length()); + m_grid_criticalLayerType.attach(text_criticalLayerName,2,4,1,1); + text_criticalLayerName.show(); + + label_criticalLayerFilterLr.set_text("Set filter lr_mult:\n(Leave unchanged if not needed) "); + label_criticalLayerFilterLr.set_line_wrap(); + label_criticalLayerFilterLr.set_justify(Gtk::JUSTIFY_FILL); + m_grid_criticalLayerType.attach(label_criticalLayerFilterLr,0,5,2,1); + label_criticalLayerFilterLr.show(); + + text_criticalLayerFilterLr.set_max_length(100); + text_criticalLayerFilterLr.set_text("1"); + text_criticalLayerFilterLr.select_region(0, text_criticalLayerFilterLr.get_text_length()); + m_grid_criticalLayerType.attach(text_criticalLayerFilterLr,2,5,1,1); + text_criticalLayerFilterLr.show(); + + label_criticalLayerFilterDm.set_text("Set filter decay_mult:\n(Leave unchanged if not needed) "); + label_criticalLayerFilterDm.set_line_wrap(); + label_criticalLayerFilterDm.set_justify(Gtk::JUSTIFY_FILL); + m_grid_criticalLayerType.attach(label_criticalLayerFilterDm,0,6,2,1); + label_criticalLayerFilterDm.show(); + + text_criticalLayerFilterDm.set_max_length(100); + text_criticalLayerFilterDm.set_text("1"); + text_criticalLayerFilterDm.select_region(0, text_criticalLayerFilterDm.get_text_length()); + m_grid_criticalLayerType.attach(text_criticalLayerFilterDm,2,6,1,1); + text_criticalLayerFilterDm.show(); + + label_criticalLayerBiasLr.set_text("Set bias lr_mult:\n(Leave unchanged if not needed) "); + label_criticalLayerBiasLr.set_line_wrap(); + label_criticalLayerBiasLr.set_justify(Gtk::JUSTIFY_FILL); + m_grid_criticalLayerType.attach(label_criticalLayerBiasLr,0,7,2,1); + label_criticalLayerBiasLr.show(); + + text_criticalLayerBiasLr.set_max_length(100); + text_criticalLayerBiasLr.set_text("2"); + text_criticalLayerBiasLr.select_region(0, text_criticalLayerBiasLr.get_text_length()); + m_grid_criticalLayerType.attach(text_criticalLayerBiasLr,2,7,1,1); + text_criticalLayerBiasLr.show(); + + label_criticalLayerBiasDm.set_text("Set bias decay_mult:\n(Leave unchanged if not needed) "); + label_criticalLayerBiasDm.set_line_wrap(); + label_criticalLayerBiasDm.set_justify(Gtk::JUSTIFY_FILL); + m_grid_criticalLayerType.attach(label_criticalLayerBiasDm,0,8,2,1); + label_criticalLayerBiasDm.show(); + + text_criticalLayerBiasDm.set_max_length(100); + text_criticalLayerBiasDm.set_text("0"); + text_criticalLayerBiasDm.select_region(0, text_criticalLayerBiasDm.get_text_length()); + m_grid_criticalLayerType.attach(text_criticalLayerBiasDm,2,8,1,1); + text_criticalLayerBiasDm.show(); + + label_criticalLayerNumOutput.set_text("Set param num_output: "); + label_criticalLayerNumOutput.set_line_wrap(); + label_criticalLayerNumOutput.set_justify(Gtk::JUSTIFY_FILL); + m_grid_criticalLayerType.attach(label_criticalLayerNumOutput,0,9,2,1); + label_criticalLayerNumOutput.show(); + + text_criticalLayerNumOutput.set_max_length(100); + text_criticalLayerNumOutput.set_text("64"); + text_criticalLayerNumOutput.select_region(0, text_criticalLayerNumOutput.get_text_length()); + m_grid_criticalLayerType.attach(text_criticalLayerNumOutput,2,9,1,1); + text_criticalLayerNumOutput.show(); + + label_criticalLayerKernelW.set_text("Set param kernel_w: "); + label_criticalLayerKernelW.set_line_wrap(); + label_criticalLayerKernelW.set_justify(Gtk::JUSTIFY_FILL); + m_grid_criticalLayerType.attach(label_criticalLayerKernelW,0,10,2,1); + label_criticalLayerKernelW.show(); + + text_criticalLayerKernelW.set_max_length(100); + text_criticalLayerKernelW.set_text("3"); + text_criticalLayerKernelW.select_region(0, text_criticalLayerKernelW.get_text_length()); + m_grid_criticalLayerType.attach(text_criticalLayerKernelW,2,10,1,1); + text_criticalLayerKernelW.show(); + + label_criticalLayerKernelH.set_text("Set param kernel_h: "); + label_criticalLayerKernelH.set_line_wrap(); + label_criticalLayerKernelH.set_justify(Gtk::JUSTIFY_FILL); + m_grid_criticalLayerType.attach(label_criticalLayerKernelH,0,11,2,1); + label_criticalLayerKernelH.show(); + + text_criticalLayerKernelH.set_max_length(100); + text_criticalLayerKernelH.set_text("3"); + text_criticalLayerKernelH.select_region(0, text_criticalLayerKernelH.get_text_length()); + m_grid_criticalLayerType.attach(text_criticalLayerKernelH,2,11,1,1); + text_criticalLayerKernelH.show(); + + label_criticalLayerStrideW.set_text("Set param stride_w: "); + label_criticalLayerStrideW.set_line_wrap(); + label_criticalLayerStrideW.set_justify(Gtk::JUSTIFY_FILL); + m_grid_criticalLayerType.attach(label_criticalLayerStrideW,0,12,2,1); + label_criticalLayerStrideW.show(); + + text_criticalLayerStrideW.set_max_length(100); + text_criticalLayerStrideW.set_text("1"); + text_criticalLayerStrideW.select_region(0, text_criticalLayerStrideW.get_text_length()); + m_grid_criticalLayerType.attach(text_criticalLayerStrideW,2,12,1,1); + text_criticalLayerStrideW.show(); + + label_criticalLayerStrideH.set_text("Set param stride_h: "); + label_criticalLayerStrideH.set_line_wrap(); + label_criticalLayerStrideH.set_justify(Gtk::JUSTIFY_FILL); + m_grid_criticalLayerType.attach(label_criticalLayerStrideH,0,13,2,1); + label_criticalLayerStrideH.show(); + + text_criticalLayerStrideH.set_max_length(100); + text_criticalLayerStrideH.set_text("1"); + text_criticalLayerStrideH.select_region(0, text_criticalLayerStrideH.get_text_length()); + m_grid_criticalLayerType.attach(text_criticalLayerStrideH,2,13,1,1); + text_criticalLayerStrideH.show(); + + label_criticalLayerPadW.set_text("Set param pad_w: "); + label_criticalLayerPadW.set_line_wrap(); + label_criticalLayerPadW.set_justify(Gtk::JUSTIFY_FILL); + m_grid_criticalLayerType.attach(label_criticalLayerPadW,0,14,2,1); + label_criticalLayerPadW.show(); + + text_criticalLayerPadW.set_max_length(100); + text_criticalLayerPadW.set_text("1"); + text_criticalLayerPadW.select_region(0, text_criticalLayerPadW.get_text_length()); + m_grid_criticalLayerType.attach(text_criticalLayerPadW,2,14,1,1); + text_criticalLayerPadW.show(); + + label_criticalLayerPadH.set_text("Set param pad_h: "); + label_criticalLayerPadH.set_line_wrap(); + label_criticalLayerPadH.set_justify(Gtk::JUSTIFY_FILL); + m_grid_criticalLayerType.attach(label_criticalLayerPadH,0,15,2,1); + label_criticalLayerPadH.show(); + + text_criticalLayerPadH.set_max_length(100); + text_criticalLayerPadH.set_text("1"); + text_criticalLayerPadH.select_region(0, text_criticalLayerPadH.get_text_length()); + m_grid_criticalLayerType.attach(text_criticalLayerPadH,2,15,1,1); + text_criticalLayerPadH.show(); + + label_criticalLayerWeightFiller.set_text("Set Weight Filler : "); + label_criticalLayerWeightFiller.set_line_wrap(); + label_criticalLayerWeightFiller.set_justify(Gtk::JUSTIFY_FILL); + m_grid_criticalLayerType.attach(label_criticalLayerWeightFiller,0,16,2,1); + label_criticalLayerWeightFiller.show(); + + Gtk::RadioButton::Group group1 = rbutton_criticalLayerWeightFillerConstant.get_group(); + rbutton_criticalLayerWeightFillerUniform.set_group(group1); + rbutton_criticalLayerWeightGaussian.set_group(group1); + rbutton_criticalLayerWeightFillerPositiveUnitBall.set_group(group1); + rbutton_criticalLayerWeightFillerXavier.set_group(group1); + rbutton_criticalLayerWeightFillerMSRA.set_group(group1); + rbutton_criticalLayerWeightFillerBilinear.set_group(group1); + rbutton_criticalLayerWeightFillerConstant.set_active(); + m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerConstant,2,16,1,1); + rbutton_criticalLayerWeightFillerConstant.show(); + m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerUniform,2,17,1,1); + rbutton_criticalLayerWeightFillerUniform.show(); + m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightGaussian,2,18,1,1); + rbutton_criticalLayerWeightGaussian.show(); + m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerPositiveUnitBall,2,19,1,1); + rbutton_criticalLayerWeightFillerPositiveUnitBall.show(); + m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavier,2,20,1,1); + rbutton_criticalLayerWeightFillerXavier.show(); + m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerMSRA,2,21,1,1); + rbutton_criticalLayerWeightFillerMSRA.show(); + m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerBilinear,2,22,1,1); + rbutton_criticalLayerWeightFillerBilinear.show(); + + label_criticalLayerWeightFillerConstantValue.set_text("value: "); + label_criticalLayerWeightFillerConstantValue.set_line_wrap(); + label_criticalLayerWeightFillerConstantValue.set_justify(Gtk::JUSTIFY_FILL); + m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerConstantValue,4,16,1,1); + label_criticalLayerWeightFillerConstantValue.show(); + + text_criticalLayerWeightFillerConstantValue.set_max_length(100); + text_criticalLayerWeightFillerConstantValue.set_text("0.5"); + text_criticalLayerWeightFillerConstantValue.select_region(0, text_criticalLayerWeightFillerConstantValue.get_text_length()); + m_grid_criticalLayerType.attach(text_criticalLayerWeightFillerConstantValue,5,16,1,1); + text_criticalLayerWeightFillerConstantValue.show(); + + label_criticalLayerWeightFillerUniformMin.set_text("min: "); + label_criticalLayerWeightFillerUniformMin.set_line_wrap(); + label_criticalLayerWeightFillerUniformMin.set_justify(Gtk::JUSTIFY_FILL); + m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerUniformMin,4,17,1,1); + label_criticalLayerWeightFillerUniformMin.show(); + + text_criticalLayerWeightFillerUniformMin.set_max_length(100); + text_criticalLayerWeightFillerUniformMin.set_text("0"); + text_criticalLayerWeightFillerUniformMin.select_region(0, text_criticalLayerWeightFillerUniformMin.get_text_length()); + m_grid_criticalLayerType.attach(text_criticalLayerWeightFillerUniformMin,5,17,1,1); + text_criticalLayerWeightFillerUniformMin.show(); + + label_criticalLayerWeightFillerUniformMax.set_text("max: "); + label_criticalLayerWeightFillerUniformMax.set_line_wrap(); + label_criticalLayerWeightFillerUniformMax.set_justify(Gtk::JUSTIFY_FILL); + m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerUniformMax,6,17,1,1); + label_criticalLayerWeightFillerUniformMax.show(); + + text_criticalLayerWeightFillerUniformMax.set_max_length(100); + text_criticalLayerWeightFillerUniformMax.set_text("1"); + text_criticalLayerWeightFillerUniformMax.select_region(0, text_criticalLayerWeightFillerUniformMax.get_text_length()); + m_grid_criticalLayerType.attach(text_criticalLayerWeightFillerUniformMax,7,17,1,1); + text_criticalLayerWeightFillerUniformMax.show(); + + label_criticalLayerWeightFillerGaussianMean.set_text("mean: "); + label_criticalLayerWeightFillerGaussianMean.set_line_wrap(); + label_criticalLayerWeightFillerGaussianMean.set_justify(Gtk::JUSTIFY_FILL); + m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerGaussianMean,4,18,1,1); + label_criticalLayerWeightFillerGaussianMean.show(); + + text_criticalLayerWeightFillerGaussianMean.set_max_length(100); + text_criticalLayerWeightFillerGaussianMean.set_text("0"); + text_criticalLayerWeightFillerGaussianMean.select_region(0, text_criticalLayerWeightFillerGaussianMean.get_text_length()); + m_grid_criticalLayerType.attach(text_criticalLayerWeightFillerGaussianMean,5,18,1,1); + text_criticalLayerWeightFillerGaussianMean.show(); + + label_criticalLayerWeightFillerGaussianStd.set_text("std: "); + label_criticalLayerWeightFillerGaussianStd.set_line_wrap(); + label_criticalLayerWeightFillerGaussianStd.set_justify(Gtk::JUSTIFY_FILL); + m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerGaussianStd,6,18,1,1); + label_criticalLayerWeightFillerGaussianStd.show(); + + text_criticalLayerWeightFillerGaussianStd.set_max_length(100); + text_criticalLayerWeightFillerGaussianStd.set_text("0.1"); + text_criticalLayerWeightFillerGaussianStd.select_region(0, text_criticalLayerWeightFillerGaussianStd.get_text_length()); + m_grid_criticalLayerType.attach(text_criticalLayerWeightFillerGaussianStd,7,18,1,1); + text_criticalLayerWeightFillerGaussianStd.show(); + + label_criticalLayerWeightFillerXavierVariance.set_text("variance_norm: "); + label_criticalLayerWeightFillerXavierVariance.set_line_wrap(); + label_criticalLayerWeightFillerXavierVariance.set_justify(Gtk::JUSTIFY_FILL); + m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerXavierVariance,4,20,1,1); + label_criticalLayerWeightFillerXavierVariance.show(); + + Gtk::RadioButton::Group group2 = rbutton_criticalLayerWeightFillerXavierIn.get_group(); + rbutton_criticalLayerWeightFillerXavierOut.set_group(group2); + rbutton_criticalLayerWeightFillerXavierAvg.set_group(group2); + rbutton_criticalLayerWeightFillerXavierIn.set_active(); + m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavierIn,5,20,1,1); + rbutton_criticalLayerWeightFillerXavierIn.show(); + m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavierOut,6,20,1,1); + rbutton_criticalLayerWeightFillerXavierOut.show(); + m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavierAvg,7,20,1,1); + rbutton_criticalLayerWeightFillerXavierAvg.show(); + + label_criticalLayerWeightFillerMSRAVariance.set_text("variance_norm: "); + label_criticalLayerWeightFillerMSRAVariance.set_line_wrap(); + label_criticalLayerWeightFillerMSRAVariance.set_justify(Gtk::JUSTIFY_FILL); + m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerMSRAVariance,4,21,1,1); + label_criticalLayerWeightFillerMSRAVariance.show(); + + Gtk::RadioButton::Group group3 = rbutton_criticalLayerWeightFillerMSRAIn.get_group(); + rbutton_criticalLayerWeightFillerMSRAOut.set_group(group3); + rbutton_criticalLayerWeightFillerMSRAAvg.set_group(group3); + rbutton_criticalLayerWeightFillerMSRAIn.set_active(); + m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerMSRAIn,5,21,1,1); + rbutton_criticalLayerWeightFillerMSRAIn.show(); + m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerMSRAOut,6,21,1,1); + rbutton_criticalLayerWeightFillerMSRAOut.show(); + m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerMSRAAvg,7,21,1,1); + rbutton_criticalLayerWeightFillerMSRAAvg.show(); + + label_criticalLayerDropoutRatio.set_text("Set Dropout Ratio: "); + label_criticalLayerDropoutRatio.set_line_wrap(); + label_criticalLayerDropoutRatio.set_justify(Gtk::JUSTIFY_FILL); + m_grid_criticalLayerType.attach(label_criticalLayerDropoutRatio,0,22,2,1); + label_criticalLayerDropoutRatio.show(); + + text_criticalLayerDropoutRatio.set_max_length(100); + text_criticalLayerDropoutRatio.set_text("0.5"); + text_criticalLayerDropoutRatio.select_region(0, text_criticalLayerDropoutRatio.get_text_length()); + m_grid_criticalLayerType.attach(text_criticalLayerDropoutRatio,2,22,1,1); + text_criticalLayerDropoutRatio.show(); + + label_criticalLayerPool.set_text("Set Pool Type: "); + label_criticalLayerPool.set_line_wrap(); + label_criticalLayerPool.set_justify(Gtk::JUSTIFY_FILL); + m_grid_criticalLayerType.attach(label_criticalLayerPool,0,23,2,1); + label_criticalLayerPool.show(); + + Gtk::RadioButton::Group group4 = rbutton_criticalLayerPoolMax.get_group(); + rbutton_criticalLayerPoolAve.set_group(group4); + rbutton_criticalLayerPoolMax.set_active(); + m_grid_criticalLayerType.attach(rbutton_criticalLayerPoolMax,2,23,1,1); + rbutton_criticalLayerPoolMax.show(); + m_grid_criticalLayerType.attach(rbutton_criticalLayerPoolAve,3,23,1,1); + rbutton_criticalLayerPoolAve.show(); + + label_criticalLayerBias.set_text("Set bias value: "); + label_criticalLayerBias.set_line_wrap(); + label_criticalLayerBias.set_justify(Gtk::JUSTIFY_FILL); + m_grid_criticalLayerType.attach(label_criticalLayerBias,0,24,2,1); + label_criticalLayerBias.show(); + + text_criticalLayerBias.set_max_length(100); + text_criticalLayerBias.set_text("0.1"); + text_criticalLayerBias.select_region(0, text_criticalLayerBias.get_text_length()); + m_grid_criticalLayerType.attach(text_criticalLayerBias,2,24,1,1); + text_criticalLayerBias.show(); + + m_sw_criticalLayerType.add(m_grid_criticalLayerType); + + + //Normalization Layer Window + title_normalizationLayerType.set_text("Will be updated soon"); + title_normalizationLayerType.set_line_wrap(); + title_normalizationLayerType.set_justify(Gtk::JUSTIFY_FILL); + m_grid_normalizationLayerType.attach(title_normalizationLayerType,0,0,2,1); + title_normalizationLayerType.show(); + + button_setNormalizationParameters.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "setNormalizationParameters")); + m_grid_normalizationLayerType.attach(button_setNormalizationParameters,0,25,2,1); + + button_addMoreLayer3.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "addMoreLayer3")); + m_grid_normalizationLayerType.attach(button_addMoreLayer3,2,25,2,1); + + label_normalizationLayerBottom.set_text("Bottom Layer Name: "); + label_normalizationLayerBottom.set_line_wrap(); + label_normalizationLayerBottom.set_justify(Gtk::JUSTIFY_FILL); + m_grid_normalizationLayerType.attach(label_normalizationLayerBottom,0,1,2,1); + label_normalizationLayerBottom.show(); + + text_normalizationLayerBottom.set_max_length(100); + text_normalizationLayerBottom.set_text(""); + text_normalizationLayerBottom.select_region(0, text_normalizationLayerBottom.get_text_length()); + m_grid_normalizationLayerType.attach(text_normalizationLayerBottom,2,1,1,1); + text_normalizationLayerBottom.show(); + + label_normalizationLayerTop.set_text("Top Layer Name: "); + label_normalizationLayerTop.set_line_wrap(); + label_normalizationLayerTop.set_justify(Gtk::JUSTIFY_FILL); + m_grid_normalizationLayerType.attach(label_normalizationLayerTop,0,3,2,1); + label_normalizationLayerTop.show(); + + text_normalizationLayerTop.set_max_length(100); + text_normalizationLayerTop.set_text(""); + text_normalizationLayerTop.select_region(0, text_normalizationLayerTop.get_text_length()); + m_grid_normalizationLayerType.attach(text_normalizationLayerTop,2,3,1,1); + text_normalizationLayerTop.show(); + + label_normalizationLayerName.set_text("Current Layer Name: "); + label_normalizationLayerName.set_line_wrap(); + label_normalizationLayerName.set_justify(Gtk::JUSTIFY_FILL); + m_grid_normalizationLayerType.attach(label_normalizationLayerName,0,4,2,1); + label_normalizationLayerName.show(); + + text_normalizationLayerName.set_max_length(100); + text_normalizationLayerName.set_text(""); + text_normalizationLayerName.select_region(0, text_normalizationLayerName.get_text_length()); + m_grid_normalizationLayerType.attach(text_normalizationLayerName,2,4,1,1); + text_normalizationLayerName.show(); + + label_normalizationLayerlocalSize.set_text("Inner Parameter - local_size: "); + label_normalizationLayerlocalSize.set_line_wrap(); + label_normalizationLayerlocalSize.set_justify(Gtk::JUSTIFY_FILL); + m_grid_normalizationLayerType.attach(label_normalizationLayerlocalSize,0,5,2,1); + label_normalizationLayerlocalSize.show(); + + text_normalizationLayerlocalSize.set_max_length(100); + text_normalizationLayerlocalSize.set_text("5"); + text_normalizationLayerlocalSize.select_region(0, text_normalizationLayerlocalSize.get_text_length()); + m_grid_normalizationLayerType.attach(text_normalizationLayerlocalSize,2,5,1,1); + text_normalizationLayerlocalSize.show(); + + label_normalizationLayerAlpha.set_text("Inner Parameter - alpha: "); + label_normalizationLayerAlpha.set_line_wrap(); + label_normalizationLayerAlpha.set_justify(Gtk::JUSTIFY_FILL); + m_grid_normalizationLayerType.attach(label_normalizationLayerAlpha,0,6,2,1); + label_normalizationLayerAlpha.show(); + + text_normalizationLayerAlpha.set_max_length(100); + text_normalizationLayerAlpha.set_text("0.0001"); + text_normalizationLayerAlpha.select_region(0, text_normalizationLayerAlpha.get_text_length()); + m_grid_normalizationLayerType.attach(text_normalizationLayerAlpha,2,6,1,1); + text_normalizationLayerAlpha.show(); + + label_normalizationLayerBeta.set_text("Inner Parameter - beta: "); + label_normalizationLayerBeta.set_line_wrap(); + label_normalizationLayerBeta.set_justify(Gtk::JUSTIFY_FILL); + m_grid_normalizationLayerType.attach(label_normalizationLayerBeta,0,7,2,1); + label_normalizationLayerBeta.show(); + + text_normalizationLayerBeta.set_max_length(100); + text_normalizationLayerBeta.set_text("0.0001"); + text_normalizationLayerBeta.select_region(0, text_normalizationLayerBeta.get_text_length()); + m_grid_normalizationLayerType.attach(text_normalizationLayerBeta,2,7,1,1); + text_normalizationLayerBeta.show(); + + label_normalizationLayerK.set_text("Inner Parameter - k: "); + label_normalizationLayerK.set_line_wrap(); + label_normalizationLayerK.set_justify(Gtk::JUSTIFY_FILL); + m_grid_normalizationLayerType.attach(label_normalizationLayerK,0,8,2,1); + label_normalizationLayerK.show(); + + text_normalizationLayerK.set_max_length(100); + text_normalizationLayerK.set_text("1"); + text_normalizationLayerK.select_region(0, text_normalizationLayerK.get_text_length()); + m_grid_normalizationLayerType.attach(text_normalizationLayerK,2,8,1,1); + text_normalizationLayerK.show(); + + label_normalizationLayerNormRegion.set_text("Inner Parameter - norm_region: "); + label_normalizationLayerNormRegion.set_line_wrap(); + label_normalizationLayerNormRegion.set_justify(Gtk::JUSTIFY_FILL); + m_grid_normalizationLayerType.attach(label_normalizationLayerNormRegion,0,9,2,1); + label_normalizationLayerNormRegion.show(); + + Gtk::RadioButton::Group group5 = rbutton_normalizationLayerLRNWithin.get_group(); + rbutton_normalizationLayerLRNAcross.set_group(group5); + rbutton_normalizationLayerLRNWithin.set_active(); + m_grid_normalizationLayerType.attach(rbutton_normalizationLayerLRNWithin,2,9,1,1); + rbutton_normalizationLayerLRNWithin.show(); + m_grid_normalizationLayerType.attach(rbutton_normalizationLayerLRNAcross,3,9,1,1); + rbutton_normalizationLayerLRNAcross.show(); + + label_normalizationLayerAcrossChannel.set_text("Inner Parameter - across_channels: \n(boolean- 0 or 1)"); + label_normalizationLayerAcrossChannel.set_line_wrap(); + label_normalizationLayerAcrossChannel.set_justify(Gtk::JUSTIFY_FILL); + m_grid_normalizationLayerType.attach(label_normalizationLayerAcrossChannel,0,10,2,1); + label_normalizationLayerAcrossChannel.show(); + + text_normalizationLayerAcrossChannel.set_max_length(100); + text_normalizationLayerAcrossChannel.set_text("0"); + text_normalizationLayerAcrossChannel.select_region(0, text_normalizationLayerAcrossChannel.get_text_length()); + m_grid_normalizationLayerType.attach(text_normalizationLayerAcrossChannel,2,10,1,1); + text_normalizationLayerAcrossChannel.show(); + + label_normalizationLayerNormalizeVariance.set_text("Inner Parameter - normalize_variance: \n(boolean- 0 or 1)"); + label_normalizationLayerNormalizeVariance.set_line_wrap(); + label_normalizationLayerNormalizeVariance.set_justify(Gtk::JUSTIFY_FILL); + m_grid_normalizationLayerType.attach(label_normalizationLayerNormalizeVariance,0,11,2,1); + label_normalizationLayerNormalizeVariance.show(); + + text_normalizationLayerNormalizeVariance.set_max_length(100); + text_normalizationLayerNormalizeVariance.set_text("0"); + text_normalizationLayerNormalizeVariance.select_region(0, text_normalizationLayerNormalizeVariance.get_text_length()); + m_grid_normalizationLayerType.attach(text_normalizationLayerNormalizeVariance,2,11,1,1); + text_normalizationLayerNormalizeVariance.show(); + + label_normalizationLayerEps.set_text("Inner Parameter - eps: "); + label_normalizationLayerEps.set_line_wrap(); + label_normalizationLayerEps.set_justify(Gtk::JUSTIFY_FILL); + m_grid_normalizationLayerType.attach(label_normalizationLayerEps,0,12,2,1); + label_normalizationLayerEps.show(); + + text_normalizationLayerEps.set_max_length(100); + text_normalizationLayerEps.set_text("100"); + text_normalizationLayerEps.select_region(0, text_normalizationLayerEps.get_text_length()); + m_grid_normalizationLayerType.attach(text_normalizationLayerEps,2,12,1,1); + text_normalizationLayerEps.show(); + + + m_sw_normalizationLayerType.add(m_grid_normalizationLayerType); + + + //Loss Layer Window + title_lossLayerType.set_text("Will be updated soon"); + title_lossLayerType.set_line_wrap(); + title_lossLayerType.set_justify(Gtk::JUSTIFY_FILL); + m_grid_lossLayerType.attach(title_lossLayerType,0,0,2,1); + title_lossLayerType.show(); + + + button_setLossParameters.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "setLossParameters")); + m_grid_lossLayerType.attach(button_setLossParameters,0,25,2,1); + + button_addMoreLayer4.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "addMoreLayer4")); + m_grid_lossLayerType.attach(button_addMoreLayer4,2,25,1,1); + + label_lossLayerBottom1.set_text("Bottom1 Layer Name: "); + label_lossLayerBottom1.set_line_wrap(); + label_lossLayerBottom1.set_justify(Gtk::JUSTIFY_FILL); + m_grid_lossLayerType.attach(label_lossLayerBottom1,0,1,2,1); + label_lossLayerBottom1.show(); + + text_lossLayerBottom1.set_max_length(100); + text_lossLayerBottom1.set_text(""); + text_lossLayerBottom1.select_region(0, text_lossLayerBottom1.get_text_length()); + m_grid_lossLayerType.attach(text_lossLayerBottom1,2,1,1,1); + text_lossLayerBottom1.show(); + + label_lossLayerBottom2.set_text("Bottom2 Layer Name: "); + label_lossLayerBottom2.set_line_wrap(); + label_lossLayerBottom2.set_justify(Gtk::JUSTIFY_FILL); + m_grid_lossLayerType.attach(label_lossLayerBottom2,0,2,2,1); + label_lossLayerBottom2.show(); + + text_lossLayerBottom2.set_max_length(100); + text_lossLayerBottom2.set_text(""); + text_lossLayerBottom2.select_region(0, text_lossLayerBottom2.get_text_length()); + m_grid_lossLayerType.attach(text_lossLayerBottom2,2,2,1,1); + text_lossLayerBottom2.show(); + + label_lossLayerTop.set_text("Top Layer Name: "); + label_lossLayerTop.set_line_wrap(); + label_lossLayerTop.set_justify(Gtk::JUSTIFY_FILL); + m_grid_lossLayerType.attach(label_lossLayerTop,0,3,2,1); + label_lossLayerTop.show(); + + text_lossLayerTop.set_max_length(100); + text_lossLayerTop.set_text(""); + text_lossLayerTop.select_region(0, text_lossLayerTop.get_text_length()); + m_grid_lossLayerType.attach(text_lossLayerTop,2,3,1,1); + text_lossLayerTop.show(); + + label_lossLayerName.set_text("Current Layer Name: "); + label_lossLayerName.set_line_wrap(); + label_lossLayerName.set_justify(Gtk::JUSTIFY_FILL); + m_grid_lossLayerType.attach(label_lossLayerName,0,4,2,1); + label_lossLayerName.show(); + + text_lossLayerName.set_max_length(100); + text_lossLayerName.set_text(""); + text_lossLayerName.select_region(0, text_lossLayerName.get_text_length()); + m_grid_lossLayerType.attach(text_lossLayerName,2,4,1,1); + text_lossLayerName.show(); + + label_lossLayerNormalize.set_text("Normalize: \n(bool value)"); + label_lossLayerNormalize.set_line_wrap(); + label_lossLayerNormalize.set_justify(Gtk::JUSTIFY_FILL); + m_grid_lossLayerType.attach(label_lossLayerNormalize,0,5,2,1); + label_lossLayerNormalize.show(); + + text_lossLayerNormalize.set_max_length(100); + text_lossLayerNormalize.set_text(""); + text_lossLayerNormalize.select_region(0, text_lossLayerNormalize.get_text_length()); + m_grid_lossLayerType.attach(text_lossLayerNormalize,2,5,1,1); + text_lossLayerNormalize.show(); + + label_lossLayerNormalize.set_text("Normalization: \n(select type)"); + label_lossLayerNormalize.set_line_wrap(); + label_lossLayerNormalize.set_justify(Gtk::JUSTIFY_FILL); + m_grid_lossLayerType.attach(label_lossLayerNormalize,0,6,2,1); + label_lossLayerNormalize.show(); + + + m_sw_lossLayerType.add(m_grid_lossLayerType); + + + //Extra Layer Window + title_extraLayerType.set_text("Will be updated soon"); + title_extraLayerType.set_line_wrap(); + title_extraLayerType.set_justify(Gtk::JUSTIFY_FILL); + m_grid_extraLayerType.attach(title_extraLayerType,0,0,2,1); + title_extraLayerType.show(); + + button_addMoreLayer5.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "addMoreLayer5")); + m_grid_extraLayerType.attach(button_addMoreLayer5,0,2,1,1); + + m_sw_extraLayerType.add(m_grid_extraLayerType); + + //Display Window + + set_title("Activation Layer"); + set_border_width(10); +// add(box_fullCnnLayerMatter); + m_sw_fullCnnLayerMatter.add(textView_fullCnnLayerMatter); + m_sw_fullCnnLayerMatter.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); + box_fullCnnLayerMatter.pack_start(m_sw_fullCnnLayerMatter); + button_editMore.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "editMore")); + button_deleteLayerAtEnd.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "deleteLayerAtEnd")); + box_fullCnnLayerMatter.pack_start(buttonBox_fullCnnLayerMatter, Gtk::PACK_SHRINK); + buttonBox_fullCnnLayerMatter.pack_start(button_editMore, Gtk::PACK_SHRINK); + buttonBox_fullCnnLayerMatter.pack_start(button_deleteLayerAtEnd, Gtk::PACK_SHRINK); + buttonBox_fullCnnLayerMatter.set_border_width(5); + buttonBox_fullCnnLayerMatter.set_spacing(5); + buttonBox_fullCnnLayerMatter.set_layout(Gtk::BUTTONBOX_END); + buffer_fullCnnLayerMatter = Gtk::TextBuffer::create(); +} + +NetworkCreator::~NetworkCreator() +{ +} + + +void NetworkCreator::on_button_clicked(Glib::ustring data) +{ + if(data == "networkFileName") + { + networkFileName = text_networkFileName.get_text(); + std::cout << "Network File Name set as: " << networkFileName << std::endl; + } + else if(data == "activationLayerType") + { + showWindow_activationLayerType(activationLayerTypeData); + } + else if(data == "addMoreLayer") + { + showWindow_main(); + } + else if(data == "setActivationParameters") + { + activationLayerTypeMatter = ""; + if(activationLayerTypeData == "AbsVal" or activationLayerTypeData == "PReLU" or activationLayerTypeData == "Sigmoid" or activationLayerTypeData == "TanH" or activationLayerTypeData == "") + { + activationLayerTypeMatter = "layer{"; + activationLayerTypeMatter += "\n\tbottom: \"" + text_activationLayerBottom.get_text() + "\""; + activationLayerTypeMatter += "\n\ttop: \"" + text_activationLayerTop.get_text() + "\""; + activationLayerTypeMatter += "\n\tname: \"" + text_activationLayerName.get_text() + "\""; + if(activationLayerTypeData == "") + activationLayerTypeData = "AbsVal"; + activationLayerTypeMatter += "\n\ttype: \"" + activationLayerTypeData + "\""; + activationLayerTypeMatter += "\n}\n"; + } + else if(activationLayerTypeData == "ReLU") + { + activationLayerTypeMatter = "layer{"; + activationLayerTypeMatter += "\n\tbottom: \"" + text_activationLayerBottom.get_text() + "\""; + activationLayerTypeMatter += "\n\ttop: \"" + text_activationLayerTop.get_text() + "\""; + activationLayerTypeMatter += "\n\tname: \"" + text_activationLayerName.get_text() + "\""; + activationLayerTypeMatter += "\n\ttype: \"" + activationLayerTypeData + "\""; + activationLayerTypeMatter += "\n\trelu_param{"; + activationLayerTypeMatter += "\n\t\tnegative_slope: " + text_activationLayerNegativeSlope.get_text(); + activationLayerTypeMatter += "\n\t}"; + activationLayerTypeMatter += "\n}\n"; + } + else if(activationLayerTypeData == "Exp" or activationLayerTypeData == "Log") + { + + activationLayerTypeMatter = "layer{"; + activationLayerTypeMatter += "\n\tbottom: \"" + text_activationLayerBottom.get_text() + "\""; + activationLayerTypeMatter += "\n\ttop: \"" + text_activationLayerTop.get_text() + "\""; + activationLayerTypeMatter += "\n\tname: \"" + text_activationLayerName.get_text() + "\""; + activationLayerTypeMatter += "\n\ttype: \"" + activationLayerTypeData + "\""; + if(activationLayerTypeData == "Exp") + activationLayerTypeMatter += "\n\texp_param{"; + else if(activationLayerTypeData == "Log") + activationLayerTypeMatter += "\n\texp_param{"; + activationLayerTypeMatter += "\n\t\tscale: " + text_activationLayerScale.get_text(); + activationLayerTypeMatter += "\n\t\tshift: " + text_activationLayerShift.get_text(); + activationLayerTypeMatter += "\n\t\tbase: " + text_activationLayerBase.get_text(); + activationLayerTypeMatter += "\n\t}"; + activationLayerTypeMatter += "\n}\n"; + } + else if(activationLayerTypeData == "Power") + { + activationLayerTypeMatter = "layer{"; + activationLayerTypeMatter += "\n\tbottom: \"" + text_activationLayerBottom.get_text() + "\""; + activationLayerTypeMatter += "\n\ttop: \"" + text_activationLayerTop.get_text() + "\""; + activationLayerTypeMatter += "\n\tname: \"" + text_activationLayerName.get_text() + "\""; + activationLayerTypeMatter += "\n\ttype: \"" + data + "\""; + activationLayerTypeMatter += "\n\tpower_param{"; + activationLayerTypeMatter += "\n\t\tscale: " + text_activationLayerScale.get_text(); + activationLayerTypeMatter += "\n\t\tshift: " + text_activationLayerShift.get_text(); + activationLayerTypeMatter += "\n\t\tpower: " + text_activationLayerBase.get_text(); + activationLayerTypeMatter += "\n\t}"; + activationLayerTypeMatter += "\n}\n"; + } + + +// std::cout << activationLayerTypeMatter << std::endl; + if(numLayers == 0) + { + initializeLayer(headLayer,activationLayerTypeMatter); + numLayers++; + fullCnnLayers.push_back(activationLayerTypeMatter); + } + else + { + appendLayer(headLayer,activationLayerTypeMatter); + numLayers++; + fullCnnLayers.push_back(activationLayerTypeMatter); + } + } + else if(data == "displayCnnLayers") + { + fullCnnLayerMatter = displayCNN(headLayer); + showWindow_displayWindow(); + } + else if(data == "editMore") + { + showWindow_main(); + } + else if(data == "deleteLayerAtEnd") + { + if(numLayers>1) //cannot delete the first created layer + { + Glib::ustring lastLayer = fullCnnLayers[numLayers-1]; +// std::cout << lastLayer << std:: endl; + fullCnnLayers.pop_back(); + numLayers--; + Node *layer = searchLayer(headLayer,lastLayer); + if(deleteLayer(&headLayer,layer)) + std::cout << "numLayers = "<< numLayers << "\n"; + fullCnnLayerMatter = displayCNN(headLayer); + showWindow_displayWindow(); + } + + } + else if(data == "criticalLayerType") + { + showWindow_criticalLayerType(criticalLayerTypeData); + } + else if(data == "addMoreLayer2") + { + showWindow_main(); + } + else if(data == "setCriticalParameters") + { + criticalLayerTypeMatter = ""; + if(criticalLayerTypeData == "") + criticalLayerTypeData = "Accuracy"; + if(criticalLayerTypeData == "Accuracy" or criticalLayerTypeData == "Softmax") + { + criticalLayerTypeMatter = "layer{"; + criticalLayerTypeMatter += "\n\tbottom: \"" + text_criticalLayerBottom1.get_text() + "\""; + criticalLayerTypeMatter += "\n\tbottom: \"" + text_criticalLayerBottom2.get_text() + "\""; + criticalLayerTypeMatter += "\n\ttop: \"" + text_criticalLayerTop.get_text() + "\""; + criticalLayerTypeMatter += "\n\tname: \"" + text_criticalLayerName.get_text() + "\""; + criticalLayerTypeMatter += "\n\ttype: \"" + criticalLayerTypeData + "\""; + criticalLayerTypeMatter += "\n\tinclude{"; + criticalLayerTypeMatter += "\n\t\tphase: TEST"; + criticalLayerTypeMatter += "\n\t}"; + criticalLayerTypeMatter += "\n}\n"; + } + else if(criticalLayerTypeData == "Convolution" or criticalLayerTypeData == "Deconvolution") + { + criticalLayerTypeMatter = "layer{"; + criticalLayerTypeMatter += "\n\tbottom: \"" + text_criticalLayerBottom1.get_text() + "\""; + criticalLayerTypeMatter += "\n\ttop: \"" + text_criticalLayerTop.get_text() + "\""; + criticalLayerTypeMatter += "\n\tname: \"" + text_criticalLayerName.get_text() + "\""; + criticalLayerTypeMatter += "\n\ttype: \"" + criticalLayerTypeData + "\""; + criticalLayerTypeMatter += "\n\tparam{"; + criticalLayerTypeMatter += "\n\t\tlr_mult: " + text_criticalLayerFilterLr.get_text(); + criticalLayerTypeMatter += "\n\t\tdecay_mult: " + text_criticalLayerFilterDm.get_text(); + criticalLayerTypeMatter += "\n\t}"; + criticalLayerTypeMatter += "\n\tparam{"; + criticalLayerTypeMatter += "\n\t\tlr_mult: " + text_criticalLayerBiasLr.get_text(); + criticalLayerTypeMatter += "\n\t\tdecay_mult: " + text_criticalLayerBiasDm.get_text(); + criticalLayerTypeMatter += "\n\t}"; + criticalLayerTypeMatter += "\n\tconvolution_param{"; + criticalLayerTypeMatter += "\n\t\tnum_output: " + text_criticalLayerNumOutput.get_text(); + criticalLayerTypeMatter += "\n\t\tkernel_w: " + text_criticalLayerKernelW.get_text(); + criticalLayerTypeMatter += "\n\t\tkernel_h: " + text_criticalLayerKernelH.get_text(); + criticalLayerTypeMatter += "\n\t\tstride_w: " + text_criticalLayerStrideW.get_text(); + criticalLayerTypeMatter += "\n\t\tstride_h: " + text_criticalLayerStrideH.get_text(); + criticalLayerTypeMatter += "\n\t\tpad_w: " + text_criticalLayerPadW.get_text(); + criticalLayerTypeMatter += "\n\t\tpad_h: " + text_criticalLayerPadH.get_text(); + criticalLayerTypeMatter += "\n\t\tweight_filler{"; + if(rbutton_criticalLayerWeightFillerConstant.get_active() == 1) + { + criticalLayerTypeMatter += "\n\t\t\ttype: \"constant\""; + criticalLayerTypeMatter += "\n\t\t\tvalue: " + text_criticalLayerWeightFillerConstantValue.get_text(); + } + else if(rbutton_criticalLayerWeightFillerUniform.get_active() == 1) + { + criticalLayerTypeMatter += "\n\t\t\ttype: \"uniform\""; + criticalLayerTypeMatter += "\n\t\t\tmin: " + text_criticalLayerWeightFillerUniformMin.get_text(); + criticalLayerTypeMatter += "\n\t\t\tmax: " + text_criticalLayerWeightFillerUniformMax.get_text(); + } + else if(rbutton_criticalLayerWeightGaussian.get_active() == 1) + { + criticalLayerTypeMatter += "\n\t\t\ttype: \"gaussian\""; + criticalLayerTypeMatter += "\n\t\t\tmean: " + text_criticalLayerWeightFillerGaussianMean.get_text(); + criticalLayerTypeMatter += "\n\t\t\tstd: " + text_criticalLayerWeightFillerGaussianStd.get_text(); + } + else if(rbutton_criticalLayerWeightFillerPositiveUnitBall.get_active() == 1) + { + criticalLayerTypeMatter += "\n\t\t\ttype: \"positive_unitball\""; + } + else if(rbutton_criticalLayerWeightFillerXavier.get_active() == 1) + { + criticalLayerTypeMatter += "\n\t\t\ttype: \"xavier\""; + if(rbutton_criticalLayerWeightFillerXavierIn.get_active() == 1) + criticalLayerTypeMatter += "\n\t\t\tvariance_norm: FAN_IN"; + else if(rbutton_criticalLayerWeightFillerXavierOut.get_active() == 1) + criticalLayerTypeMatter += "\n\t\t\tvariance_norm: FAN_OUT"; + else if(rbutton_criticalLayerWeightFillerXavierAvg.get_active() == 1) + criticalLayerTypeMatter += "\n\t\t\tvariance_norm: AVERAGE"; + } + else if(rbutton_criticalLayerWeightFillerMSRA.get_active() == 1) + { + criticalLayerTypeMatter += "\n\t\t\ttype: \"msra\""; + if(rbutton_criticalLayerWeightFillerMSRAIn.get_active() == 1) + criticalLayerTypeMatter += "\n\t\t\tvariance_norm: FAN_IN"; + else if(rbutton_criticalLayerWeightFillerMSRAOut.get_active() == 1) + criticalLayerTypeMatter += "\n\t\t\tvariance_norm: FAN_OUT"; + else if(rbutton_criticalLayerWeightFillerMSRAAvg.get_active() == 1) + criticalLayerTypeMatter += "\n\t\t\tvariance_norm: AVERAGE"; + } + else if(rbutton_criticalLayerWeightFillerBilinear.get_active() == 1) + { + criticalLayerTypeMatter += "\n\t\t\ttype: \"bilinear\""; + } + criticalLayerTypeMatter += "\n\t\t}"; + criticalLayerTypeMatter += "\n\t\tbias_filler{"; + criticalLayerTypeMatter += "\n\t\t\ttype: \"constant\""; //only type of filler as of now + criticalLayerTypeMatter += "\n\t\t\tvalue: " + text_criticalLayerBias.get_text(); + criticalLayerTypeMatter += "\n\t\t}"; + criticalLayerTypeMatter += "\n\t}"; + criticalLayerTypeMatter += "\n}\n"; + + } + else if(criticalLayerTypeData == "Dropout") + { + criticalLayerTypeMatter = "layer{"; + criticalLayerTypeMatter += "\n\tbottom: \"" + text_criticalLayerBottom1.get_text() + "\""; + criticalLayerTypeMatter += "\n\ttop: \"" + text_criticalLayerTop.get_text() + "\""; + criticalLayerTypeMatter += "\n\tname: \"" + text_criticalLayerName.get_text() + "\""; + criticalLayerTypeMatter += "\n\ttype: \"" + criticalLayerTypeData + "\""; + criticalLayerTypeMatter += "\n\tdropout_param{"; + criticalLayerTypeMatter += "\n\t\tdropout_ratio: " + text_criticalLayerDropoutRatio.get_text(); + criticalLayerTypeMatter += "\n\t}"; + criticalLayerTypeMatter += "\n}\n"; + } + else if(criticalLayerTypeData == "InnerProduct") + { + criticalLayerTypeMatter = "layer{"; + criticalLayerTypeMatter += "\n\tbottom: \"" + text_criticalLayerBottom1.get_text() + "\""; + criticalLayerTypeMatter += "\n\ttop: \"" + text_criticalLayerTop.get_text() + "\""; + criticalLayerTypeMatter += "\n\tname: \"" + text_criticalLayerName.get_text() + "\""; + criticalLayerTypeMatter += "\n\ttype: \"" + criticalLayerTypeData + "\""; + criticalLayerTypeMatter += "\n\tparam{"; + criticalLayerTypeMatter += "\n\t\tlr_mult: " + text_criticalLayerFilterLr.get_text(); + criticalLayerTypeMatter += "\n\t\tdecay_mult: " + text_criticalLayerFilterDm.get_text(); + criticalLayerTypeMatter += "\n\t}"; + criticalLayerTypeMatter += "\n\tparam{"; + criticalLayerTypeMatter += "\n\t\tlr_mult: " + text_criticalLayerBiasLr.get_text(); + criticalLayerTypeMatter += "\n\t\tdecay_mult: " + text_criticalLayerBiasDm.get_text(); + criticalLayerTypeMatter += "\n\t}"; + criticalLayerTypeMatter += "\n\tinner_product_param{"; + criticalLayerTypeMatter += "\n\t\tnum_output: " + text_criticalLayerNumOutput.get_text(); + criticalLayerTypeMatter += "\n\t\tweight_filler{"; + if(rbutton_criticalLayerWeightFillerConstant.get_active() == 1) + { + criticalLayerTypeMatter += "\n\t\t\ttype: \"constant\""; + criticalLayerTypeMatter += "\n\t\t\tvalue: " + text_criticalLayerWeightFillerConstantValue.get_text(); + } + else if(rbutton_criticalLayerWeightFillerUniform.get_active() == 1) + { + criticalLayerTypeMatter += "\n\t\t\ttype: \"uniform\""; + criticalLayerTypeMatter += "\n\t\t\tmin: " + text_criticalLayerWeightFillerUniformMin.get_text(); + criticalLayerTypeMatter += "\n\t\t\tmax: " + text_criticalLayerWeightFillerUniformMax.get_text(); + } + else if(rbutton_criticalLayerWeightGaussian.get_active() == 1) + { + criticalLayerTypeMatter += "\n\t\t\ttype: \"gaussian\""; + criticalLayerTypeMatter += "\n\t\t\tmean: " + text_criticalLayerWeightFillerGaussianMean.get_text(); + criticalLayerTypeMatter += "\n\t\t\tstd: " + text_criticalLayerWeightFillerGaussianStd.get_text(); + } + else if(rbutton_criticalLayerWeightFillerPositiveUnitBall.get_active() == 1) + { + criticalLayerTypeMatter += "\n\t\t\ttype: \"positive_unitball\""; + } + else if(rbutton_criticalLayerWeightFillerXavier.get_active() == 1) + { + criticalLayerTypeMatter += "\n\t\t\ttype: \"xavier\""; + if(rbutton_criticalLayerWeightFillerXavierIn.get_active() == 1) + criticalLayerTypeMatter += "\n\t\t\tvariance_norm: FAN_IN"; + else if(rbutton_criticalLayerWeightFillerXavierOut.get_active() == 1) + criticalLayerTypeMatter += "\n\t\t\tvariance_norm: FAN_OUT"; + else if(rbutton_criticalLayerWeightFillerXavierAvg.get_active() == 1) + criticalLayerTypeMatter += "\n\t\t\tvariance_norm: AVERAGE"; + } + else if(rbutton_criticalLayerWeightFillerMSRA.get_active() == 1) + { + criticalLayerTypeMatter += "\n\t\t\ttype: \"msra\""; + if(rbutton_criticalLayerWeightFillerMSRAIn.get_active() == 1) + criticalLayerTypeMatter += "\n\t\t\tvariance_norm: FAN_IN"; + else if(rbutton_criticalLayerWeightFillerMSRAOut.get_active() == 1) + criticalLayerTypeMatter += "\n\t\t\tvariance_norm: FAN_OUT"; + else if(rbutton_criticalLayerWeightFillerMSRAAvg.get_active() == 1) + criticalLayerTypeMatter += "\n\t\t\tvariance_norm: AVERAGE"; + } + else if(rbutton_criticalLayerWeightFillerBilinear.get_active() == 1) + { + criticalLayerTypeMatter += "\n\t\t\ttype: \"bilinear\""; + } + criticalLayerTypeMatter += "\n\t\t}"; + criticalLayerTypeMatter += "\n\t\tbias_filler{"; + criticalLayerTypeMatter += "\n\t\t\ttype: \"constant\""; //only type of filler as of now + criticalLayerTypeMatter += "\n\t\t\tvalue: " + text_criticalLayerBias.get_text(); + criticalLayerTypeMatter += "\n\t\t}"; + criticalLayerTypeMatter += "\n\t}"; + criticalLayerTypeMatter += "\n}\n"; + } + else if(criticalLayerTypeData == "Pooling") + { + criticalLayerTypeMatter = "layer{"; + criticalLayerTypeMatter += "\n\tbottom: \"" + text_criticalLayerBottom1.get_text() + "\""; + criticalLayerTypeMatter += "\n\ttop: \"" + text_criticalLayerTop.get_text() + "\""; + criticalLayerTypeMatter += "\n\tname: \"" + text_criticalLayerName.get_text() + "\""; + criticalLayerTypeMatter += "\n\ttype: \"" + criticalLayerTypeData + "\""; + criticalLayerTypeMatter += "\n\tparam{"; + criticalLayerTypeMatter += "\n\t\tlr_mult: " + text_criticalLayerFilterLr.get_text(); + criticalLayerTypeMatter += "\n\t\tdecay_mult: " + text_criticalLayerFilterDm.get_text(); + criticalLayerTypeMatter += "\n\t}"; + criticalLayerTypeMatter += "\n\tparam{"; + criticalLayerTypeMatter += "\n\t\tlr_mult: " + text_criticalLayerBiasLr.get_text(); + criticalLayerTypeMatter += "\n\t\tdecay_mult: " + text_criticalLayerBiasDm.get_text(); + criticalLayerTypeMatter += "\n\t}"; + criticalLayerTypeMatter += "\n\tpooling_param{"; + criticalLayerTypeMatter += "\n\t\tnum_output: " + text_criticalLayerNumOutput.get_text(); + criticalLayerTypeMatter += "\n\t\tkernel_w: " + text_criticalLayerKernelW.get_text(); + criticalLayerTypeMatter += "\n\t\tkernel_h: " + text_criticalLayerKernelH.get_text(); + criticalLayerTypeMatter += "\n\t\tstride_w: " + text_criticalLayerStrideW.get_text(); + criticalLayerTypeMatter += "\n\t\tstride_h: " + text_criticalLayerStrideH.get_text(); + criticalLayerTypeMatter += "\n\t\tpad_w: " + text_criticalLayerPadW.get_text(); + criticalLayerTypeMatter += "\n\t\tpad_h: " + text_criticalLayerPadH.get_text(); + if(rbutton_criticalLayerPoolMax.get_active() == 1) + criticalLayerTypeMatter += "\n\t\tpool: MAX"; + else if(rbutton_criticalLayerPoolAve.get_active() == 1) + criticalLayerTypeMatter += "\n\t\tpool: AVE"; + criticalLayerTypeMatter += "\n\t}"; + criticalLayerTypeMatter += "\n}\n"; + } + if(numLayers == 0) + { + initializeLayer(headLayer,criticalLayerTypeMatter); + numLayers++; + fullCnnLayers.push_back(criticalLayerTypeMatter); + } + else + { + appendLayer(headLayer,criticalLayerTypeMatter); + numLayers++; + fullCnnLayers.push_back(criticalLayerTypeMatter); + } + } + else if(data == "normalizationLayerType") + { + showWindow_normalizationLayerType(normalizationLayerTypeData); + } + else if(data == "setNormalizationParameters") + { + normalizationLayerTypeMatter = ""; + if(normalizationLayerTypeData == "BatchNorm" or normalizationLayerTypeData == "") + { + normalizationLayerTypeMatter = "layer{"; + normalizationLayerTypeMatter += "\n\tbottom: \"" + text_normalizationLayerBottom.get_text() + "\""; + normalizationLayerTypeMatter += "\n\ttop: \"" + text_normalizationLayerTop.get_text() + "\""; + normalizationLayerTypeMatter += "\n\tname: \"" + text_normalizationLayerName.get_text() + "\""; + if(normalizationLayerTypeData == "") + normalizationLayerTypeData = "BatchNorm"; + normalizationLayerTypeMatter += "\n\ttype: \"" + normalizationLayerTypeData + "\""; + normalizationLayerTypeMatter += "\n}\n"; + } + else if(normalizationLayerTypeData == "LRN") + { + normalizationLayerTypeMatter = "layer{"; + normalizationLayerTypeMatter += "\n\tbottom: \"" + text_normalizationLayerBottom.get_text() + "\""; + normalizationLayerTypeMatter += "\n\ttop: \"" + text_normalizationLayerTop.get_text() + "\""; + normalizationLayerTypeMatter += "\n\tname: \"" + text_normalizationLayerName.get_text() + "\""; + normalizationLayerTypeMatter += "\n\ttype: \"" + normalizationLayerTypeData + "\""; + normalizationLayerTypeMatter += "\n\tlrn_param{"; + normalizationLayerTypeMatter += "\n\t\tlocal_size: " + text_normalizationLayerlocalSize.get_text(); + normalizationLayerTypeMatter += "\n\t\talpha: " + text_normalizationLayerAlpha.get_text(); + normalizationLayerTypeMatter += "\n\t\tbeta: " + text_normalizationLayerBeta.get_text(); + normalizationLayerTypeMatter += "\n\t\tk: " + text_normalizationLayerK.get_text(); + if(rbutton_normalizationLayerLRNWithin.get_active() == 1) + normalizationLayerTypeMatter += "\n\t\tnorm_region: WITHIN_CHANNEL"; + else + normalizationLayerTypeMatter += "\n\t\tnorm_region: ACROSS_CHANNEL"; + normalizationLayerTypeMatter += "\n\t}"; + normalizationLayerTypeMatter += "\n}\n"; + } + else if(normalizationLayerTypeData == "MVN") + { + normalizationLayerTypeMatter = "layer{"; + normalizationLayerTypeMatter += "\n\tbottom: \"" + text_normalizationLayerBottom.get_text() + "\""; + normalizationLayerTypeMatter += "\n\ttop: \"" + text_normalizationLayerTop.get_text() + "\""; + normalizationLayerTypeMatter += "\n\tname: \"" + text_normalizationLayerName.get_text() + "\""; + normalizationLayerTypeMatter += "\n\ttype: \"" + normalizationLayerTypeData + "\""; + normalizationLayerTypeMatter += "\n\tmvn_param{"; + normalizationLayerTypeMatter += "\n\t\tacross_channels: " + text_normalizationLayerAcrossChannel.get_text(); + normalizationLayerTypeMatter += "\n\t\tnormalize_variance: " + text_normalizationLayerNormalizeVariance.get_text(); + normalizationLayerTypeMatter += "\n\t\teps: " + text_normalizationLayerEps.get_text(); + normalizationLayerTypeMatter += "\n\t}"; + normalizationLayerTypeMatter += "\n}\n"; + } + if(numLayers == 0) + { + initializeLayer(headLayer,normalizationLayerTypeMatter); + numLayers++; + fullCnnLayers.push_back(normalizationLayerTypeMatter); + } + else + { + appendLayer(headLayer,normalizationLayerTypeMatter); + numLayers++; + fullCnnLayers.push_back(normalizationLayerTypeMatter); + } + } + else if(data == "addMoreLayer3") + { + showWindow_main(); + } + else if(data == "lossLayerType") + { + showWindow_lossLayerType(lossLayerTypeData); + } + else if(data == "addMoreLayer4") + { + showWindow_main(); + } + else if(data == "extraLayerType") + { + showWindow_extraLayerType(extraLayerTypeData); + } + else if(data == "addMoreLayer5") + { + showWindow_main(); + } + else if(data == "saveFile") + { + networkFileName = text_networkFileName.get_text(); + std::ofstream myfile; + myfile.open(networkFileName); + std::cout << "Network File Name saved as: " << networkFileName << std::endl; + myfile << "#File generated using OpenDetection" << std::endl; + myfile.close(); + } + +} + + diff --git a/detectors/src/global2D/training/Solver.cpp b/detectors/src/global2D/training/Solver.cpp new file mode 100644 index 00000000..636a2b24 --- /dev/null +++ b/detectors/src/global2D/training/Solver.cpp @@ -0,0 +1,1001 @@ +#include "od/detectors/global2D/training/Solver.h" +#include <iostream> +#include <fstream> + +SolverProperties::SolverProperties(): + label_solverFileName(""), + button_solverFileName("Update"), + text_solverFileName(), + + label_trainNetworkFileType(""), + rbutton_trainNetworkFileType_net("net"), rbutton_trainNetworkFileType_tt("train_net"), + label_trainNetworkFileName(""), + button_trainNetworkFileName("Update"), + text_trainNetworkFileName(), + + label_enableTestNet(""), + rbutton_enableTestNet_no("No"), rbutton_enableTestNet_yes("Yes"), + label_testNetworkFileName(""), + button_testNetworkFileName("Update"), + text_testNetworkFileName(), + + label_enableValidationParameters(""), + rbutton_enableValidationParameters_no("No"), rbutton_enableValidationParameters_yes("Yes"), + label_testIter(""), + button_testIter("Update"), + text_testIter(), + + label_testInterval(""), + button_testInterval("Update"), + text_testInterval(), + + label_enableAverageLoss(""), + rbutton_enableAverageLoss_no("No"), rbutton_enableAverageLoss_yes("Yes"), + label_averageLoss(""), + button_averageLoss("Update"), + text_averageLoss(), + + label_enableRandomSample(""), + rbutton_enableRandomSample_no("No"), rbutton_enableRandomSample_yes("Yes"), + label_randomSample(""), + button_randomSample("Update"), + text_randomSample(), + + label_display(""), + button_display("Update"), + text_display(), + + label_enableDebugInfo(""), + rbutton_enableDebugInfo_no("No"), rbutton_enableDebugInfo_yes("Yes"), + button_debugInfo("Update"), + + label_snapshot(""), + button_snapshot("Update"), + text_snapshot(), + + label_enableTestComputeLoss(""), + rbutton_enableTestComputeLoss_no("No"), rbutton_enableTestComputeLoss_yes("Yes"), + button_testComputeLoss("Update"), + + label_snapshotPrefix(""), + button_snapshotPrefix("Update"), + text_snapshotPrefix(), + + label_maxIter(""), + button_maxIter("Update"), + text_maxIter(), + + label_type(""), + button_type("Update"), + rbutton_typeSGD_yes("SGD"), rbutton_typeAdadelta_yes("AdaDelta"), rbutton_typeAdagrad_yes("AdaGrad"), rbutton_typeAdam_yes("Adam"), + rbutton_typeRMSProp_yes("RMSProp"), rbutton_typeNesterov_yes("Nesterov"), + + label_learningRatePolicy(""), + button_learningRatePolicy("Update"), + rbutton_learningRatePolicyFixed_yes("fixed"), rbutton_learningRatePolicyExp_yes("exp"), rbutton_learningRatePolicyStep_yes("step"), + rbutton_learningRatePolicyInv_yes("inv"), rbutton_learningRatePolicyMultistep_yes("multistep"), + rbutton_learningRatePolicyPoly_yes("poly"), rbutton_learningRatePolicySigmoid_yes("sigmoid"), + + label_baseLearningRate(""), + button_baseLearningRate("Update"), + text_baseLearningRate(), + + label_gamma(""), + button_gamma("Update"), + text_gamma(), + + label_power(""), + button_power("Update"), + text_power(), + + label_stepSize(""), + button_stepSize("Update"), + text_stepSize(), + + label_stepSizeValue(""), + button_stepSizeValue("Update"), + text_stepSizeValue(), + + label_weightDecay(""), + button_weightDecay("Update"), + text_weightDecay(), + + label_momentum(""), + button_momentum("Update"), + text_momentum(), + + button_saveFile("Save File") +{ + set_title("Solver"); + set_border_width(10); + add(m_sw1); + m_grid1.set_column_spacing (10); + m_grid1.set_row_spacing (50); + + //level0 + + label_solverFileName.set_text("1) Give a proper name to the solver file: "); + label_solverFileName.set_line_wrap(); + label_solverFileName.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_solverFileName,0,0,2,1); + label_solverFileName.show(); + + text_solverFileName.set_max_length(100); + text_solverFileName.set_text("../examples/objectdetector/Mnist_Train/solverCustom1.prototxt"); + text_solverFileName.select_region(0, text_solverFileName.get_text_length()); + m_grid1.attach(text_solverFileName,2,0,5,1); + text_solverFileName.show(); + + button_solverFileName.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "solverFileName")); + m_grid1.attach(button_solverFileName,7,0,1,1); + button_solverFileName.show(); + + //level1 + + label_trainNetworkFileType.set_text("2)Select type of training network file type.\nUsually these exists two types,\nfirst adds validation and training in the same file,\nWhile other adds them in two different files"); + label_trainNetworkFileType.set_line_wrap(); + label_trainNetworkFileType.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_trainNetworkFileType,0,1,2,1); + label_trainNetworkFileType.show(); + + Gtk::RadioButton::Group group = rbutton_trainNetworkFileType_net.get_group(); + rbutton_trainNetworkFileType_tt.set_group(group); + rbutton_trainNetworkFileType_net.set_active(); + m_grid1.attach(rbutton_trainNetworkFileType_net,2,1,1,1); + rbutton_trainNetworkFileType_net.show(); + m_grid1.attach(rbutton_trainNetworkFileType_tt,3,1,1,1); + rbutton_trainNetworkFileType_tt.show(); + + //level2 + + label_trainNetworkFileName.set_text("2.1) net: or train_net:\n(Parameter Details: Give location of \nthe net file or the train_net file) "); + label_trainNetworkFileName.set_line_wrap(); + label_trainNetworkFileName.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_trainNetworkFileName,0,2,2,1); + label_trainNetworkFileName.show(); + + text_trainNetworkFileName.set_max_length(500); + text_trainNetworkFileName.set_text("../examples/objectdetector/Mnist_Train/train1.prototxt"); + text_trainNetworkFileName.select_region(0, text_solverFileName.get_text_length()); + m_grid1.attach(text_trainNetworkFileName,2,2,5,1); + text_trainNetworkFileName.show(); + + button_trainNetworkFileName.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "trainNetworkFileName")); + m_grid1.attach(button_trainNetworkFileName,7,2,1,1); + button_trainNetworkFileName.show(); + + //level3 + + label_enableTestNet.set_text("3) Enable Test Network Parameter:\n(Enable only with using \"train_net\" parameter.)"); + label_enableTestNet.set_line_wrap(); + label_enableTestNet.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_enableTestNet,0,3,2,1); + label_enableTestNet.show(); + + Gtk::RadioButton::Group group2 = rbutton_enableTestNet_no.get_group(); + rbutton_enableTestNet_yes.set_group(group2); + rbutton_enableTestNet_no.set_active(); + m_grid1.attach(rbutton_enableTestNet_no,2,3,1,1); + rbutton_enableTestNet_no.show(); + m_grid1.attach(rbutton_enableTestNet_yes,3,3,1,1); + rbutton_enableTestNet_yes.show(); + + label_testNetworkFileName.set_text("3.1) test_net:"); + label_testNetworkFileName.set_line_wrap(); + label_testNetworkFileName.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_testNetworkFileName,4,3,1,1); + label_testNetworkFileName.show(); + + text_testNetworkFileName.set_max_length(500); + text_testNetworkFileName.set_text("../examples/objectdetector/Mnist_Train/test1.prototxt"); + text_testNetworkFileName.select_region(0, text_testNetworkFileName.get_text_length()); + m_grid1.attach(text_testNetworkFileName,5,3,3,1); + text_testNetworkFileName.show(); + + button_testNetworkFileName.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "testNetworkFileName")); + m_grid1.attach(button_testNetworkFileName,8,3,1,1); + button_testNetworkFileName.show(); + + + //level4 + + label_enableValidationParameters.set_text("4) Enable Validation(test) phase Parameters:\nParameters are \"test_iter\" and \"test_interval\""); + label_enableValidationParameters.set_line_wrap(); + label_enableValidationParameters.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_enableValidationParameters,0,4,2,1); + label_enableValidationParameters.show(); + + Gtk::RadioButton::Group group3 = rbutton_enableValidationParameters_no.get_group(); + rbutton_enableValidationParameters_yes.set_group(group3); + rbutton_enableValidationParameters_no.set_active(); + m_grid1.attach(rbutton_enableValidationParameters_no,2,4,1,1); + rbutton_enableValidationParameters_no.show(); + m_grid1.attach(rbutton_enableValidationParameters_yes,3,4,1,1); + rbutton_enableValidationParameters_yes.show(); + + //level 5 + + label_testIter.set_text("4.1) test_iter:\n(Set number of iterations during validation phase)"); + label_testIter.set_line_wrap(); + label_testIter.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_testIter,0,5,2,1); + label_testIter.show(); + + text_testIter.set_max_length(100); + text_testIter.set_text("100"); + text_testIter.select_region(0, text_testNetworkFileName.get_text_length()); + m_grid1.attach(text_testIter,2,5,1,1); + text_testIter.show(); + + button_testIter.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "testIter")); + m_grid1.attach(button_testIter,3,5,1,1); + button_testIter.show(); + + + //level 6 + + label_testInterval.set_text("4.2) test_interval:\n(Specifies that after a set of mentioned training iterations,\na validation phase is initiated)"); + label_testInterval.set_line_wrap(); + label_testInterval.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_testInterval,0,6,2,1); + label_testInterval.show(); + + text_testInterval.set_max_length(100); + text_testInterval.set_text("100"); + text_testInterval.select_region(0, text_testNetworkFileName.get_text_length()); + m_grid1.attach(text_testInterval,2,6,1,1); + text_testInterval.show(); + + button_testInterval.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "testInterval")); + m_grid1.attach(button_testInterval,3,6,1,1); + button_testInterval.show(); + + //level 7 + + label_enableAverageLoss.set_text("5) Enable \"average_loss\" parameter: "); + label_enableAverageLoss.set_line_wrap(); + label_enableAverageLoss.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_enableAverageLoss,0,7,2,1); + label_enableAverageLoss.show(); + + Gtk::RadioButton::Group group4 = rbutton_enableAverageLoss_no.get_group(); + rbutton_enableAverageLoss_yes.set_group(group4); + rbutton_enableAverageLoss_no.set_active(); + m_grid1.attach(rbutton_enableAverageLoss_no,2,7,1,1); + rbutton_enableAverageLoss_no.show(); + m_grid1.attach(rbutton_enableAverageLoss_yes,3,7,1,1); + rbutton_enableAverageLoss_yes.show(); + + label_averageLoss.set_text("average_loss:"); + label_averageLoss.set_line_wrap(); + label_averageLoss.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_averageLoss,4,7,1,1); + label_averageLoss.show(); + + text_averageLoss.set_max_length(100); + text_averageLoss.set_text("1.0"); + text_averageLoss.select_region(0, text_averageLoss.get_text_length()); + m_grid1.attach(text_averageLoss,5,7,1,1); + text_averageLoss.show(); + + button_averageLoss.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "averageLoss")); + m_grid1.attach(button_averageLoss,6,7,1,1); + button_averageLoss.show(); + + + //level 8 + + label_enableRandomSample.set_text("6) Enable \"random_seed\" parameter: "); + label_enableRandomSample.set_line_wrap(); + label_enableRandomSample.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_enableRandomSample,0,8,2,1); + label_enableRandomSample.show(); + + Gtk::RadioButton::Group group5 = rbutton_enableRandomSample_no.get_group(); + rbutton_enableRandomSample_yes.set_group(group5); + rbutton_enableRandomSample_no.set_active(); + m_grid1.attach(rbutton_enableRandomSample_no,2,8,1,1); + rbutton_enableRandomSample_no.show(); + m_grid1.attach(rbutton_enableRandomSample_yes,3,8,1,1); + rbutton_enableRandomSample_yes.show(); + + label_randomSample.set_text("random_seed:"); + label_randomSample.set_line_wrap(); + label_randomSample.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_randomSample,4,8,1,1); + label_randomSample.show(); + + text_randomSample.set_max_length(100); + text_randomSample.set_text("1"); + text_randomSample.select_region(0, text_randomSample.get_text_length()); + m_grid1.attach(text_randomSample,5,8,1,1); + text_randomSample.show(); + + button_randomSample.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "randomSample")); + m_grid1.attach(button_randomSample,6,8,1,1); + button_randomSample.show(); + + //label 9 + + label_display.set_text("7)display:\n(Used to diplay output afer every specified number of iterations)"); + label_display.set_line_wrap(); + label_display.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_display,0,9,2,1); + label_display.show(); + + text_display.set_max_length(100); + text_display.set_text("100"); + text_display.select_region(0, text_display.get_text_length()); + m_grid1.attach(text_display,2,9,1,1); + text_display.show(); + + button_display.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "display")); + m_grid1.attach(button_display,3,9,1,1); + button_display.show(); + + + //level 10 + + label_enableDebugInfo.set_text("8) Enable \"debug_info\" parameter:\n(Used to see every step in training,\nsuitable for resolving bugs"); + label_enableDebugInfo.set_line_wrap(); + label_enableDebugInfo.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_enableDebugInfo,0,10,2,1); + label_enableDebugInfo.show(); + + Gtk::RadioButton::Group group6 = rbutton_enableDebugInfo_no.get_group(); + rbutton_enableDebugInfo_yes.set_group(group6); + rbutton_enableDebugInfo_no.set_active(); + m_grid1.attach(rbutton_enableDebugInfo_no,2,10,1,1); + rbutton_enableDebugInfo_no.show(); + m_grid1.attach(rbutton_enableDebugInfo_yes,3,10,1,1); + rbutton_enableDebugInfo_yes.show(); + + button_debugInfo.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "debugInfo")); + m_grid1.attach(button_debugInfo,4,10,1,1); + button_debugInfo.show(); + + //level 11 + + label_snapshot.set_text("9)snapshot:\n(Used to save trained weights afer every specified number of iterations)"); + label_snapshot.set_line_wrap(); + label_snapshot.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_snapshot,0,11,2,1); + label_snapshot.show(); + + text_snapshot.set_max_length(100); + text_snapshot.set_text("100"); + text_snapshot.select_region(0, text_snapshot.get_text_length()); + m_grid1.attach(text_snapshot,2,11,1,1); + text_snapshot.show(); + + button_snapshot.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "snapshot")); + m_grid1.attach(button_snapshot,3,11,1,1); + button_snapshot.show(); + + + //level 12 + + label_enableTestComputeLoss.set_text("8) Enable \"test_compute_loss\" parameter:\n(Set as 1 when needed to calculate loss in validation phase"); + label_enableTestComputeLoss.set_line_wrap(); + label_enableTestComputeLoss.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_enableTestComputeLoss,0,12,2,1); + label_enableTestComputeLoss.show(); + + Gtk::RadioButton::Group group7 = rbutton_enableTestComputeLoss_no.get_group(); + rbutton_enableTestComputeLoss_yes.set_group(group7); + rbutton_enableTestComputeLoss_no.set_active(); + m_grid1.attach(rbutton_enableTestComputeLoss_no,2,12,1,1); + rbutton_enableTestComputeLoss_no.show(); + m_grid1.attach(rbutton_enableTestComputeLoss_yes,3,12,1,1); + rbutton_enableTestComputeLoss_yes.show(); + + button_testComputeLoss.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "testComputeLoss")); + m_grid1.attach(button_testComputeLoss,4,12,1,1); + button_testComputeLoss.show(); + + //level 13 + + label_snapshotPrefix.set_text("10)snapshot_prefix:\n(Provide prefix string to save the weights.\nProvide path to the saved weights.)"); + label_snapshotPrefix.set_line_wrap(); + label_snapshotPrefix.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_snapshotPrefix,0,13,2,1); + label_snapshotPrefix.show(); + + text_snapshotPrefix.set_max_length(500); + text_snapshotPrefix.set_text("../examples/objectdetector/Mnist_Train/sample_prefix"); + text_snapshotPrefix.select_region(0, text_snapshotPrefix.get_text_length()); + m_grid1.attach(text_snapshotPrefix,2,13,5,1); + text_snapshotPrefix.show(); + + button_snapshotPrefix.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "snapshotPrefix")); + m_grid1.attach(button_snapshotPrefix,7,13,1,1); + button_snapshotPrefix.show(); + + + + //level 14 + + label_maxIter.set_text("11)max_iter:\n(Provide maximum number of iteratios to be performed"); + label_maxIter.set_line_wrap(); + label_maxIter.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_maxIter,0,14,2,1); + label_maxIter.show(); + + text_maxIter.set_max_length(100); + text_maxIter.set_text("10000"); + text_maxIter.select_region(0, text_maxIter.get_text_length()); + m_grid1.attach(text_maxIter,2,14,1,1); + text_maxIter.show(); + + button_maxIter.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "maxIter")); + m_grid1.attach(button_maxIter,3,14,1,1); + button_maxIter.show(); + + + //level 15 + + label_type.set_text("12)type:\n(Select Type of Solver"); + label_type.set_line_wrap(); + label_type.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_type,0,15,2,1); + label_type.show(); + + Gtk::RadioButton::Group group8 = rbutton_typeSGD_yes.get_group(); + rbutton_typeAdadelta_yes.set_group(group8); + rbutton_typeAdagrad_yes.set_group(group8); + rbutton_typeAdam_yes.set_group(group8); + rbutton_typeRMSProp_yes.set_group(group8); + rbutton_typeNesterov_yes.set_group(group8); + rbutton_typeSGD_yes.set_active(); + m_grid1.attach(rbutton_typeSGD_yes,2,15,1,1); + rbutton_typeSGD_yes.show(); + m_grid1.attach(rbutton_typeAdadelta_yes,3,15,1,1); + rbutton_typeAdadelta_yes.show(); + m_grid1.attach(rbutton_typeAdagrad_yes,4,15,1,1); + rbutton_typeAdagrad_yes.show(); + m_grid1.attach(rbutton_typeAdam_yes,5,15,1,1); + rbutton_typeAdam_yes.show(); + m_grid1.attach(rbutton_typeRMSProp_yes,6,15,1,1); + rbutton_typeRMSProp_yes.show(); + m_grid1.attach(rbutton_typeNesterov_yes,7,15,1,1); + rbutton_typeNesterov_yes.show(); + + button_type.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "type")); + m_grid1.attach(button_type,8,15,1,1); + button_type.show(); + + //level 16 + + label_learningRatePolicy.set_text("13)lr_policy:\n(Set learning rate policy"); + label_learningRatePolicy.set_line_wrap(); + label_learningRatePolicy.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_learningRatePolicy,0,16,2,1); + label_learningRatePolicy.show(); + + Gtk::RadioButton::Group group9 = rbutton_learningRatePolicyFixed_yes.get_group(); + rbutton_learningRatePolicyExp_yes.set_group(group9); + rbutton_learningRatePolicyStep_yes.set_group(group9); + rbutton_learningRatePolicyInv_yes.set_group(group9); + rbutton_learningRatePolicyMultistep_yes.set_group(group9); + rbutton_learningRatePolicyPoly_yes.set_group(group9); + rbutton_learningRatePolicySigmoid_yes.set_group(group9); + rbutton_learningRatePolicyFixed_yes.set_active(); + m_grid1.attach(rbutton_learningRatePolicyFixed_yes,2,16,1,1); + rbutton_learningRatePolicyFixed_yes.show(); + m_grid1.attach(rbutton_learningRatePolicyExp_yes,3,16,1,1); + rbutton_learningRatePolicyExp_yes.show(); + m_grid1.attach(rbutton_learningRatePolicyStep_yes,4,16,1,1); + rbutton_learningRatePolicyStep_yes.show(); + m_grid1.attach(rbutton_learningRatePolicyInv_yes,5,16,1,1); + rbutton_learningRatePolicyInv_yes.show(); + m_grid1.attach(rbutton_learningRatePolicyMultistep_yes,6,16,1,1); + rbutton_learningRatePolicyMultistep_yes.show(); + m_grid1.attach(rbutton_learningRatePolicyPoly_yes,7,16,1,1); + rbutton_learningRatePolicyPoly_yes.show(); + m_grid1.attach(rbutton_learningRatePolicySigmoid_yes,8,16,1,1); + rbutton_learningRatePolicySigmoid_yes.show(); + + button_learningRatePolicy.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "learningRatePolicy")); + m_grid1.attach(button_learningRatePolicy,9,16,1,1); + button_learningRatePolicy.show(); + + //level 17 + + label_baseLearningRate.set_text("11)base_lr:\n(Set initial learning rate"); + label_baseLearningRate.set_line_wrap(); + label_baseLearningRate.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_baseLearningRate,0,17,2,1); + label_baseLearningRate.show(); + + text_baseLearningRate.set_max_length(100); + text_baseLearningRate.set_text("0.01"); + text_baseLearningRate.select_region(0, text_baseLearningRate.get_text_length()); + m_grid1.attach(text_baseLearningRate,2,17,1,1); + text_baseLearningRate.show(); + + button_baseLearningRate.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "baseLearningRate")); + m_grid1.attach(button_baseLearningRate,3,17,1,1); + button_baseLearningRate.show(); + + //level 18 + + label_gamma.set_text("14)gamma:\n(Set gamma value. Used in learning rate policies)"); + label_gamma.set_line_wrap(); + label_gamma.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_gamma,0,18,2,1); + label_gamma.show(); + + text_gamma.set_max_length(100); + text_gamma.set_text("0.0001"); + text_gamma.select_region(0, text_gamma.get_text_length()); + m_grid1.attach(text_gamma,2,18,1,1); + text_gamma.show(); + + button_gamma.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "gamma")); + m_grid1.attach(button_gamma,3,18,1,1); + button_gamma.show(); + + + //level 19 + + label_power.set_text("15)power:\n(Set power value. Used in learning rate policies)"); + label_power.set_line_wrap(); + label_power.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_power,0,19,2,1); + label_power.show(); + + text_power.set_max_length(100); + text_power.set_text("0.75"); + text_power.select_region(0, text_power.get_text_length()); + m_grid1.attach(text_power,2,19,1,1); + text_power.show(); + + button_power.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "power")); + m_grid1.attach(button_power,3,19,1,1); + button_power.show(); + + + //level 20 + + label_stepSize.set_text("16)stepsize:\n(Set stepSize. Used in learning rate policies)"); + label_stepSize.set_line_wrap(); + label_stepSize.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_stepSize,0,20,2,1); + label_stepSize.show(); + + text_stepSize.set_max_length(100); + text_stepSize.set_text("100"); + text_stepSize.select_region(0, text_stepSize.get_text_length()); + m_grid1.attach(text_stepSize,2,20,1,1); + text_stepSize.show(); + + button_stepSize.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "stepSize")); + m_grid1.attach(button_stepSize,3,20,1,1); + button_stepSize.show(); + + + //level 21 + + label_stepSizeValue.set_text("17)stepvalue:\n(Set stepvalue. Used in learning rate policu \"multistep\")"); + label_stepSizeValue.set_line_wrap(); + label_stepSizeValue.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_stepSizeValue,0,21,2,1); + label_stepSizeValue.show(); + + text_stepSizeValue.set_max_length(100); + text_stepSizeValue.set_text("100"); + text_stepSizeValue.select_region(0, text_stepSizeValue.get_text_length()); + m_grid1.attach(text_stepSizeValue,2,21,1,1); + text_stepSizeValue.show(); + + button_stepSizeValue.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "stepSizeValue")); + m_grid1.attach(button_stepSizeValue,3,21,1,1); + button_stepSizeValue.show(); + + //level 22 + + label_weightDecay.set_text("18)weight_decay:"); + label_weightDecay.set_line_wrap(); + label_weightDecay.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_weightDecay,0,22,2,1); + label_weightDecay.show(); + + text_weightDecay.set_max_length(100); + text_weightDecay.set_text("0.0005"); + text_weightDecay.select_region(0, text_weightDecay.get_text_length()); + m_grid1.attach(text_weightDecay,2,22,1,1); + text_weightDecay.show(); + + button_weightDecay.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "weightDecay")); + m_grid1.attach(button_weightDecay,3,22,1,1); + button_weightDecay.show(); + + //level 23 + + label_momentum.set_text("19)momentum:"); + label_momentum.set_line_wrap(); + label_momentum.set_justify(Gtk::JUSTIFY_FILL); + m_grid1.attach(label_momentum,0,23,2,1); + label_momentum.show(); + + text_momentum.set_max_length(100); + text_momentum.set_text("0.9"); + text_momentum.select_region(0, text_momentum.get_text_length()); + m_grid1.attach(text_momentum,2,23,1,1); + text_momentum.show(); + + button_momentum.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "momentum")); + m_grid1.attach(button_momentum,3,23,1,1); + button_momentum.show(); + + + + button_saveFile.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "saveFile")); + m_grid1.attach(button_saveFile,0,24,2,1); + button_saveFile.show(); + + + + m_sw1.add(m_grid1); + m_sw1.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); +// m_grid1.show(); + show_all_children(); + m_sw1.show(); +} + +SolverProperties::~SolverProperties() +{ +} + +void SolverProperties::on_button_clicked(Glib::ustring data) +{ + if(data == "solverFileName") + { + solverFileName = text_solverFileName.get_text(); + std::cout << "Solver File Name set as: " << solverFileName << std::endl; + } + else if(data == "trainNetworkFileName") + { + trainNetworkFileName = text_trainNetworkFileName.get_text(); + std::cout << "Train Network File Name set as: " << trainNetworkFileName << std::endl; + } + else if(data == "testNetworkFileName") + { + testNetworkFileName = text_testNetworkFileName.get_text(); + std::cout << "Test Network File Name set as: " << testNetworkFileName << std::endl; + if(rbutton_enableTestNet_yes.get_active() == 1 and rbutton_trainNetworkFileType_net.get_active() == 1) + { + Gtk::MessageDialog dialog(*this, "\"test_net\" parameter not required"); + dialog.set_secondary_text("\"test_net\" parameter is only required when \"train_net\" parameter is specified"); + dialog.run(); + } + } + else if(data == "testIter") + { + testIter = text_testIter.get_text(); + std::cout << "Validation(test) Phase iterations set as: " << testIter << std::endl; + if((rbutton_enableTestNet_yes.get_active() == 1 and rbutton_enableValidationParameters_no.get_active() == 1)) + { + Gtk::MessageDialog dialog(*this, "Validation parameters required"); + dialog.set_secondary_text("Validation parameters are required when \"test_net\" parameter is specified."); + dialog.run(); + } + else if(rbutton_enableValidationParameters_no.get_active() == 1) + { + Gtk::MessageDialog dialog(*this, "Enable Validation Parameters"); + dialog.set_secondary_text("Validation parameters can be updated only after enabling them"); + dialog.run(); + } + } + else if(data == "testInterval") + { + testInterval = text_testInterval.get_text(); + std::cout << "Validation(test) Phase iterations set as: " << testInterval << std::endl; + if((rbutton_enableTestNet_yes.get_active() == 1 and rbutton_enableValidationParameters_no.get_active() == 1)) + { + Gtk::MessageDialog dialog(*this, "Validation parameters required"); + dialog.set_secondary_text("Validation parameters are required when \"test_net\" parameter is specified."); + dialog.run(); + } + else if(rbutton_enableValidationParameters_no.get_active() == 1) + { + Gtk::MessageDialog dialog(*this, "Enable Validation Parameters"); + dialog.set_secondary_text("Validation parameters can be updated only after enabling them"); + dialog.run(); + } + } + else if(data == "averageLoss") + { + averageLoss = text_averageLoss.get_text(); + std::cout << "Average Loss set as: " << averageLoss << std::endl; + if(rbutton_enableAverageLoss_no.get_active() == 1) + { + Gtk::MessageDialog dialog(*this, "Enable Average Loss Parameter"); + dialog.set_secondary_text("Average Loss Parameter can be updated only after enabling it"); + dialog.run(); + } + } + else if(data == "randomSample") + { + randomSample = text_randomSample.get_text(); + std::cout << "Random Sample Parameters set as: " << randomSample << std::endl; + if(rbutton_enableRandomSample_no.get_active() == 1) + { + Gtk::MessageDialog dialog(*this, "Enable Random Sample Parameter"); + dialog.set_secondary_text("Random Sample Parameter can be updated only after enabling it"); + dialog.run(); + } + } + else if(data == "display") + { + display = text_display.get_text(); + std::cout << "Display after every: " << display << " iterations" << std::endl; + } + else if(data == "debugInfo") + { + if(rbutton_enableDebugInfo_yes.get_active()) + debugInfo = "1"; + else if(rbutton_enableDebugInfo_no.get_active()) + debugInfo = "0"; + std::cout << "Debug Information: " << debugInfo << std::endl; + } + else if(data == "snapshot") + { + snapshot = text_snapshot.get_text(); + std::cout << "Snapshot saved after every: " << snapshot << " iterations" << std::endl; + } + else if(data == "testComputeLoss") + { + if(rbutton_enableTestComputeLoss_yes.get_active()) + testComputeLoss = "1"; + else if(rbutton_enableTestComputeLoss_no.get_active()) + testComputeLoss = "0"; + std::cout << "Compute Loss in Validation Phase: " << testComputeLoss << std::endl; + } + else if(data == "snapshotPrefix") + { + snapshotPrefix = text_snapshotPrefix.get_text(); + std::cout << "Snapshot saved with prefix: " << snapshotPrefix << std::endl; + } + else if(data == "maxIter") + { + maxIter = text_maxIter.get_text(); + std::cout << "Maximum iterations set as: " << maxIter << std::endl; + } + else if(data == "type") + { + if(rbutton_typeSGD_yes.get_active()) + type = "1"; + else if(rbutton_typeAdadelta_yes.get_active()) + type = "AdaDelta"; + else if(rbutton_typeAdagrad_yes.get_active()) + type = "AdaGrad"; + else if(rbutton_typeAdam_yes.get_active()) + type = "Adam"; + else if(rbutton_typeRMSProp_yes.get_active()) + type = "RMSProp"; + else if(rbutton_typeNesterov_yes.get_active()) + type = "Nesterov"; + std::cout << "Solver Type Set as: " << type << std::endl; + } + else if(data == "learningRatePolicy") + { + if(rbutton_learningRatePolicyFixed_yes.get_active()) + learningRatePolicy = "fixed"; + else if(rbutton_learningRatePolicyExp_yes.get_active()) + learningRatePolicy = "exp"; + else if(rbutton_learningRatePolicyStep_yes.get_active()) + learningRatePolicy = "step"; + else if(rbutton_learningRatePolicyInv_yes.get_active()) + learningRatePolicy = "inv"; + else if(rbutton_learningRatePolicyMultistep_yes.get_active()) + learningRatePolicy = "multistep"; + else if(rbutton_learningRatePolicyPoly_yes.get_active()) + learningRatePolicy = "poly"; + else if(rbutton_learningRatePolicySigmoid_yes.get_active()) + learningRatePolicy = "sigmoid"; + + std::cout << "Learning rate policy is set as: " << learningRatePolicy << std::endl; + } + else if(data == "baseLearningRate") + { + baseLearningRate = text_baseLearningRate.get_text(); + std::cout << "Initial learning rate set as: " << baseLearningRate << std::endl; + } + else if(data == "gamma") + { + gamma = text_gamma.get_text(); + std::cout << "Gamma set as: " << gamma << std::endl; + } + else if(data == "power") + { + power = text_power.get_text(); + std::cout << "Power set as: " << power << std::endl; + } + else if(data == "stepSize") + { + stepSize = text_stepSize.get_text(); + std::cout << "stepSize set as: " << stepSize << std::endl; + } + else if(data == "stepSizeValue") + { + stepSizeValue = text_stepSizeValue.get_text(); + std::cout << "stepSizeValue set as: " << stepSizeValue << std::endl; + } + else if(data == "weightDecay") + { + weightDecay = text_weightDecay.get_text(); + std::cout << "weightDecay set as: " << weightDecay << std::endl; + } + else if(data == "momentum") + { + momentum = text_momentum.get_text(); + std::cout << "momentum set as: " << momentum << std::endl; + } + else if(data == "saveFile") + { + + if(rbutton_enableTestNet_yes.get_active() == 1 and rbutton_trainNetworkFileType_net.get_active() == 1) + { + Gtk::MessageDialog dialog(*this, "\"test_net\" parameter not required"); + dialog.set_secondary_text("\"test_net\" parameter is only required when \"train_net\" parameter is specified"); + dialog.run(); + } + else if(rbutton_enableTestNet_yes.get_active() == 1 and rbutton_enableValidationParameters_no.get_active() == 1) + { + Gtk::MessageDialog dialog(*this, "Validation parameters required"); + dialog.set_secondary_text("Validation parameters are required when \"test_net\" parameter is specified."); + dialog.run(); + } + else + { + solverFileName = text_solverFileName.get_text(); + trainNetworkFileName = text_trainNetworkFileName.get_text(); + testNetworkFileName = text_testNetworkFileName.get_text(); + testIter = text_testIter.get_text(); + testInterval = text_testInterval.get_text(); + averageLoss = text_averageLoss.get_text(); + randomSample = text_randomSample.get_text(); + display = text_display.get_text(); + snapshot = text_snapshot.get_text(); + snapshotPrefix = text_snapshotPrefix.get_text(); + maxIter = text_maxIter.get_text(); + baseLearningRate = text_baseLearningRate.get_text(); + gamma = text_gamma.get_text(); + power = text_power.get_text(); + stepSize = text_stepSize.get_text(); + stepSizeValue = text_stepSizeValue.get_text(); + weightDecay = text_weightDecay.get_text(); + momentum = text_momentum.get_text(); + + if(rbutton_enableDebugInfo_yes.get_active()) + debugInfo = "1"; + else if(rbutton_enableDebugInfo_no.get_active()) + debugInfo = "0"; + + if(rbutton_enableTestComputeLoss_yes.get_active()) + testComputeLoss = "1"; + else if(rbutton_enableTestComputeLoss_no.get_active()) + testComputeLoss = "0"; + + if(rbutton_typeSGD_yes.get_active()) + type = "1"; + else if(rbutton_typeAdadelta_yes.get_active()) + type = "AdaDelta"; + else if(rbutton_typeAdagrad_yes.get_active()) + type = "AdaGrad"; + else if(rbutton_typeAdam_yes.get_active()) + type = "Adam"; + else if(rbutton_typeRMSProp_yes.get_active()) + type = "RMSProp"; + else if(rbutton_typeNesterov_yes.get_active()) + type = "Nesterov"; + + if(rbutton_learningRatePolicyFixed_yes.get_active()) + learningRatePolicy = "fixed"; + else if(rbutton_learningRatePolicyExp_yes.get_active()) + learningRatePolicy = "exp"; + else if(rbutton_learningRatePolicyStep_yes.get_active()) + learningRatePolicy = "step"; + else if(rbutton_learningRatePolicyInv_yes.get_active()) + learningRatePolicy = "inv"; + else if(rbutton_learningRatePolicyMultistep_yes.get_active()) + learningRatePolicy = "multistep"; + else if(rbutton_learningRatePolicyPoly_yes.get_active()) + learningRatePolicy = "poly"; + else if(rbutton_learningRatePolicySigmoid_yes.get_active()) + learningRatePolicy = "sigmoid"; + + std::ofstream myfile; + myfile.open(solverFileName); + myfile << "#File generated using OpenDetection" << std::endl; + myfile.close(); + myfile.open(solverFileName); + if(!myfile) + { + Gtk::MessageDialog dialog(*this, "File Could not be Created"); + dialog.set_secondary_text("Make sure the destination exists or the file is writable"); + dialog.run(); + } + std::cout << "Solver File Name saved as: " << solverFileName << std::endl; + + + if(rbutton_trainNetworkFileType_net.get_active() == 1) + myfile << "net: " << "\"" << trainNetworkFileName << "\"" << std::endl; + else if(rbutton_trainNetworkFileType_tt.get_active() == 1) + myfile << "train_net: " << "\"" << trainNetworkFileName << "\"" << std::endl; + + if(rbutton_enableTestNet_yes.get_active() == 1) + myfile << "test_net: " << "\"" << testNetworkFileName << "\"" << std::endl; + else if(rbutton_enableTestNet_no.get_active() == 1) + myfile << "#test_net: " << "\"" << testNetworkFileName << "\"" << std::endl; + + if(rbutton_enableValidationParameters_yes.get_active() == 1) + { + myfile << "test_iter: " << testIter << std::endl; + myfile << "test_interval: " << testInterval << std::endl; + } + else if(rbutton_enableValidationParameters_no.get_active() == 1) + { + myfile << "#test_iter: " << testIter << std::endl; + myfile << "#test_interval: " << testInterval << std::endl; + } + + if(rbutton_enableAverageLoss_yes.get_active() == 1) + myfile << "average_loss: " << averageLoss << std::endl; + else if(rbutton_enableAverageLoss_no.get_active() == 1) + myfile << "#average_loss: " << averageLoss << std::endl; + + if(rbutton_enableRandomSample_yes.get_active() == 1) + myfile << "random_seed: " << randomSample << std::endl; + else if(rbutton_enableRandomSample_no.get_active() == 1) + myfile << "#random_seed: " << randomSample << std::endl; + + + myfile << "display: " << display << std::endl; + myfile << "debug_info: " << debugInfo << std::endl; + myfile << "snapshot: " << snapshot << std::endl; + myfile << "test_compute_loss: " << testComputeLoss << std::endl; + myfile << "snapshot_prefix: " << "\"" << snapshotPrefix << "\"" << std::endl; + myfile << "max_iter: " << maxIter << std::endl; + myfile << "type: " << "\"" << type << "\"" << std::endl; + + myfile << "lr_policy: " << "\"" << learningRatePolicy << "\"" << std::endl; + myfile << "base_lr: " << baseLearningRate << std::endl; + myfile << "gamma: " << gamma << std::endl; + myfile << "power: " << power << std::endl; + myfile << "stepsize: " << stepSize << std::endl; + myfile << "stepvalue: " << stepSizeValue << std::endl; + + myfile << "weight_decay: " << weightDecay << std::endl; + myfile << "momentum: " << momentum << std::endl; + + + myfile.close(); + } + } + +} diff --git a/examples/apps/CMakeLists.txt b/examples/apps/CMakeLists.txt index e7870891..a0665889 100644 --- a/examples/apps/CMakeLists.txt +++ b/examples/apps/CMakeLists.txt @@ -5,12 +5,13 @@ if(cad_recog_test_single_model) include_directories(${OpenCV_INCLUDE_DIRS}) include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) + include_directories(${COMMON_IMPL_DIR}) include_directories(${CMAKE_3RDPARTY_DIR}/pugixml/src/) include_directories(${CMAKE_3RDPARTY_DIR}/SiftGPU/src/SiftGPU/) include_directories(${PCL_INCLUDE_DIRS}) OD_ADD_EXAMPLE(odcadrecog_test_single_model - FILES cadrecog2D/od_test_single_db_single_model.cpp + FILES cadrecog2D/test_single_db_single_model.cpp LINK_WITH od_local_image_detector ) else() @@ -38,5 +39,5 @@ endif(od_multihog_app) ################################################################################## set(SOURCE_FILES version/opendetection.cpp) -include_directories(${_BINARY_DIR}/generated) +include_directories(${CMAKE_OD_GENERATED_DIR}) add_executable(opendetection ${SOURCE_FILES}) diff --git a/examples/apps/version/opendetection.cpp b/examples/apps/version/opendetection.cpp index d5dc2388..58cec556 100644 --- a/examples/apps/version/opendetection.cpp +++ b/examples/apps/version/opendetection.cpp @@ -2,7 +2,7 @@ // Created by sarkar on 03.06.15. // -#include "od_version.h" +#include "version.h" #include <iostream> int main (int argc, char *argv[]) diff --git a/examples/objectdetector/CMakeLists.txt b/examples/objectdetector/CMakeLists.txt index 644944e7..8f25d8cd 100644 --- a/examples/objectdetector/CMakeLists.txt +++ b/examples/objectdetector/CMakeLists.txt @@ -46,8 +46,8 @@ if(hog_train_example) include_directories(${COMMON_IMPL_DIR}) OD_ADD_EXAMPLE(od_hog_train - FILES od_hog_train.cpp - LINK_WITH global_image_detector) + FILES hog_train.cpp + LINK_WITH od_global_image_detector) else() message("!!! built WITH_SVMLIGHT to enable hog_train_example") endif() @@ -218,7 +218,7 @@ if(cascade_files_example) include_directories(${COMMON_INCLUDE_DIR}) include_directories(${COMMON_IMPL_DIR}) - V_ADD_EXAMPLE(od_cascade_files + OD_ADD_EXAMPLE(od_cascade_files FILES cascade_files.cpp LINK_WITH od_global_image_detector) else() From 6da0702082a66864524e8c4e6fb6d9ae46e76761 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Mon, 1 Aug 2016 16:14:27 +0200 Subject: [PATCH 61/79] adds first 3 examples of cnn --- cmake/Options.cmake | 33 ++++------ .../global2D/detection/ConvClassification.h | 1 + .../global2D/detection/ConvClassification.cpp | 6 ++ examples/objectdetector/CMakeLists.txt | 61 ++++++++++++++++++ .../cnn_mnist_classification.cpp | 63 +++++++++++++++++++ .../cnn_mnist_train_customSolver.cpp | 11 ++++ .../objectdetector/cnn_mnist_train_simple.cpp | 11 ++++ 7 files changed, 166 insertions(+), 20 deletions(-) create mode 100644 examples/objectdetector/cnn_mnist_classification.cpp create mode 100644 examples/objectdetector/cnn_mnist_train_customSolver.cpp create mode 100644 examples/objectdetector/cnn_mnist_train_simple.cpp diff --git a/cmake/Options.cmake b/cmake/Options.cmake index cc868c00..846170f4 100644 --- a/cmake/Options.cmake +++ b/cmake/Options.cmake @@ -9,11 +9,20 @@ elseif(UNIX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") endif() +if(WITH_WARNINGS) + if(WIN32) + if(MSVC) + set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} /WALL") + elseif(CMAKE_COMPILER_IS_GNUCXX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") + endif() + elseif(UNIX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") + endif() +endif() + if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE "Release" CACHE STRING - "Choose the type of build, options are: Debug Release -RelWithDebInfo MinSizeRel." - FORCE) + set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE) endif(NOT CMAKE_BUILD_TYPE) # Optional parameters @@ -31,18 +40,6 @@ option(BUILD_GLOBAL_3D_DETECTION "build global 3D detection" ON) option(BUILD_LOCAL_2D_DETECTION "build local 2D detection" ON) option(BUILD_MISC_DETECTION "build miscellaneous detection" ON) -if(WITH_WARNINGS) - if(WIN32) - if(MSVC) - set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} /WALL") - elseif(CMAKE_COMPILER_IS_GNUCXX) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") - endif() - elseif(UNIX) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") - endif() -endif() - if(WITH_STD_SHARED_PTR) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWITH_STD_SHARED_PTR") endif() @@ -51,7 +48,3 @@ if(WITH_GPU) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWITH_GPU") endif() -if(WITH_CAFFE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWITH_CAFFE") -endif() - diff --git a/detectors/include/od/detectors/global2D/detection/ConvClassification.h b/detectors/include/od/detectors/global2D/detection/ConvClassification.h index a87db5b4..99b5cde1 100644 --- a/detectors/include/od/detectors/global2D/detection/ConvClassification.h +++ b/detectors/include/od/detectors/global2D/detection/ConvClassification.h @@ -46,6 +46,7 @@ namespace od void init(); shared_ptr<Detections2D> detectOmni(shared_ptr<SceneImage> scene); shared_ptr<Detections> detect(shared_ptr<SceneImage> scene); + shared_ptr<Detections> detectOmni(shared_ptr<Scene> scene); private: std::string weightModelFileLoaction; diff --git a/detectors/src/global2D/detection/ConvClassification.cpp b/detectors/src/global2D/detection/ConvClassification.cpp index 1669b52a..93068de3 100644 --- a/detectors/src/global2D/detection/ConvClassification.cpp +++ b/detectors/src/global2D/detection/ConvClassification.cpp @@ -115,6 +115,12 @@ namespace od { return make_shared<Detections>(); } + + shared_ptr<Detections> ConvClassification::detectOmni(shared_ptr<Scene> scene) + { + std::cout << "not implemented, use with shared_ptr<SceneImage>" <<std::endl; + return nullptr; + } diff --git a/examples/objectdetector/CMakeLists.txt b/examples/objectdetector/CMakeLists.txt index 8f25d8cd..f1939a27 100644 --- a/examples/objectdetector/CMakeLists.txt +++ b/examples/objectdetector/CMakeLists.txt @@ -307,5 +307,66 @@ if(framegenerator_example) LINK_WITH od_common) endif() +################################################################################## +option(cnn_classification "Build the cnn classification example" ON) +if(Caffe_FOUND) + if(cnn_classification) + if(TARGET od_global_image_detector) + + include_directories(${DETECTORS_INCLUDE_DIR}) + if(WITH_GPU) + include_directories(${CUDA_INCLUDE_DIRS}) + endif() + + OD_ADD_EXAMPLE(cnn_classification + FILES cnn_mnist_classification.cpp + LINK_WITH od_global_image_detector) + else() + message("!!! cnn_classification is set to on but BUILD_GLOBAL_2D_DETECTION is necessary to build cnn_classification") + endif(TARGET od_global_image_detector) + endif(cnn_classification) +else() + message("!!! cnn_classification is set to on but CAFFE was not found") +endif(Caffe_FOUND) + +################################################################################## +option(cnn_train_custom "Build the cnn training custom example" ON) +if(Caffe_FOUND AND GTKMM_FOUND) + if(cnn_train_custom) + if(TARGET od_global_image_detector) + + include_directories(${DETECTORS_INCLUDE_DIR}) + include_directories(${GTKMM_INCLUDE_DIRS}) + + OD_ADD_EXAMPLE(cnn_train_custom + FILES cnn_mnist_train_customSolver.cpp + LINK_WITH od_global_image_detector) + else() + message("!!! cnn_train_custom is set to on but BUILD_GLOBAL_2D_DETECTION is necessary to build cnn_train_custom") + endif(TARGET od_global_image_detector) + endif(cnn_train_custom) +else() + message("!!! cnn_train_custom is set to on but CAFFE or GTKMM were not found") +endif(Caffe_FOUND AND GTKMM_FOUND) + +################################################################################## +option(cnn_training "Build the cnn training example" ON) +if(Caffe_FOUND) + if(cnn_training) + if(TARGET od_global_image_detector) + + include_directories(${DETECTORS_INCLUDE_DIR}) + + OD_ADD_EXAMPLE(cnn_training + FILES cnn_mnist_train_simple.cpp + LINK_WITH od_global_image_detector) + else() + message("!!! cnn_training is set to on but BUILD_GLOBAL_2D_DETECTION is necessary to build cnn_training") + endif(TARGET od_global_image_detector) + endif(cnn_training) +else() + message("!!! cnn_training is set to on but CAFFE was not found") +endif(Caffe_FOUND) + diff --git a/examples/objectdetector/cnn_mnist_classification.cpp b/examples/objectdetector/cnn_mnist_classification.cpp new file mode 100644 index 00000000..7c5897d5 --- /dev/null +++ b/examples/objectdetector/cnn_mnist_classification.cpp @@ -0,0 +1,63 @@ +#include "od/detectors/global2D/detection/ConvClassification.h" +#include <fstream> + +void help() +{ + std::cout << "Usage: ./examples/objectdetector/od_cnn_mnist_classification <path to weight caffemodel file> <path to network file> <path to image file>" << std::endl; + std::cout << "Example: ./examples/objectdetector/od_cnn_mnist_classification ../examples/objectdetector/Mnist_Classify/mnist.caffemodel ../examples/objectdetector/Mnist_Classify/lenet.prototxt ../examples/objectdetector/Mnist_Classify/3.png" << std::endl << std::endl; + exit(-1); +} + +int main(int argc, char **argv) +{ + if (argc != 4) + { + help(); + } + + od::g2d::ConvClassification mnist_classifier(""); + mnist_classifier.setWeightModelFileLocation(argv[1]); + mnist_classifier.setNetworkModelFileLocation(argv[2]); + mnist_classifier.setImageFileLocation(argv[3]); + + for(unsigned int i = 1; i < argc; i++) + { + std::string check(argv[i]); + if(i == 1) + { + size_t found = check.find(".caffemodel"); + if(found > check.length()) + { + std::cout << "First argument should be caffe weight caffemodel file" << std::endl; + help(); + } + } + else if(i == 2) + { + size_t found = check.find(".prototxt"); + if(found > check.length()) + { + std::cout << "Second argument should be caffe network prototxt file" << std::endl; + help(); + } + + } + std::ifstream ifs; + ifs.open(check.c_str()); + if(!ifs) + { + std::cout << "File not found: " << check << std::endl; + help(); + } + } + std::string weight_file_location = mnist_classifier.getWeightModelFileLocation(); + std::string network_file_location = mnist_classifier.getNetworkModelFileLocation(); + std::string image_file_location = mnist_classifier.getImageFileLocation(); + + mnist_classifier.setTestBlob(1, 28, 28); + mnist_classifier.classify(); + + return 0; +} + + diff --git a/examples/objectdetector/cnn_mnist_train_customSolver.cpp b/examples/objectdetector/cnn_mnist_train_customSolver.cpp new file mode 100644 index 00000000..d51d3e9a --- /dev/null +++ b/examples/objectdetector/cnn_mnist_train_customSolver.cpp @@ -0,0 +1,11 @@ +#include "od/detectors/global2D/training/ConvTrainer.h" +#include "od/detectors/global2D/detection/ConvClassification.h" + +int main(int argc, char *argv[]) +{ + argc = 1; + od::g2d::ConvTrainer mnist_trainer("",""); + mnist_trainer.setSolverProperties(argc, argv); + mnist_trainer.startTraining(); + return 0; +} diff --git a/examples/objectdetector/cnn_mnist_train_simple.cpp b/examples/objectdetector/cnn_mnist_train_simple.cpp new file mode 100644 index 00000000..cc0faf53 --- /dev/null +++ b/examples/objectdetector/cnn_mnist_train_simple.cpp @@ -0,0 +1,11 @@ +#include "od/detectors/global2D/training/ConvTrainer.h" +#include "od/detectors/global2D/detection/ConvClassification.h" + +int main(int argc, char **argv) +{ + od::g2d::ConvTrainer mnist_trainer("", ""); + mnist_trainer.setSolverLocation(argv[1]); + mnist_trainer.startTraining(); + return 0; +} + From 05f47b69c9329f5d4a19ed077ed487bc0b3f8cfe Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Tue, 2 Aug 2016 10:20:54 +0200 Subject: [PATCH 62/79] adds blog post about integration giacomo dabisias --- .../gsoc2016_blog_giacomo.md | 32 +++++++++++-------- .../tutorials_doxygen/writing_a_new_app.md | 7 ++-- .../cnn_mnist_classification.cpp | 2 +- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md index e509ed8c..810f06c8 100644 --- a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md +++ b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md @@ -40,7 +40,7 @@ Depending on OpenCV/PCL version activate/deactivate modules of the library. - **Task 9** - small task - merge other gsoc16 contributions : Merge changes from other contribution of GSOC. Other projects will be using existing APIs. There might me need to make small changes to fit to the changes made in this project - **Task 10** - moderate task - deb packaging : Create an automated way to generate a deb file for the library so it can be installed through debian packaging system. -##Fix 3rd party dependencies 12/05/15## +##Fix 3rd party dependencies 12/05/16## The library has some dependencies inserted as source code into the 3rdparty folder; these are pugixml and SiftGPU (and maybe others which will come later). There is also the dependency of svmlight, but this is not mandatory so it will be added as external dependency; if the library is present on the system all the depending libraries will be built. @@ -50,7 +50,7 @@ In general its a bad idea to integrate external source code into a library since - **Pugixml** is a bit more tricky since it has no cmake file; it contains a Make file which produces a test executable but no library. The library can be easily built since we have just two include files and one .cpp file. To fix the issue I created a separate pugixml_build folder with a simple custom cmake which builds the library. This way we can just add that folder as add_subdirectory and directly export the newly compile **libpugixml.so** library. - **Svmlight** will be inserted in the system with a find_package or something similar and a custom **WITH_SVMLIGHT** cmake flag. These steps will come shortly; we will just leave the svmlight binding. -##Refactoring file structure 13/05/15## +##Refactoring file structure 13/05/16## I started to move files in appropriate folders and to fix the "Main" *CmakeLists.txt* file. I renamed the ODconfig.h.in file to od_config.h and moved it into the cmake folder for now. Then I moved the opendetection.cpp source file away from the root (not nice to have source files in the root of a library) and added it as separate app in a new version folder which builds the od_version executable. @@ -116,7 +116,7 @@ and include the upper level folder. To use the first solution we would need to i The new structure compiles fines except for three examples which have a linker bug (undefined reference to `vtable for od::g3d::ODCADDetectTrainer3DGlobal' ) which I am trying to resolve. I fixed a bit the source code of all the examples removing unnecessary includes, fixing namespaces, maintaining a common interface and avoiding dynamic memory allocation where possible. The next step after fixing the linker bug will be to fix the install paths for includes and libs. -##Include structure and install target 23/05/15## +##Include structure and install target 23/05/16## I fixed the linking error; it came from a wrong variable name in a cmake file which specified some source files which were not compiled and so the linking error. I split simple_ransac_detection in src and include and reinserted it in local2D detectors since it is used only there. The source files are just added to the local2D library and compiled together. @@ -149,7 +149,7 @@ Before modifying and digging into the code I wanted to have a common coding styl After this I started to fix the detectors which I am still working on. The first class which has bee updated is the *ODHOGDetector* which had also a lot of naming issues since it used a different naming style. There are stll some function which I dislike, for example the parsing methods. They are mainly based on creating a fake argc,argv couple of variables and the parsing them. This has to be removed in favor of a better and newer parsing library as the one included in boost. This will be one of the first steps which I will do as soon as I finished the basic refactoring step. -##Code refactoring 2 07/06/15## +##Code refactoring 2 07/06/16## The first code refactoring phase terminated today, since I finished to review a bit all files; the main steps have been: @@ -166,7 +166,7 @@ Missing parts and next steps: - Use shared pointer where normal pointer are used. We have to decide if we want to use boost or stds shared pointers since pcl is using the first version. The second would be better so we would have everything which is possible using std and then when pcl moves toward std shared pointer we move also the rest of the code to that. - Remove where possible the argc,argv parsing methods using standard ones. -##Shared Pointers 10/06/15## +##Shared Pointers 10/06/16## The following step took quite a bit in order to have everything work well. Until now the library was using normal C++ pointer created with new and then deleted when not used anymore. This can lead to bad habits of forgetting to delete pointers, causing memory leaks. To avoid this its always good to use safe pointers which get deallocated as soon as they are not referenced my anyone anymore. Safe pointers are called **shared_ptr** in C++ and were intially implemented in the *Boost* library; then, after C++11, they where also inserted in the standard in the *std::* namespace. Substituting all pointers n the library was quite a complex task for different reasons: @@ -178,7 +178,7 @@ The following step took quite a bit in order to have everything work well. Until While restructuring the code I also fixed again some coding style issues, but I still have to go through the code a few more times in order to finish fixing everything. The next step is as previously mentioned, to check the compilation with the svmlight option. -##Cmake improvments 20/06/15## +##Cmake improvments 20/06/16## I created a **FindOD.cmake** which can be used by other libraries to find the include directories path, the library path and the library names. The variables which get set are: @@ -203,7 +203,7 @@ Another important missing thing was the creation of a .deb package which can be All files in the library have been renamed according to a common scheme which consists in a Prefix "OD" followed by the first charachter of the file name uppercase. -##Templates and Windows compilation 27/06/15## +##Templates and Windows compilation 27/06/16## To improve compilation time of projects using the **OpenDetection** library I decided to precompile all the templates for the most common types. In this case almost all templates depend on *pcl* point types. The most commonly used point type for which I precompiled the library are: @@ -218,7 +218,7 @@ I then fixed all the linking in the whole library since there where some useless After that I tested compilation of the library under Windows, but without success. There have been several issues which are ut of the Open Deteciton library. I tested compilation using **Visual Studio 2015** and **MinGW**. Pcl has been installed by using the prebuilt version while opencv has to be built manually since *opencv_contrib* is necessary while not present in the prebuilt binaries. Building opencv3 with visual studio and cuda is not possible at the moment since cuda 7.5 is not compatible with visual studio 2015, so version 8 is necessary which will be available soon. MinGW is also not usable since compilation of opencv3 with cuda is disabled as flagged as incompatible by opencv. -##Face detection 31/06/15## +##Face detection 31/06/16## To test the new cmake and code structure I **implemented face detection** (not recognition which is already available). Face detection is implemented in *OpenCV* both in **CPU** mode and in **GPU** mode (using **CUDA**). @@ -231,7 +231,7 @@ I added in the cmake the two new examples with a custom option and tested them s The next step will be to add the functionality previously described to the frame generator; I will also add the possibility to use standard containers as input sources in roder to be able to use arrays, vectors and lists for example. -##Face detection 2 06/07/15## +##Face detection 2 06/07/16## After talking with Kripa I found out that the CPU cascade detector (the detector used to detect faces) was already rpesent n the library; so only the gpu version had to be added. This implies to create in the library a new gpu folder containing all gpu modules which should be built only if support is present. @@ -283,13 +283,13 @@ There were three possible solutions to how to impement gpu in the library: I removed the previous facedetector classes which I had added and used the new ones in the examples which I had previoulsy created. I also used the new viewer in all examples and fixed the build of the different examples to be independent and clean in the *CMake*. -##Visualizer update 11/07/15## +##Visualizer update 11/07/16## To cover all examples and to have a more versatile viewer I added today an interface so add and remove text, clouds and added some improvments to be able to have multiple clouds in the same viewer at the same time. There is still some testing needed. I also added as usual all the template precompilation for the common PCL types. The next step consists in adding as stated before the std::containers to the frame generator class and then have a look if there are some modules to move to the gpu folder or maybe add some new methods there. -##Modular build 15/07/15## +##Modular build 15/07/16## Looking at the *PCL* and *OpenCV* libraries I noticed how the libraries are subdivided into modules. I understood then the importance of having this structure also in the OD library and so started to change a bit the structure, expecially the *Cmake* structure, to support such a feature. With the new changes now each detector is a separate module which can be built or not as also gpu. One issue was that some modules might have dependencies on other modules and can be built only if a specific module was built. For example the misc detector can be only built if other detectors where built. @@ -324,7 +324,7 @@ The same structure has been adde to all examples so that the correct examples ar I also built the system on lniux 16.04 with gcc 5.3; I had some initial issues but also those were fixed. I will make a blogpost about them next week since some errors where quite interesting. -##Modular build 2 and new interface for GPU 22/07/15## +##Modular build 2 and new interface for GPU 22/07/16## After discussing with my mentor, I did some changes to the code. @@ -369,7 +369,7 @@ The only small drawback of this structure is that the common module and the gpu_ Obviously I had to change all the examples to adapt to this new structure, but that was not so hard, while the new interface was quite tricky to be created in a clean way. -##Naming convention and build example 29/07/15## +##Naming convention and build example 29/07/16## I changed the nameing convention in the library to adapt it to a more C++ style. Before all file names started with **OD**, along with all classes and types. This is useful in C code since you are laking namespaces, but in C++ it makes no sense. I changed everything to the new style and fixed some compilation errors which arose. @@ -403,3 +403,9 @@ This shows that you can use the provided config file, which is created during in I am finishing now the Doxygen documentation which is missing in some parts of the lbirary and then i will finish to do also some template percompilation which I saw was missing. After that there is the need to integrate the other google summer of code Project. + +##Integrating the caffe gsoc project 02/08/16## + +I integrated the work from Abhishek, who did the other gsoc project in opendetection. I had to adapt the fiel structure and naming convention to the new struture and change some code to use shared pointers; there is still some work to do to adapt and clean the code which has many flaws, but thats something which should be done as part of the other gsoc project. Anyway I added two new options in the cmake: **WITH_CAFFE** and **WITH_GTKMM**; the first one enables to build the OD library with Caffe support, while the second one adds to caffe also the visualization part. If the flag **WITH_GPU** is added then the Caffe gpu part will be used. +I also restructured a bit the cmake structure adding some more searated cmake files and fixed the naming on the cmake files as for the other files. +THe only thing missing now is a bit of documentation and then everything should be done :) \ No newline at end of file diff --git a/doc/doxygen/tutorials_doxygen/writing_a_new_app.md b/doc/doxygen/tutorials_doxygen/writing_a_new_app.md index 78d8f54f..ded236a0 100644 --- a/doc/doxygen/tutorials_doxygen/writing_a_new_app.md +++ b/doc/doxygen/tutorials_doxygen/writing_a_new_app.md @@ -9,14 +9,17 @@ Writing your own app{#writing_a_new_app1} Put your c++ files in a suitable folder under example/apps. In the apps/CMakeLists.txt add the following line: OD_ADD_EXAMPLE(<name_of_app> FILES <source_files> - LINK_WITH od_common <od_detector_lib_list>) + LINK_WITH <od_detector_lib_list>) Where `<od_detector_lib_list>` is the list of OpenDetection libraries. Following are the currently available libraries for different detectors types: - + +* common: od_common * local2d: od_local_image_detector * global2d: od_global_image_detector * global3d: od_pointcloud_global_detector * miscellaneous: od_miscellaneous_detector +* gpu global2d: od_gpu_global_image_detector +* gpu common: od_gpu_common Have a look at the existing CMakeLists.txt in `examples/apps` or `examples/objectdetector` for sample. diff --git a/examples/objectdetector/cnn_mnist_classification.cpp b/examples/objectdetector/cnn_mnist_classification.cpp index 7c5897d5..09638d74 100644 --- a/examples/objectdetector/cnn_mnist_classification.cpp +++ b/examples/objectdetector/cnn_mnist_classification.cpp @@ -4,7 +4,7 @@ void help() { std::cout << "Usage: ./examples/objectdetector/od_cnn_mnist_classification <path to weight caffemodel file> <path to network file> <path to image file>" << std::endl; - std::cout << "Example: ./examples/objectdetector/od_cnn_mnist_classification ../examples/objectdetector/Mnist_Classify/mnist.caffemodel ../examples/objectdetector/Mnist_Classify/lenet.prototxt ../examples/objectdetector/Mnist_Classify/3.png" << std::endl << std::endl; + std::cout << "Example: ./examples/objectdetector/od_cnn_mnist_classification ../../../data/Mnist_Classify/mnist.caffemodel ../../../data/Mnist_Classify/lenet.prototxt ../../../data/Mnist_Classify/3.png" << std::endl << std::endl; exit(-1); } From a933d2ae9aab4e17c82a122c983fe7ec78367d44 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Tue, 2 Aug 2016 12:16:59 +0200 Subject: [PATCH 63/79] fixes framegenerator shared_ptr --- common/include/od/common/utils/FrameGenerator.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/include/od/common/utils/FrameGenerator.h b/common/include/od/common/utils/FrameGenerator.h index a94acff3..3dda333e 100644 --- a/common/include/od/common/utils/FrameGenerator.h +++ b/common/include/od/common/utils/FrameGenerator.h @@ -71,7 +71,7 @@ namespace od exhausted_ = true; cout << "Frame: " << file_list_[curr_image_] << endl; -#ifdef WITH_BOOST_SHARED_PTR +#ifndef WITH_BOOST_SHARED_PTR return shared_ptr<SceneT>(new SceneT(file_list_[curr_image_])); #else return make_shared<SceneT>(file_list_[curr_image_]); From 1e1dab5dc7cdfba7fd575b8d8726b03c57af2fd3 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Thu, 4 Aug 2016 16:30:32 +0200 Subject: [PATCH 64/79] fixes cmake link directories and fixes maybe svmlightlib branch --- .gitmodules | 1 + .../global2D/detection/ConvClassification.h | 10 +--------- examples/apps/CMakeLists.txt | 6 ++++++ examples/objectdetector/CMakeLists.txt | 17 +++++++++++++++-- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/.gitmodules b/.gitmodules index 68660bea..5e5c3827 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,4 @@ [submodule "3rdparty/svmlightlib"] path = 3rdparty/svmlightlib url = https://github.com/giacomodabisias/svmlightlib.git + branch = master diff --git a/detectors/include/od/detectors/global2D/detection/ConvClassification.h b/detectors/include/od/detectors/global2D/detection/ConvClassification.h index 99b5cde1..46cc6bfa 100644 --- a/detectors/include/od/detectors/global2D/detection/ConvClassification.h +++ b/detectors/include/od/detectors/global2D/detection/ConvClassification.h @@ -2,25 +2,17 @@ #include "od/common/pipeline/Detector.h" #include "od/common/pipeline/Scene.h" -#include "od/common/utils/Utils.h" -#include "od/common/utils/FeatureDetector2D.h" - +#include "od/common/utils/Shared_pointers.h" #include <opencv2/opencv.hpp> - -#include <cstring> -#include <cstdlib> #include <vector> - #include <string> #include <iostream> -#include <stdio.h> #include <caffe/caffe.hpp> #include <caffe/util/io.hpp> #include <caffe/blob.hpp> -#include "od/common/utils/Shared_pointers.h" namespace od { diff --git a/examples/apps/CMakeLists.txt b/examples/apps/CMakeLists.txt index a0665889..8737774a 100644 --- a/examples/apps/CMakeLists.txt +++ b/examples/apps/CMakeLists.txt @@ -10,6 +10,9 @@ if(cad_recog_test_single_model) include_directories(${CMAKE_3RDPARTY_DIR}/SiftGPU/src/SiftGPU/) include_directories(${PCL_INCLUDE_DIRS}) + link_directories(${PCL_LIBRARY_DIRS}) + link_directories(${OPENCV_LIBRARY_DIRS}) + OD_ADD_EXAMPLE(odcadrecog_test_single_model FILES cadrecog2D/test_single_db_single_model.cpp LINK_WITH od_local_image_detector @@ -28,6 +31,9 @@ if(od_multihog_app) include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) + link_directories(${PCL_LIBRARY_DIRS}) + link_directories(${OPENCV_LIBRARY_DIRS}) + OD_ADD_EXAMPLE(od_multihog_app FILES global2D/multihog_app.cpp LINK_WITH od_global_image_detector diff --git a/examples/objectdetector/CMakeLists.txt b/examples/objectdetector/CMakeLists.txt index f1939a27..fc5e0f9e 100644 --- a/examples/objectdetector/CMakeLists.txt +++ b/examples/objectdetector/CMakeLists.txt @@ -159,6 +159,8 @@ if(multialgo_files_example) include_directories(${COMMON_IMPL_DIR}) include_directories(${OpenCV_INCLUDE_DIRS}) + link_directories(${PCL_LIBRARY_DIRS}) + OD_ADD_EXAMPLE(od_multialgo_files FILES multialgo_files.cpp LINK_WITH od_misc_detector) @@ -180,6 +182,8 @@ if(multialgo_pc_example) include_directories(${COMMON_INCLUDE_DIR}) include_directories(${COMMON_IMPL_DIR}) + link_directories(${PCL_LIBRARY_DIRS}) + OD_ADD_EXAMPLE(od_multialgo_pc FILES multialgo_pc.cpp LINK_WITH od_misc_detector) @@ -252,6 +256,8 @@ if(pc_global_example) include_directories(${DETECTORS_IMPL_DIR}) include_directories(${COMMON_INCLUDE_DIR}) + link_directories(${PCL_LIBRARY_DIRS}) + OD_ADD_EXAMPLE(od_example_pc_global FILES pc_global.cpp LINK_WITH od_pointcloud_global_detector) @@ -269,6 +275,9 @@ if(pc_global_files_example) include_directories(${DETECTORS_IMPL_DIR}) include_directories(${COMMON_INCLUDE_DIR}) + link_directories(${PCL_LIBRARY_DIRS}) + link_directories(${OPENCV_LIBRARY_DIRS}) + OD_ADD_EXAMPLE(od_example_pc_global_files FILES pc_global_files.cpp LINK_WITH od_pointcloud_global_detector) @@ -286,6 +295,8 @@ if(pc_global_real_time_example) include_directories(${DETECTORS_IMPL_DIR}) include_directories(${COMMON_INCLUDE_DIR}) + link_directories(${PCL_LIBRARY_DIRS}) + OD_ADD_EXAMPLE(od_example_pc_global_real_time FILES pc_global_real_time.cpp LINK_WITH od_pointcloud_global_detector) @@ -302,6 +313,8 @@ if(framegenerator_example) include_directories(${COMMON_IMPL_DIR}) include_directories(${PCL_INCLUDE_DIRS}) + link_directories(${PCL_LIBRARY_DIRS}) + OD_ADD_EXAMPLE(od_framegenerator FILES framegenerator.cpp LINK_WITH od_common) @@ -331,7 +344,7 @@ endif(Caffe_FOUND) ################################################################################## option(cnn_train_custom "Build the cnn training custom example" ON) -if(Caffe_FOUND AND GTKMM_FOUND) +if(Caffe_FOUND AND WITH_GTKMM AND GTKMM_FOUND) if(cnn_train_custom) if(TARGET od_global_image_detector) @@ -347,7 +360,7 @@ if(Caffe_FOUND AND GTKMM_FOUND) endif(cnn_train_custom) else() message("!!! cnn_train_custom is set to on but CAFFE or GTKMM were not found") -endif(Caffe_FOUND AND GTKMM_FOUND) +endif(Caffe_FOUND AND WITH_GTKMM AND GTKMM_FOUND) ################################################################################## option(cnn_training "Build the cnn training example" ON) From f0a3f85d02f749b7dc3e0fa39c73b6c88fbb72b2 Mon Sep 17 00:00:00 2001 From: giacomo <giacomo.dabisias@gmail.com> Date: Sat, 20 Aug 2016 17:05:55 +0200 Subject: [PATCH 65/79] adds abhisheks work except examples --- .../global2D/annotation/Annotation.h | 119 ++ .../detectors/global2D/annotation/Annotator.h | 27 + .../global2D/detection/ConvClassification.h | 22 +- .../global2D/training/DisplayWindow.h | 17 +- .../detectors/global2D/training/ExtraWindow.h | 956 ++++++++++- .../detectors/global2D/training/MainWindow.h | 20 + .../od/detectors/global2D/training/Network.h | 76 +- .../od/detectors/global2D/training/Node.h | 36 +- detectors/src/global2D/CMakeLists.txt | 10 +- .../src/global2D/annotation/Annotation.cpp | 1465 +++++++++++++++++ .../src/global2D/annotation/Annotator.cpp | 20 + .../global2D/detection/ConvClassification.cpp | 141 ++ detectors/src/global2D/training/Network.cpp | 872 +++++++++- detectors/src/global2D/training/Solver.cpp | 87 +- 14 files changed, 3808 insertions(+), 60 deletions(-) create mode 100644 detectors/include/od/detectors/global2D/annotation/Annotation.h create mode 100644 detectors/include/od/detectors/global2D/annotation/Annotator.h create mode 100644 detectors/src/global2D/annotation/Annotation.cpp create mode 100644 detectors/src/global2D/annotation/Annotator.cpp diff --git a/detectors/include/od/detectors/global2D/annotation/Annotation.h b/detectors/include/od/detectors/global2D/annotation/Annotation.h new file mode 100644 index 00000000..ed052af0 --- /dev/null +++ b/detectors/include/od/detectors/global2D/annotation/Annotation.h @@ -0,0 +1,119 @@ +#pragma once + +#include <gtkmm.h> +#include <gtkmm/grid.h> +#include <gtkmm/entry.h> +#include <gtkmm/button.h> +#include <gtkmm/radiobutton.h> +#include <gtkmm/messagedialog.h> +#include <gtkmm/window.h> +#include <gtkmm/scrolledwindow.h> +#include <gtkmm/application.h> +#include <gtkmm/comboboxtext.h> +#include <gtkmm/liststore.h> +#include <gtkmm/textview.h> +#include <iostream> +#include <fstream> +#include <sstream> +#include <vector> +#include <dirent.h> + +#include "opencv2/objdetect/objdetect.hpp" +#include "opencv2/highgui/highgui.hpp" +#include "opencv2/imgproc/imgproc.hpp" +#include "opencv2/core/core.hpp" + +#include <cmath> +#include <cstdio> +#include <vector> +#include <iostream> +#include <algorithm> +#include <stdio.h> +#include <math.h> +#include <stdlib.h> +#include <time.h> +#include <cstdlib> +#include <iomanip> +#include <fstream> +#include <ctime> +#include <cstring> +#include <sstream> +#include <string> + + +namespace od +{ + namespace g2d + { + + class Annotation : public Gtk::Window + { + public: + Annotation(); + virtual ~Annotation(); + + protected: + void on_button_clicked(Glib::ustring data); + void on_combo_changed(); + void on_cell_data_extra(const Gtk::TreeModel::const_iterator& iter); + void showWindow_main(); + void showWindow_imageLoad(Glib::ustring data); + bool on_button_press_event(GdkEventButton *event); + void createDot(int x, int y); + void loadOriginalImage(std::string data, bool st); + void createVisualROI(int xPressed, int yPressed, int xReleased, int yReleased); + void loadOriginalImageWithSavedMarkings(std::string filename, std::vector<std::vector<int> > & storageROILocationMultiple, bool st); + void loadResetedMarkings(std::string data, std::vector<std::vector<int> > & roi, bool st); + // Child widgets: + Gtk::Grid m_grid1, + m_grid_imageLoad; + + Gtk::ScrolledWindow m_sw1, + m_sw_imageLoad; + + Gtk::Button button_selectImageFileName, + button_loadImage, + button_loadAnotherImage, + button_resetMarkings, + button_loadMainWindow, + button_selectRoi, + button_saveMarked, + button_saveCropMarked, + button_resetMarkingsCurrent, + button_selectRoiCurrent, + button_saveMarkedMultiple, + button_selectDatasetFolder, + button_saveCropMarkedMultiple, + button_loadNextImage, + button_resetSegnetMaskCurrent, + button_selectSegnetMaskCurrent, + button_saveSegnetMask, + button_quit; + + Gtk::RadioButton rbutton_markBB, rbutton_cropBB, rbutton_markBBWithLabel, + rbutton_cropMultipleBB, rbutton_segnetMaskedBased; + + Gtk::Label label_annotationType, + label_outputFile, + label_annotationLabel; + + Gtk::Entry text_outputFile, + text_annotationLabel; + + private: + Glib::ustring imageFileLocation; + std::string filename, foldername, file_folder; + Gtk::Image image; + double imgFocusX,imgFocusY, scale; + int lastXMouse, lastYMouse, xPressed, yPressed, xReleased, yReleased, storage, imagesInFolder, storageID; + bool resetFlag, moveFlag, targetFlag, status; + + std::string currentWindow; + std::vector<std::string> storageFileName, imageFileNames; + std::vector<float> widthMultiplier, heightMultiplier; + std::vector<std::vector<int> > storageROILocationCurrent, roiPointsForMask, roiPointsForMaskPermanent, storageROILocation; + float w,h; + }; + + } +} \ No newline at end of file diff --git a/detectors/include/od/detectors/global2D/annotation/Annotator.h b/detectors/include/od/detectors/global2D/annotation/Annotator.h new file mode 100644 index 00000000..00533516 --- /dev/null +++ b/detectors/include/od/detectors/global2D/annotation/Annotator.h @@ -0,0 +1,27 @@ +#pragma once + +#include "od/common/pipeline/Trainer.h" +#include "od/common/utils/Utils.h" +#include <iostream> + +#include "Annotation.h" + +namespace od +{ + namespace g2d + { + + class Annotator : public Trainer + { + public: + + Annotator(const std::string & training_input_location_ = std::string(""), const std::string & trained_data_location_ = std::string("")): + Trainer(training_input_location_, trained_data_location_){} + + int train(); + void init(){} + void startAnnotator(int argc, char *argv[]); + }; + } +} + diff --git a/detectors/include/od/detectors/global2D/detection/ConvClassification.h b/detectors/include/od/detectors/global2D/detection/ConvClassification.h index 46cc6bfa..0ab5a132 100644 --- a/detectors/include/od/detectors/global2D/detection/ConvClassification.h +++ b/detectors/include/od/detectors/global2D/detection/ConvClassification.h @@ -27,27 +27,43 @@ namespace od void setWeightModelFileLocation(const std::string & location); void setNetworkModelFileLocation(const std::string & location); void setImageFileLocation(const std::string & location); + void setOutputFileLocation(const std::string & location); std::string getWeightModelFileLocation(); std::string getNetworkModelFileLocation(); std::string getImageFileLocation(); + std::vector<float> classifyMultiLabel(); void setTestBlob(int num_channels, int img_height, int img_width); void classify(); + //Multiclass classification . Note: Code Partially taken from caffe library + std::vector<float> runMultiClassClassifier(); + int runMultiClassClassifierPythonMode(); + std::vector<float> Predict(const cv::Mat & img); + void WrapInputLayer(std::vector<cv::Mat> & input_channels); + + //Segnet classification + void setSegnetLocation(const std::string & location); + void setImageGroundTruthFileLocation(const std::string & location); + void setColorLocation(const std::string & location); + int runSegnetBasedClassifierPythonMode(); + void init(); shared_ptr<Detections2D> detectOmni(shared_ptr<SceneImage> scene); shared_ptr<Detections> detect(shared_ptr<SceneImage> scene); shared_ptr<Detections> detectOmni(shared_ptr<Scene> scene); private: - std::string weightModelFileLoaction; - std::string networkFileLocation; - std::string imageFileLocation; + std::string imageFileLocation, outputFileLocation, networkFileLocation, weightModelFileLoaction, segnetLocation, colorLocation, imageGroundTruthFileLocation; caffe::Datum strucBlob; caffe::BlobProto protoBlob; std::vector<caffe::Blob<float>*> inputBlob; + cv::Size input_geometry_; + int num_channels_; + + }; } } diff --git a/detectors/include/od/detectors/global2D/training/DisplayWindow.h b/detectors/include/od/detectors/global2D/training/DisplayWindow.h index 3566ccd2..7de1920a 100644 --- a/detectors/include/od/detectors/global2D/training/DisplayWindow.h +++ b/detectors/include/od/detectors/global2D/training/DisplayWindow.h @@ -8,7 +8,20 @@ void NetworkCreator::showWindow_displayWindow() add(box_fullCnnLayerMatter); buffer_fullCnnLayerMatter->set_text(fullCnnLayerMatter); textView_fullCnnLayerMatter.set_buffer(buffer_fullCnnLayerMatter); + + ref_currentLayers->clear(); + combo_currentLayers.set_model(ref_currentLayers); + Gtk::TreeModel::Row row_currentLayers = *(ref_currentLayers->append()); + row_currentLayers[column_currentLayers.m_col_id] = 0; + row_currentLayers[column_currentLayers.m_col_name] = "Layers"; + row_currentLayers[column_currentLayers.m_col_extra] = "All Layers"; + combo_currentLayers.set_active(row_currentLayers); + for(unsigned int i = 0; i < numLayers; i++) + { + Gtk::TreeModel::Row row_currentLayers = *(ref_currentLayers->append()); + row_currentLayers[column_currentLayers.m_col_id] = i + 1; + row_currentLayers[column_currentLayers.m_col_name] = fullCnnLayersName[i]; + } + show_all_children(); } - - diff --git a/detectors/include/od/detectors/global2D/training/ExtraWindow.h b/detectors/include/od/detectors/global2D/training/ExtraWindow.h index 17c0e38e..4014660d 100644 --- a/detectors/include/od/detectors/global2D/training/ExtraWindow.h +++ b/detectors/include/od/detectors/global2D/training/ExtraWindow.h @@ -1,5 +1,6 @@ #include "od/detectors/global2D/training/Network.h" + void NetworkCreator::showWindow_extraLayerType(Glib::ustring data) { remove(); @@ -9,7 +10,12 @@ void NetworkCreator::showWindow_extraLayerType(Glib::ustring data) m_grid_extraLayerType.set_column_spacing (10); m_grid_extraLayerType.set_row_spacing (50); - title_extraLayerType.set_text("Will be updated soon"); + if(data == "" or data == "ArgMax") + title_extraLayerType.set_text("Set the Properties of Loss Layer type: ArgMax"); + else if(data == "BNLL" or data == "Eltwise" or data == "ImageData" or data == "Data" or data == "HDF5Data") + title_extraLayerType.set_text("Set the Properties of Loss Layer type: " + data); + else + title_extraLayerType.set_text("Will be updated soon"); title_extraLayerType.set_line_wrap(); title_extraLayerType.set_justify(Gtk::JUSTIFY_FILL); // m_grid_extraLayerType.attach(title_extraLayerType,0,0,2,1); @@ -17,6 +23,954 @@ void NetworkCreator::showWindow_extraLayerType(Glib::ustring data) button_addMoreLayer5.show(); + + if(data == "" or data == "ArgMax") + { + button_setExtraParameters.hide(); + label_extraLayerBottom1.hide(); + label_extraLayerTop1.hide(); + label_extraLayerTop2.hide(); + label_extraLayerName.hide(); + text_extraLayerBottom1.hide(); + text_extraLayerTop1.hide(); + text_extraLayerTop2.hide(); + text_extraLayerName.hide(); + text_extraLayerTopK.hide(); + label_extraLayerTopK.hide(); + label_extraLayerOutMaxVal.hide(); + rbutton_extraLayerOutMaxValTrue.hide(); + rbutton_extraLayerOutMaxValFalse.hide(); + label_extraLayerBottom2.hide(); + text_extraLayerBottom2.hide(); + label_extraLayerPhase.hide(); + rbutton_extraLayerTrain.hide(); + rbutton_extraLayerTest.hide(); + label_extraLayerScale.hide(); + label_extraLayerNewHeight.hide(); + label_extraLayerNewWidth.hide(); + label_extraLayerCropSize.hide(); + label_extraLayerMeanFile.hide(); + rbutton_extraLayerScaleYes.hide(); + rbutton_extraLayerScaleNo.hide(); + rbutton_extraLayerNewHeightYes.hide(); + rbutton_extraLayerNewHeightNo.hide(); + rbutton_extraLayerNewWidthYes.hide(); + rbutton_extraLayerNewWidthNo.hide(); + rbutton_extraLayerCropSizeYes.hide(); + rbutton_extraLayerCropSizeNo.hide(); + rbutton_extraLayerMeanFileYes.hide(); + rbutton_extraLayerMeanFileNo.hide(); + text_extraLayerScale.hide(); + text_extraLayerNewHeight.hide(); + text_extraLayerNewWidth.hide(); + text_extraLayerCropSize.hide(); + text_extraLayerMeanFile.hide(); + label_extraLayerSource.hide(); + label_extraLayerBatchSize.hide(); + text_extraLayerSource.hide(); + text_extraLayerBatchSize.hide(); + label_extraLayerBackend.hide(); + rbutton_extraLayerLMDB.hide(); + rbutton_extraLayerLEVELDB.hide(); + + + label_extraLayerBottom1.set_text("Bottom1 Layer Name: "); + label_extraLayerBottom1.set_line_wrap(); + label_extraLayerBottom1.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerBottom1,0,1,2,1); + label_extraLayerBottom1.show(); + + text_extraLayerBottom1.set_max_length(100); + text_extraLayerBottom1.set_text(""); + text_extraLayerBottom1.select_region(0, text_extraLayerBottom1.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerBottom1,2,1,1,1); + text_extraLayerBottom1.show(); + + label_extraLayerTop1.set_text("Top1 Layer Name: "); + label_extraLayerTop1.set_line_wrap(); + label_extraLayerTop1.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerTop1,0,2,2,1); + label_extraLayerTop1.show(); + + text_extraLayerTop1.set_max_length(100); + text_extraLayerTop1.set_text(""); + text_extraLayerTop1.select_region(0, text_extraLayerTop1.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerTop1,2,2,1,1); + text_extraLayerTop1.show(); + + label_extraLayerName.set_text("Current Layer Name: "); + label_extraLayerName.set_line_wrap(); + label_extraLayerName.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerName,0,3,2,1); + label_extraLayerName.show(); + + text_extraLayerName.set_max_length(100); + text_extraLayerName.set_text(""); + text_extraLayerName.select_region(0, text_extraLayerName.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerName,2,3,1,1); + text_extraLayerName.show(); + + label_extraLayerTopK.set_text("top_k: \n(Top K Classifications)"); + label_extraLayerTopK.set_line_wrap(); + label_extraLayerTopK.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerTopK,0,4,2,1); + label_extraLayerTopK.show(); + + text_extraLayerTopK.set_max_length(100); + text_extraLayerTopK.set_text(""); + text_extraLayerTopK.select_region(0, text_extraLayerTopK.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerName,2,4,1,1); + text_extraLayerTopK.show(); + + label_extraLayerOutMaxVal.set_text("out_max_val: \n(if true returns pair \n{max_index, max_value} the input)"); + label_extraLayerOutMaxVal.set_line_wrap(); + label_extraLayerOutMaxVal.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerOutMaxVal,0,5,2,1); + label_extraLayerOutMaxVal.show(); + +// Gtk::RadioButton::Group group8 = rbutton_extraLayerOutMaxValTrue.get_group(); +// rbutton_extraLayerOutMaxValFalse.set_group(group8); + rbutton_extraLayerOutMaxValTrue.set_active(); +// m_grid_lossLayerType.attach(rbutton_extraLayerOutMaxValTrue,2,5,1,1); + rbutton_extraLayerOutMaxValTrue.show(); +// m_grid_lossLayerType.attach(rbutton_extraLayerOutMaxValFalse,3,5,1,1); + rbutton_extraLayerOutMaxValFalse.show(); + + button_setExtraParameters.show(); + } + else if(data == "BNLL") + { + button_setExtraParameters.hide(); + label_extraLayerBottom1.hide(); + label_extraLayerTop1.hide(); + label_extraLayerTop2.hide(); + label_extraLayerName.hide(); + text_extraLayerBottom1.hide(); + text_extraLayerTop1.hide(); + text_extraLayerTop2.hide(); + text_extraLayerName.hide(); + text_extraLayerTopK.hide(); + label_extraLayerTopK.hide(); + label_extraLayerOutMaxVal.hide(); + rbutton_extraLayerOutMaxValTrue.hide(); + rbutton_extraLayerOutMaxValFalse.hide(); + label_extraLayerBottom2.hide(); + text_extraLayerBottom2.hide(); + label_extraLayerPhase.hide(); + rbutton_extraLayerTrain.hide(); + rbutton_extraLayerTest.hide(); + label_extraLayerScale.hide(); + label_extraLayerNewHeight.hide(); + label_extraLayerNewWidth.hide(); + label_extraLayerCropSize.hide(); + label_extraLayerMeanFile.hide(); + rbutton_extraLayerScaleYes.hide(); + rbutton_extraLayerScaleNo.hide(); + rbutton_extraLayerNewHeightYes.hide(); + rbutton_extraLayerNewHeightNo.hide(); + rbutton_extraLayerNewWidthYes.hide(); + rbutton_extraLayerNewWidthNo.hide(); + rbutton_extraLayerCropSizeYes.hide(); + rbutton_extraLayerCropSizeNo.hide(); + rbutton_extraLayerMeanFileYes.hide(); + rbutton_extraLayerMeanFileNo.hide(); + text_extraLayerScale.hide(); + text_extraLayerNewHeight.hide(); + text_extraLayerNewWidth.hide(); + text_extraLayerCropSize.hide(); + text_extraLayerMeanFile.hide(); + label_extraLayerSource.hide(); + label_extraLayerBatchSize.hide(); + text_extraLayerSource.hide(); + text_extraLayerBatchSize.hide(); + label_extraLayerBackend.hide(); + rbutton_extraLayerLMDB.hide(); + rbutton_extraLayerLEVELDB.hide(); + + label_extraLayerBottom1.set_text("Bottom1 Layer Name: "); + label_extraLayerBottom1.set_line_wrap(); + label_extraLayerBottom1.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerBottom1,0,1,2,1); + label_extraLayerBottom1.show(); + + text_extraLayerBottom1.set_max_length(100); + text_extraLayerBottom1.set_text(""); + text_extraLayerBottom1.select_region(0, text_extraLayerBottom1.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerBottom1,2,1,1,1); + text_extraLayerBottom1.show(); + + label_extraLayerTop1.set_text("Top1 Layer Name: "); + label_extraLayerTop1.set_line_wrap(); + label_extraLayerTop1.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerTop1,0,2,2,1); + label_extraLayerTop1.show(); + + text_extraLayerTop1.set_max_length(100); + text_extraLayerTop1.set_text(""); + text_extraLayerTop1.select_region(0, text_extraLayerTop1.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerTop1,2,2,1,1); + text_extraLayerTop1.show(); + + label_extraLayerName.set_text("Current Layer Name: "); + label_extraLayerName.set_line_wrap(); + label_extraLayerName.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerName,0,3,2,1); + label_extraLayerName.show(); + + text_extraLayerName.set_max_length(100); + text_extraLayerName.set_text(""); + text_extraLayerName.select_region(0, text_extraLayerName.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerName,2,3,1,1); + text_extraLayerName.show(); + + button_setExtraParameters.show(); + } + else if(data == "Eltwise") + { + button_setExtraParameters.hide(); + label_extraLayerBottom1.hide(); + label_extraLayerTop1.hide(); + label_extraLayerTop2.hide(); + label_extraLayerName.hide(); + text_extraLayerBottom1.hide(); + text_extraLayerTop1.hide(); + text_extraLayerTop2.hide(); + text_extraLayerName.hide(); + text_extraLayerTopK.hide(); + label_extraLayerTopK.hide(); + label_extraLayerOutMaxVal.hide(); + rbutton_extraLayerOutMaxValTrue.hide(); + rbutton_extraLayerOutMaxValFalse.hide(); + label_extraLayerBottom2.hide(); + text_extraLayerBottom2.hide(); + label_extraLayerPhase.hide(); + rbutton_extraLayerTrain.hide(); + rbutton_extraLayerTest.hide(); + label_extraLayerScale.hide(); + label_extraLayerNewHeight.hide(); + label_extraLayerNewWidth.hide(); + label_extraLayerCropSize.hide(); + label_extraLayerMeanFile.hide(); + rbutton_extraLayerScaleYes.hide(); + rbutton_extraLayerScaleNo.hide(); + rbutton_extraLayerNewHeightYes.hide(); + rbutton_extraLayerNewHeightNo.hide(); + rbutton_extraLayerNewWidthYes.hide(); + rbutton_extraLayerNewWidthNo.hide(); + rbutton_extraLayerCropSizeYes.hide(); + rbutton_extraLayerCropSizeNo.hide(); + rbutton_extraLayerMeanFileYes.hide(); + rbutton_extraLayerMeanFileNo.hide(); + text_extraLayerScale.hide(); + text_extraLayerNewHeight.hide(); + text_extraLayerNewWidth.hide(); + text_extraLayerCropSize.hide(); + text_extraLayerMeanFile.hide(); + label_extraLayerSource.hide(); + label_extraLayerBatchSize.hide(); + text_extraLayerSource.hide(); + text_extraLayerBatchSize.hide(); + label_extraLayerBackend.hide(); + rbutton_extraLayerLMDB.hide(); + rbutton_extraLayerLEVELDB.hide(); + + + label_extraLayerBottom1.set_text("Bottom1 Layer Name: "); + label_extraLayerBottom1.set_line_wrap(); + label_extraLayerBottom1.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerBottom1,0,1,2,1); + label_extraLayerBottom1.show(); + + text_extraLayerBottom1.set_max_length(100); + text_extraLayerBottom1.set_text(""); + text_extraLayerBottom1.select_region(0, text_extraLayerBottom1.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerBottom1,2,1,1,1); + text_extraLayerBottom1.show(); + + label_extraLayerBottom2.set_text("Bottom2 Layer Name: "); + label_extraLayerBottom2.set_line_wrap(); + label_extraLayerBottom2.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerBottom2,0,2,2,1); + label_extraLayerBottom2.show(); + + text_extraLayerBottom2.set_max_length(100); + text_extraLayerBottom2.set_text(""); + text_extraLayerBottom2.select_region(0, text_extraLayerBottom2.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerBottom2,2,2,1,1); + text_extraLayerBottom2.show(); + + label_extraLayerTop1.set_text("Top1 Layer Name: "); + label_extraLayerTop1.set_line_wrap(); + label_extraLayerTop1.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerTop1,0,2,2,1); + label_extraLayerTop1.show(); + + text_extraLayerTop1.set_max_length(100); + text_extraLayerTop1.set_text(""); + text_extraLayerTop1.select_region(0, text_extraLayerTop1.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerTop1,2,2,1,1); + text_extraLayerTop1.show(); + + label_extraLayerName.set_text("Current Layer Name: "); + label_extraLayerName.set_line_wrap(); + label_extraLayerName.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerName,0,3,2,1); + label_extraLayerName.show(); + + text_extraLayerName.set_max_length(100); + text_extraLayerName.set_text(""); + text_extraLayerName.select_region(0, text_extraLayerName.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerName,2,3,1,1); + text_extraLayerName.show(); + + button_setExtraParameters.show(); + } + else if(data == "ImageData") + { + button_setExtraParameters.hide(); + label_extraLayerBottom1.hide(); + label_extraLayerTop1.hide(); + label_extraLayerTop2.hide(); + label_extraLayerName.hide(); + text_extraLayerBottom1.hide(); + text_extraLayerTop1.hide(); + text_extraLayerTop2.hide(); + text_extraLayerName.hide(); + text_extraLayerTopK.hide(); + label_extraLayerTopK.hide(); + label_extraLayerOutMaxVal.hide(); + rbutton_extraLayerOutMaxValTrue.hide(); + rbutton_extraLayerOutMaxValFalse.hide(); + label_extraLayerBottom2.hide(); + text_extraLayerBottom2.hide(); + label_extraLayerPhase.hide(); + rbutton_extraLayerTrain.hide(); + rbutton_extraLayerTest.hide(); + label_extraLayerScale.hide(); + label_extraLayerNewHeight.hide(); + label_extraLayerNewWidth.hide(); + label_extraLayerCropSize.hide(); + label_extraLayerMeanFile.hide(); + rbutton_extraLayerScaleYes.hide(); + rbutton_extraLayerScaleNo.hide(); + rbutton_extraLayerNewHeightYes.hide(); + rbutton_extraLayerNewHeightNo.hide(); + rbutton_extraLayerNewWidthYes.hide(); + rbutton_extraLayerNewWidthNo.hide(); + rbutton_extraLayerCropSizeYes.hide(); + rbutton_extraLayerCropSizeNo.hide(); + rbutton_extraLayerMeanFileYes.hide(); + rbutton_extraLayerMeanFileNo.hide(); + text_extraLayerScale.hide(); + text_extraLayerNewHeight.hide(); + text_extraLayerNewWidth.hide(); + text_extraLayerCropSize.hide(); + text_extraLayerMeanFile.hide(); + label_extraLayerSource.hide(); + label_extraLayerBatchSize.hide(); + text_extraLayerSource.hide(); + text_extraLayerBatchSize.hide(); + label_extraLayerBackend.hide(); + rbutton_extraLayerLMDB.hide(); + rbutton_extraLayerLEVELDB.hide(); + + + label_extraLayerTop1.set_text("Top1 Layer Name: "); + label_extraLayerTop1.set_line_wrap(); + label_extraLayerTop1.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerTop1,0,2,2,1); + label_extraLayerTop1.show(); + + text_extraLayerTop1.set_max_length(100); + text_extraLayerTop1.set_text(""); + text_extraLayerTop1.select_region(0, text_extraLayerTop1.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerTop1,2,2,1,1); + text_extraLayerTop1.show(); + + label_extraLayerTop2.set_text("Top2 Layer Name: "); + label_extraLayerTop2.set_line_wrap(); + label_extraLayerTop2.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerTop2,0,4,2,1); + label_extraLayerTop2.show(); + + text_extraLayerTop2.set_max_length(100); + text_extraLayerTop2.set_text(""); + text_extraLayerTop2.select_region(0, text_extraLayerTop2.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerTop2,2,4,1,1); + text_extraLayerTop2.show(); + + label_extraLayerName.set_text("Current Layer Name: "); + label_extraLayerName.set_line_wrap(); + label_extraLayerName.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerName,0,3,2,1); + label_extraLayerName.show(); + + text_extraLayerName.set_max_length(100); + text_extraLayerName.set_text(""); + text_extraLayerName.select_region(0, text_extraLayerName.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerName,2,3,1,1); + text_extraLayerName.show(); + + label_extraLayerPhase.set_text("phase: "); + label_extraLayerPhase.set_line_wrap(); + label_extraLayerPhase.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerPhase,0,8,2,1); + label_extraLayerPhase.show(); + +// Gtk::RadioButton::Group group9 = rbutton_extraLayerTrain.get_group(); +// rbutton_extraLayerTest.set_group(group9); +// rbutton_extraLayerTrain.set_active(); +// m_grid_extraLayerType.attach(rbutton_extraLayerTrain,2,8,1,1); + rbutton_extraLayerTrain.show(); +// m_grid_extraLayerType.attach(rbutton_extraLayerTest,3,8,1,1); + rbutton_extraLayerTest.show(); + + label_extraLayerScale.set_text("scale: "); + label_extraLayerScale.set_line_wrap(); + label_extraLayerScale.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerScale,0,9,2,1); + label_extraLayerScale.show(); + +// Gtk::RadioButton::Group group10 = rbutton_extraLayerScaleYes.get_group(); +// rbutton_extraLayerScaleNo.set_group(group10); + rbutton_extraLayerScaleNo.set_active(); +// m_grid_extraLayerType.attach(rbutton_extraLayerScaleYes,2,9,1,1); + rbutton_extraLayerScaleYes.show(); +// m_grid_extraLayerType.attach(rbutton_extraLayerScaleNo,3,9,1,1); + rbutton_extraLayerScaleNo.show(); + + text_extraLayerScale.set_max_length(100); + text_extraLayerScale.set_text(""); + text_extraLayerScale.select_region(0, text_extraLayerScale.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerScale,4,9,1,1); + text_extraLayerScale.show(); + + label_extraLayerNewHeight.set_text("new_height: "); + label_extraLayerNewHeight.set_line_wrap(); + label_extraLayerNewHeight.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerNewHeight,0,10,2,1); + label_extraLayerNewHeight.show(); + +// Gtk::RadioButton::Group group11 = rbutton_extraLayerNewHeightYes.get_group(); +// rbutton_extraLayerNewHeightNo.set_group(group11); + rbutton_extraLayerNewHeightNo.set_active(); +// m_grid_extraLayerType.attach(rbutton_extraLayerNewHeightYes,2,10,1,1); + rbutton_extraLayerNewHeightYes.show(); +// m_grid_extraLayerType.attach(rbutton_extraLayerNewHeightNo,3,10,1,1); + rbutton_extraLayerNewHeightNo.show(); + + text_extraLayerNewHeight.set_max_length(100); + text_extraLayerNewHeight.set_text(""); + text_extraLayerNewHeight.select_region(0, text_extraLayerNewHeight.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerNewHeight,4,10,1,1); + text_extraLayerNewHeight.show(); + + label_extraLayerNewWidth.set_text("new_width: "); + label_extraLayerNewWidth.set_line_wrap(); + label_extraLayerNewWidth.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerNewWidth,0,11,2,1); + label_extraLayerNewWidth.show(); + +// Gtk::RadioButton::Group group12 = rbutton_extraLayerNewWidthYes.get_group(); +// rbutton_extraLayerNewWidthNo.set_group(group12); + rbutton_extraLayerNewWidthNo.set_active(); +// m_grid_extraLayerType.attach(rbutton_extraLayerNewWidthYes,2,11,1,1); + rbutton_extraLayerNewWidthYes.show(); +// m_grid_extraLayerType.attach(rbutton_extraLayerNewWidthNo,3,11,1,1); + rbutton_extraLayerNewWidthNo.show(); + + text_extraLayerNewWidth.set_max_length(100); + text_extraLayerNewWidth.set_text(""); + text_extraLayerNewWidth.select_region(0, text_extraLayerNewWidth.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerNewWidth,4,11,1,1); + text_extraLayerNewWidth.show(); + + label_extraLayerCropSize.set_text("crop_size: "); + label_extraLayerCropSize.set_line_wrap(); + label_extraLayerCropSize.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerCropSize,0,12,2,1); + label_extraLayerCropSize.show(); + +// Gtk::RadioButton::Group group13 = rbutton_extraLayerCropSizeYes.get_group(); +// rbutton_extraLayerCropSizeNo.set_group(group13); + rbutton_extraLayerCropSizeNo.set_active(); +// m_grid_extraLayerType.attach(rbutton_extraLayerCropSizeYes,2,12,1,1); + rbutton_extraLayerCropSizeYes.show(); +// m_grid_extraLayerType.attach(rbutton_extraLayerCropSizeNo,3,12,1,1); + rbutton_extraLayerCropSizeNo.show(); + + text_extraLayerCropSize.set_max_length(100); + text_extraLayerCropSize.set_text(""); + text_extraLayerCropSize.select_region(0, text_extraLayerCropSize.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerCropSize,4,12,1,1); + text_extraLayerCropSize.show(); + + label_extraLayerMeanFile.set_text("mean_file: "); + label_extraLayerMeanFile.set_line_wrap(); + label_extraLayerMeanFile.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerMeanFile,0,13,2,1); + label_extraLayerMeanFile.show(); + +// Gtk::RadioButton::Group group14 = rbutton_extraLayerMeanFileYes.get_group(); +// rbutton_extraLayerMeanFileNo.set_group(group14); + rbutton_extraLayerMeanFileNo.set_active(); +// m_grid_extraLayerType.attach(rbutton_extraLayerMeanFileYes,2,13,1,1); + rbutton_extraLayerMeanFileYes.show(); +// m_grid_extraLayerType.attach(rbutton_extraLayerMeanFileNo,3,13,1,1); + rbutton_extraLayerMeanFileNo.show(); + + text_extraLayerMeanFile.set_max_length(100); + text_extraLayerMeanFile.set_text(""); + text_extraLayerMeanFile.select_region(0, text_extraLayerMeanFile.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerMeanFile,4,13,1,1); + text_extraLayerMeanFile.show(); + + label_extraLayerSource.set_text("source: "); + label_extraLayerSource.set_line_wrap(); + label_extraLayerSource.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerSource,0,14,2,1); + label_extraLayerSource.show(); + + text_extraLayerSource.set_max_length(100); + text_extraLayerSource.set_text(""); + text_extraLayerSource.select_region(0, text_extraLayerSource.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerSource,2,14,1,1); + text_extraLayerSource.show(); + + label_extraLayerBatchSize.set_text("batch_size: "); + label_extraLayerBatchSize.set_line_wrap(); + label_extraLayerBatchSize.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerBatchSize,0,15,2,1); + label_extraLayerBatchSize.show(); + + text_extraLayerBatchSize.set_max_length(100); + text_extraLayerBatchSize.set_text(""); + text_extraLayerBatchSize.select_region(0, text_extraLayerBatchSize.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerBatchSize,2,15,1,1); + text_extraLayerBatchSize.show(); + + button_setExtraParameters.show(); + } + else if(data == "Data") + { + button_setExtraParameters.hide(); + label_extraLayerBottom1.hide(); + label_extraLayerTop1.hide(); + label_extraLayerTop2.hide(); + label_extraLayerName.hide(); + text_extraLayerBottom1.hide(); + text_extraLayerTop1.hide(); + text_extraLayerTop2.hide(); + text_extraLayerName.hide(); + text_extraLayerTopK.hide(); + label_extraLayerTopK.hide(); + label_extraLayerOutMaxVal.hide(); + rbutton_extraLayerOutMaxValTrue.hide(); + rbutton_extraLayerOutMaxValFalse.hide(); + label_extraLayerBottom2.hide(); + text_extraLayerBottom2.hide(); + label_extraLayerPhase.hide(); + rbutton_extraLayerTrain.hide(); + rbutton_extraLayerTest.hide(); + label_extraLayerScale.hide(); + label_extraLayerNewHeight.hide(); + label_extraLayerNewWidth.hide(); + label_extraLayerCropSize.hide(); + label_extraLayerMeanFile.hide(); + rbutton_extraLayerScaleYes.hide(); + rbutton_extraLayerScaleNo.hide(); + rbutton_extraLayerNewHeightYes.hide(); + rbutton_extraLayerNewHeightNo.hide(); + rbutton_extraLayerNewWidthYes.hide(); + rbutton_extraLayerNewWidthNo.hide(); + rbutton_extraLayerCropSizeYes.hide(); + rbutton_extraLayerCropSizeNo.hide(); + rbutton_extraLayerMeanFileYes.hide(); + rbutton_extraLayerMeanFileNo.hide(); + text_extraLayerScale.hide(); + text_extraLayerNewHeight.hide(); + text_extraLayerNewWidth.hide(); + text_extraLayerCropSize.hide(); + text_extraLayerMeanFile.hide(); + label_extraLayerSource.hide(); + label_extraLayerBatchSize.hide(); + text_extraLayerSource.hide(); + text_extraLayerBatchSize.hide(); + label_extraLayerBackend.hide(); + rbutton_extraLayerLMDB.hide(); + rbutton_extraLayerLEVELDB.hide(); + + + label_extraLayerTop1.set_text("Top1 Layer Name: "); + label_extraLayerTop1.set_line_wrap(); + label_extraLayerTop1.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerTop1,0,2,2,1); + label_extraLayerTop1.show(); + + text_extraLayerTop1.set_max_length(100); + text_extraLayerTop1.set_text(""); + text_extraLayerTop1.select_region(0, text_extraLayerTop1.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerTop1,2,2,1,1); + text_extraLayerTop1.show(); + + label_extraLayerTop2.set_text("Top2 Layer Name: "); + label_extraLayerTop2.set_line_wrap(); + label_extraLayerTop2.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerTop2,0,4,2,1); + label_extraLayerTop2.show(); + + text_extraLayerTop2.set_max_length(100); + text_extraLayerTop2.set_text(""); + text_extraLayerTop2.select_region(0, text_extraLayerTop2.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerTop2,2,4,1,1); + text_extraLayerTop2.show(); + + label_extraLayerName.set_text("Current Layer Name: "); + label_extraLayerName.set_line_wrap(); + label_extraLayerName.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerName,0,3,2,1); + label_extraLayerName.show(); + + text_extraLayerName.set_max_length(100); + text_extraLayerName.set_text(""); + text_extraLayerName.select_region(0, text_extraLayerName.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerName,2,3,1,1); + text_extraLayerName.show(); + + label_extraLayerPhase.set_text("phase: "); + label_extraLayerPhase.set_line_wrap(); + label_extraLayerPhase.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerPhase,0,8,2,1); + label_extraLayerPhase.show(); + +// Gtk::RadioButton::Group group9 = rbutton_extraLayerTrain.get_group(); +// rbutton_extraLayerTest.set_group(group9); +// rbutton_extraLayerTrain.set_active(); +// m_grid_extraLayerType.attach(rbutton_extraLayerTrain,2,8,1,1); + rbutton_extraLayerTrain.show(); +// m_grid_extraLayerType.attach(rbutton_extraLayerTest,3,8,1,1); + rbutton_extraLayerTest.show(); + + label_extraLayerScale.set_text("scale: "); + label_extraLayerScale.set_line_wrap(); + label_extraLayerScale.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerScale,0,9,2,1); + label_extraLayerScale.show(); + +// Gtk::RadioButton::Group group10 = rbutton_extraLayerScaleYes.get_group(); +// rbutton_extraLayerScaleNo.set_group(group10); + rbutton_extraLayerScaleNo.set_active(); +// m_grid_extraLayerType.attach(rbutton_extraLayerScaleYes,2,9,1,1); + rbutton_extraLayerScaleYes.show(); +// m_grid_extraLayerType.attach(rbutton_extraLayerScaleNo,3,9,1,1); + rbutton_extraLayerScaleNo.show(); + + text_extraLayerScale.set_max_length(100); + text_extraLayerScale.set_text(""); + text_extraLayerScale.select_region(0, text_extraLayerScale.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerScale,4,9,1,1); + text_extraLayerScale.show(); + + label_extraLayerNewHeight.set_text("new_height: "); + label_extraLayerNewHeight.set_line_wrap(); + label_extraLayerNewHeight.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerNewHeight,0,10,2,1); + label_extraLayerNewHeight.show(); + +// Gtk::RadioButton::Group group11 = rbutton_extraLayerNewHeightYes.get_group(); +// rbutton_extraLayerNewHeightNo.set_group(group11); + rbutton_extraLayerNewHeightNo.set_active(); +// m_grid_extraLayerType.attach(rbutton_extraLayerNewHeightYes,2,10,1,1); + rbutton_extraLayerNewHeightYes.show(); +// m_grid_extraLayerType.attach(rbutton_extraLayerNewHeightNo,3,10,1,1); + rbutton_extraLayerNewHeightNo.show(); + + text_extraLayerNewHeight.set_max_length(100); + text_extraLayerNewHeight.set_text(""); + text_extraLayerNewHeight.select_region(0, text_extraLayerNewHeight.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerNewHeight,4,10,1,1); + text_extraLayerNewHeight.show(); + + label_extraLayerNewWidth.set_text("new_width: "); + label_extraLayerNewWidth.set_line_wrap(); + label_extraLayerNewWidth.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerNewWidth,0,11,2,1); + label_extraLayerNewWidth.show(); + +// Gtk::RadioButton::Group group12 = rbutton_extraLayerNewWidthYes.get_group(); +// rbutton_extraLayerNewWidthNo.set_group(group12); + rbutton_extraLayerNewWidthNo.set_active(); +// m_grid_extraLayerType.attach(rbutton_extraLayerNewWidthYes,2,11,1,1); + rbutton_extraLayerNewWidthYes.show(); +// m_grid_extraLayerType.attach(rbutton_extraLayerNewWidthNo,3,11,1,1); + rbutton_extraLayerNewWidthNo.show(); + + text_extraLayerNewWidth.set_max_length(100); + text_extraLayerNewWidth.set_text(""); + text_extraLayerNewWidth.select_region(0, text_extraLayerNewWidth.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerNewWidth,4,11,1,1); + text_extraLayerNewWidth.show(); + + label_extraLayerCropSize.set_text("crop_size: "); + label_extraLayerCropSize.set_line_wrap(); + label_extraLayerCropSize.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerCropSize,0,12,2,1); + label_extraLayerCropSize.show(); + +// Gtk::RadioButton::Group group13 = rbutton_extraLayerCropSizeYes.get_group(); +// rbutton_extraLayerCropSizeNo.set_group(group13); + rbutton_extraLayerCropSizeNo.set_active(); +// m_grid_extraLayerType.attach(rbutton_extraLayerCropSizeYes,2,12,1,1); + rbutton_extraLayerCropSizeYes.show(); +// m_grid_extraLayerType.attach(rbutton_extraLayerCropSizeNo,3,12,1,1); + rbutton_extraLayerCropSizeNo.show(); + + text_extraLayerCropSize.set_max_length(100); + text_extraLayerCropSize.set_text(""); + text_extraLayerCropSize.select_region(0, text_extraLayerCropSize.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerCropSize,4,12,1,1); + text_extraLayerCropSize.show(); + + label_extraLayerMeanFile.set_text("mean_file: "); + label_extraLayerMeanFile.set_line_wrap(); + label_extraLayerMeanFile.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerMeanFile,0,13,2,1); + label_extraLayerMeanFile.show(); + +// Gtk::RadioButton::Group group14 = rbutton_extraLayerMeanFileYes.get_group(); +// rbutton_extraLayerMeanFileNo.set_group(group14); + rbutton_extraLayerMeanFileNo.set_active(); +// m_grid_extraLayerType.attach(rbutton_extraLayerMeanFileYes,2,13,1,1); + rbutton_extraLayerMeanFileYes.show(); +// m_grid_extraLayerType.attach(rbutton_extraLayerMeanFileNo,3,13,1,1); + rbutton_extraLayerMeanFileNo.show(); + + text_extraLayerMeanFile.set_max_length(100); + text_extraLayerMeanFile.set_text(""); + text_extraLayerMeanFile.select_region(0, text_extraLayerMeanFile.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerMeanFile,4,13,1,1); + text_extraLayerMeanFile.show(); + + label_extraLayerSource.set_text("source: "); + label_extraLayerSource.set_line_wrap(); + label_extraLayerSource.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerSource,0,14,2,1); + label_extraLayerSource.show(); + + text_extraLayerSource.set_max_length(100); + text_extraLayerSource.set_text(""); + text_extraLayerSource.select_region(0, text_extraLayerSource.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerSource,2,14,1,1); + text_extraLayerSource.show(); + + label_extraLayerBatchSize.set_text("batch_size: "); + label_extraLayerBatchSize.set_line_wrap(); + label_extraLayerBatchSize.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerBatchSize,0,15,2,1); + label_extraLayerBatchSize.show(); + + text_extraLayerBatchSize.set_max_length(100); + text_extraLayerBatchSize.set_text(""); + text_extraLayerBatchSize.select_region(0, text_extraLayerBatchSize.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerBatchSize,2,15,1,1); + text_extraLayerBatchSize.show(); + + label_extraLayerBackend.set_text("backend: "); + label_extraLayerBackend.set_line_wrap(); + label_extraLayerBackend.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerBackend,0,16,2,1); + label_extraLayerBackend.show(); + +// Gtk::RadioButton::Group group15 = rbutton_extraLayerLMDB.get_group(); +// rbutton_extraLayerLEVELDB.set_group(group15); + rbutton_extraLayerLMDB.set_active(); +// m_grid_extraLayerType.attach(rbutton_extraLayerLMDB,2,16,1,1); + rbutton_extraLayerLMDB.show(); +// m_grid_extraLayerType.attach(rbutton_extraLayerLEVELDB,3,16,1,1); + rbutton_extraLayerLEVELDB.show(); + + button_setExtraParameters.show(); + } + else if(data == "HDF5Data") + { + button_setExtraParameters.hide(); + label_extraLayerBottom1.hide(); + label_extraLayerTop1.hide(); + label_extraLayerTop2.hide(); + label_extraLayerName.hide(); + text_extraLayerBottom1.hide(); + text_extraLayerTop1.hide(); + text_extraLayerTop2.hide(); + text_extraLayerName.hide(); + text_extraLayerTopK.hide(); + label_extraLayerTopK.hide(); + label_extraLayerOutMaxVal.hide(); + rbutton_extraLayerOutMaxValTrue.hide(); + rbutton_extraLayerOutMaxValFalse.hide(); + label_extraLayerBottom2.hide(); + text_extraLayerBottom2.hide(); + label_extraLayerPhase.hide(); + rbutton_extraLayerTrain.hide(); + rbutton_extraLayerTest.hide(); + label_extraLayerScale.hide(); + label_extraLayerNewHeight.hide(); + label_extraLayerNewWidth.hide(); + label_extraLayerCropSize.hide(); + label_extraLayerMeanFile.hide(); + rbutton_extraLayerScaleYes.hide(); + rbutton_extraLayerScaleNo.hide(); + rbutton_extraLayerNewHeightYes.hide(); + rbutton_extraLayerNewHeightNo.hide(); + rbutton_extraLayerNewWidthYes.hide(); + rbutton_extraLayerNewWidthNo.hide(); + rbutton_extraLayerCropSizeYes.hide(); + rbutton_extraLayerCropSizeNo.hide(); + rbutton_extraLayerMeanFileYes.hide(); + rbutton_extraLayerMeanFileNo.hide(); + text_extraLayerScale.hide(); + text_extraLayerNewHeight.hide(); + text_extraLayerNewWidth.hide(); + text_extraLayerCropSize.hide(); + text_extraLayerMeanFile.hide(); + label_extraLayerSource.hide(); + label_extraLayerBatchSize.hide(); + text_extraLayerSource.hide(); + text_extraLayerBatchSize.hide(); + label_extraLayerBackend.hide(); + rbutton_extraLayerLMDB.hide(); + rbutton_extraLayerLEVELDB.hide(); + + + label_extraLayerTop1.set_text("Top1 Layer Name: "); + label_extraLayerTop1.set_line_wrap(); + label_extraLayerTop1.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerTop1,0,2,2,1); + label_extraLayerTop1.show(); + + text_extraLayerTop1.set_max_length(100); + text_extraLayerTop1.set_text(""); + text_extraLayerTop1.select_region(0, text_extraLayerTop1.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerTop1,2,2,1,1); + text_extraLayerTop1.show(); + + label_extraLayerTop2.set_text("Top2 Layer Name: "); + label_extraLayerTop2.set_line_wrap(); + label_extraLayerTop2.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerTop2,0,4,2,1); + label_extraLayerTop2.show(); + + text_extraLayerTop2.set_max_length(100); + text_extraLayerTop2.set_text(""); + text_extraLayerTop2.select_region(0, text_extraLayerTop2.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerTop2,2,4,1,1); + text_extraLayerTop2.show(); + + label_extraLayerName.set_text("Current Layer Name: "); + label_extraLayerName.set_line_wrap(); + label_extraLayerName.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerName,0,3,2,1); + label_extraLayerName.show(); + + text_extraLayerName.set_max_length(100); + text_extraLayerName.set_text(""); + text_extraLayerName.select_region(0, text_extraLayerName.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerName,2,3,1,1); + text_extraLayerName.show(); + + label_extraLayerPhase.set_text("phase: "); + label_extraLayerPhase.set_line_wrap(); + label_extraLayerPhase.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerPhase,0,8,2,1); + label_extraLayerPhase.show(); + +// Gtk::RadioButton::Group group9 = rbutton_extraLayerTrain.get_group(); +// rbutton_extraLayerTest.set_group(group9); +// rbutton_extraLayerTrain.set_active(); +// m_grid_extraLayerType.attach(rbutton_extraLayerTrain,2,8,1,1); + rbutton_extraLayerTrain.show(); +// m_grid_extraLayerType.attach(rbutton_extraLayerTest,3,8,1,1); + rbutton_extraLayerTest.show(); + + label_extraLayerSource.set_text("source: "); + label_extraLayerSource.set_line_wrap(); + label_extraLayerSource.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerSource,0,14,2,1); + label_extraLayerSource.show(); + + text_extraLayerSource.set_max_length(100); + text_extraLayerSource.set_text(""); + text_extraLayerSource.select_region(0, text_extraLayerSource.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerSource,2,14,1,1); + text_extraLayerSource.show(); + + label_extraLayerBatchSize.set_text("batch_size: "); + label_extraLayerBatchSize.set_line_wrap(); + label_extraLayerBatchSize.set_justify(Gtk::JUSTIFY_FILL); +// m_grid_extraLayerType.attach(label_extraLayerBatchSize,0,15,2,1); + label_extraLayerBatchSize.show(); + + text_extraLayerBatchSize.set_max_length(100); + text_extraLayerBatchSize.set_text(""); + text_extraLayerBatchSize.select_region(0, text_extraLayerBatchSize.get_text_length()); +// m_grid_extraLayerType.attach(text_extraLayerBatchSize,2,15,1,1); + text_extraLayerBatchSize.show(); + + + + button_setExtraParameters.show(); + } + else + { + button_setExtraParameters.hide(); + label_extraLayerBottom1.hide(); + label_extraLayerTop1.hide(); + label_extraLayerTop2.hide(); + label_extraLayerName.hide(); + text_extraLayerBottom1.hide(); + text_extraLayerTop1.hide(); + text_extraLayerTop2.hide(); + text_extraLayerName.hide(); + text_extraLayerTopK.hide(); + label_extraLayerTopK.hide(); + label_extraLayerOutMaxVal.hide(); + rbutton_extraLayerOutMaxValTrue.hide(); + rbutton_extraLayerOutMaxValFalse.hide(); + label_extraLayerBottom2.hide(); + text_extraLayerBottom2.hide(); + label_extraLayerPhase.hide(); + rbutton_extraLayerTrain.hide(); + rbutton_extraLayerTest.hide(); + label_extraLayerScale.hide(); + label_extraLayerNewHeight.hide(); + label_extraLayerNewWidth.hide(); + label_extraLayerCropSize.hide(); + label_extraLayerMeanFile.hide(); + rbutton_extraLayerScaleYes.hide(); + rbutton_extraLayerScaleNo.hide(); + rbutton_extraLayerNewHeightYes.hide(); + rbutton_extraLayerNewHeightNo.hide(); + rbutton_extraLayerNewWidthYes.hide(); + rbutton_extraLayerNewWidthNo.hide(); + rbutton_extraLayerCropSizeYes.hide(); + rbutton_extraLayerCropSizeNo.hide(); + rbutton_extraLayerMeanFileYes.hide(); + rbutton_extraLayerMeanFileNo.hide(); + text_extraLayerScale.hide(); + text_extraLayerNewHeight.hide(); + text_extraLayerNewWidth.hide(); + text_extraLayerCropSize.hide(); + text_extraLayerMeanFile.hide(); + label_extraLayerSource.hide(); + label_extraLayerBatchSize.hide(); + text_extraLayerSource.hide(); + text_extraLayerBatchSize.hide(); + label_extraLayerBackend.hide(); + rbutton_extraLayerLMDB.hide(); + rbutton_extraLayerLEVELDB.hide(); + } + + + m_sw_extraLayerType.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); m_grid_extraLayerType.show(); // show_all_children(); diff --git a/detectors/include/od/detectors/global2D/training/MainWindow.h b/detectors/include/od/detectors/global2D/training/MainWindow.h index 8d9813fe..2be7c1ba 100644 --- a/detectors/include/od/detectors/global2D/training/MainWindow.h +++ b/detectors/include/od/detectors/global2D/training/MainWindow.h @@ -63,6 +63,14 @@ void NetworkCreator::on_cell_data_extra(const Gtk::TreeModel::const_iterator& it cell_extraLayerType.property_text() = "-" + extra_extraLayerType + "-"; cell_extraLayerType.property_foreground() = "purple"; + // + auto row_currentLayers = *iter; + const Glib::ustring extra_currentLayers = row_currentLayers[column_currentLayers.m_col_extra]; + if(extra_currentLayers.empty()) + cell_currentLayers.property_text() = ""; + else + cell_currentLayers.property_text() = "-" + extra_currentLayers + "-"; + cell_currentLayers.property_foreground() = "red"; } void NetworkCreator::on_combo_changed() @@ -72,6 +80,7 @@ void NetworkCreator::on_combo_changed() Gtk::TreeModel::iterator iter_normalizationLayerType = combo_normalizationLayerType.get_active(); Gtk::TreeModel::iterator iter_lossLayerType = combo_lossLayerType.get_active(); Gtk::TreeModel::iterator iter_extraLayerType = combo_extraLayerType.get_active(); + Gtk::TreeModel::iterator iter_currentLayers = combo_currentLayers.get_active(); // level 1 @@ -139,6 +148,17 @@ void NetworkCreator::on_combo_changed() extraLayerTypeData = name_extraLayerType; } } + if(iter_currentLayers) + { + Gtk::TreeModel::Row row_currentLayers = *iter_currentLayers; + if(row_currentLayers) + { + int id_currentLayers = row_currentLayers[column_currentLayers.m_col_id]; + Glib::ustring name_currentLayers = row_currentLayers[column_currentLayers.m_col_name]; +// std::cout << " ID=" << id_extraLayerType << ", name=" << name_extraLayerType << std::endl; + currentLayersName = name_currentLayers; + } + } else std::cout << "invalid iter" << std::endl; diff --git a/detectors/include/od/detectors/global2D/training/Network.h b/detectors/include/od/detectors/global2D/training/Network.h index 571bae80..7d3471bd 100644 --- a/detectors/include/od/detectors/global2D/training/Network.h +++ b/detectors/include/od/detectors/global2D/training/Network.h @@ -1,5 +1,6 @@ #pragma once + #include <gtkmm/grid.h> #include <gtkmm/entry.h> #include <gtkmm/button.h> @@ -16,6 +17,8 @@ #include <sstream> #include <vector> + + class NetworkCreator : public Gtk::Window { public: @@ -67,6 +70,9 @@ class NetworkCreator : public Gtk::Window button_addMoreLayer4, button_setLossParameters, button_addMoreLayer5, + button_setExtraParameters, + button_deleteSelectedLayer, + button_appendLayerAfter, button_saveFile; Gtk::Entry text_networkFileName, text_activationLayerTop, @@ -112,8 +118,22 @@ class NetworkCreator : public Gtk::Window text_lossLayerTop, text_lossLayerBottom1, text_lossLayerBottom2, + text_lossLayerBottom3, text_lossLayerName, - text_lossLayerNormalize; + text_lossLayerNormalize, + text_extraLayerTop1, + text_extraLayerTop2, + text_extraLayerBottom1, + text_extraLayerBottom2, + text_extraLayerName, + text_extraLayerTopK, + text_extraLayerScale, + text_extraLayerNewHeight, + text_extraLayerNewWidth, + text_extraLayerCropSize, + text_extraLayerMeanFile, + text_extraLayerSource, + text_extraLayerBatchSize; Gtk::Label label_networkFileName, label_activationLayerType, label_criticalLayerType, @@ -172,14 +192,33 @@ class NetworkCreator : public Gtk::Window label_lossLayerTop, label_lossLayerBottom1, label_lossLayerBottom2, + label_lossLayerBottom3, label_lossLayerName, label_lossLayerNormalize, - label_lossLayerNormalization; + label_lossLayerNormalization, + label_lossLayerNorm, + label_extraLayerTop1, + label_extraLayerTop2, + label_extraLayerBottom1, + label_extraLayerBottom2, + label_extraLayerName, + label_extraLayerTopK, + label_extraLayerOutMaxVal, + label_extraLayerPhase, + label_extraLayerScale, + label_extraLayerNewHeight, + label_extraLayerNewWidth, + label_extraLayerCropSize, + label_extraLayerMeanFile, + label_extraLayerSource, + label_extraLayerBatchSize, + label_extraLayerBackend; Gtk::ComboBox combo_activationLayerType, combo_criticalLayerType, combo_normalizationLayerType, combo_lossLayerType, - combo_extraLayerType; + combo_extraLayerType, + combo_currentLayers; Gtk::TextView textView_fullCnnLayerMatter; Glib::RefPtr<Gtk::TextBuffer> buffer_fullCnnLayerMatter; Gtk::Box box_fullCnnLayerMatter; @@ -193,8 +232,19 @@ class NetworkCreator : public Gtk::Window rbutton_criticalLayerWeightFillerMSRAIn, rbutton_criticalLayerWeightFillerMSRAOut, rbutton_criticalLayerWeightFillerMSRAAvg, rbutton_criticalLayerPoolMax, rbutton_criticalLayerPoolAve, - rbutton_normalizationLayerLRNWithin, rbutton_normalizationLayerLRNAcross; - + rbutton_normalizationLayerLRNWithin, rbutton_normalizationLayerLRNAcross, + rbutton_lossLayerFull, rbutton_lossLayerValid, rbutton_lossLayerBatch, + rbutton_lossLayerL1, rbutton_lossLayerL2, + rbutton_extraLayerOutMaxValTrue, rbutton_extraLayerOutMaxValFalse, + rbutton_extraLayerTrain, rbutton_extraLayerTest, + rbutton_extraLayerScaleYes, rbutton_extraLayerScaleNo, + rbutton_extraLayerNewHeightYes, rbutton_extraLayerNewHeightNo, + rbutton_extraLayerNewWidthYes, rbutton_extraLayerNewWidthNo, + rbutton_extraLayerCropSizeYes, rbutton_extraLayerCropSizeNo, + rbutton_extraLayerMeanFileYes, rbutton_extraLayerMeanFileNo, + rbutton_extraLayerLMDB, rbutton_extraLayerLEVELDB; + + //Tree model columns: class ModelColumns : public Gtk::TreeModel::ColumnRecord @@ -210,17 +260,20 @@ class NetworkCreator : public Gtk::Window column_criticalLayerType, column_normalizationLayerType, column_lossLayerType, - column_extraLayerType; + column_extraLayerType, + column_currentLayers; Gtk::CellRendererText cell_activationLayerType, cell_criticalLayerType, cell_normalizationLayerType, cell_lossLayerType, - cell_extraLayerType; + cell_extraLayerType, + cell_currentLayers; Glib::RefPtr<Gtk::ListStore> ref_activationLayerType, ref_criticalLayerType, ref_normalizationLayerType, ref_lossLayerType, - ref_extraLayerType; + ref_extraLayerType, + ref_currentLayers; private: @@ -233,7 +286,8 @@ class NetworkCreator : public Gtk::Window int numLayers; Glib::ustring fullCnnLayerMatter; std::vector<Glib::ustring> fullCnnLayers; + std::vector<Glib::ustring> fullCnnLayersName; + Glib::ustring currentLayersName; + bool appendStatus; -}; - - +}; \ No newline at end of file diff --git a/detectors/include/od/detectors/global2D/training/Node.h b/detectors/include/od/detectors/global2D/training/Node.h index cd19ee88..a3788c80 100644 --- a/detectors/include/od/detectors/global2D/training/Node.h +++ b/detectors/include/od/detectors/global2D/training/Node.h @@ -1,22 +1,27 @@ #include "od/detectors/global2D/training/Network.h" #include <iostream> +#include "od/detectors/global2D/training/Network.h" +#include <iostream> struct Node { Glib::ustring data; + Glib::ustring name; Node* nextLayer; }; -void initializeLayer(struct Node *headLayer, Glib::ustring data) +void initializeLayer(struct Node *headLayer, Glib::ustring data, Glib::ustring name) { headLayer->data = data; + headLayer->name = name; headLayer->nextLayer = NULL; } -void appendLayer(struct Node *headLayer, Glib::ustring data) +void appendLayer(struct Node *headLayer, Glib::ustring data, Glib::ustring name) { Node *newLayer = new Node; newLayer->data = data; + newLayer->name = name; newLayer->nextLayer = NULL; Node *currentLayer = headLayer; @@ -31,6 +36,29 @@ void appendLayer(struct Node *headLayer, Glib::ustring data) } } +void insertLayer(struct Node *headLayer, int pos, Glib::ustring data, Glib::ustring name){ + + Node *newLayer = new Node; + newLayer->data = data; + newLayer->name = name; + newLayer->nextLayer = NULL; + + Node *currentLayer = headLayer; + int i = 0; + while(currentLayer) + { + if(i==pos) + { + newLayer->nextLayer = currentLayer->nextLayer; + currentLayer->nextLayer = newLayer; + return; + } + currentLayer = currentLayer->nextLayer; + i++; + } + +} + bool deleteLayer(struct Node **headLayer, Node *deleteLayer) { @@ -65,6 +93,10 @@ struct Node *searchLayer(struct Node *headLayer, Glib::ustring data) { return currentLayer; } + else if(currentLayer->data == data) + { + return currentLayer; + } currentLayer = currentLayer->nextLayer; } std::cout << "No Layer " << data << " in the CNN." << std::endl; diff --git a/detectors/src/global2D/CMakeLists.txt b/detectors/src/global2D/CMakeLists.txt index 2bb038dd..68a1fb1e 100644 --- a/detectors/src/global2D/CMakeLists.txt +++ b/detectors/src/global2D/CMakeLists.txt @@ -30,7 +30,10 @@ if(BUILD_GLOBAL_2D_DETECTION) endif() if(WITH_GTKMM AND GTKMM_FOUND) - set(SOURCES ${SOURCES} "training/Solver.cpp" "training/ConvTrainer.cpp" "training/Network.cpp") + set(SOURCES ${SOURCES} "training/Solver.cpp" + "training/ConvTrainer.cpp" + "training/Network.cpp" + ) include_directories(${GTKMM_INCLUDE_DIRS}) link_directories(${GTKMM_LIBRARY_DIRS}) set(SUBSYS_DEPS ${SUBSYS_DEPS} ${GTKMM_LIBRARIES}) @@ -38,6 +41,11 @@ if(BUILD_GLOBAL_2D_DETECTION) endif() + if(WITH_GTKMM AND GTKMM_FOUND) + set(SOURCES ${SOURCES} "annotation/Annotation.cpp" + "annotation/Annotator.cpp") + endif() + if(WITH_SVMLIGHT) set(SOURCES ${SOURCES} "training/HOGTrainer.cpp" "detection/HOGDetector.cpp") include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) diff --git a/detectors/src/global2D/annotation/Annotation.cpp b/detectors/src/global2D/annotation/Annotation.cpp new file mode 100644 index 00000000..37a6f903 --- /dev/null +++ b/detectors/src/global2D/annotation/Annotation.cpp @@ -0,0 +1,1465 @@ +#include "od/detectors/global2D/annotation/Annotation.h" + +namespace od +{ + namespace g2d + { + + void Annotation::showWindow_main() + { + storage = 0; + storageROILocation.clear(); + storageROILocationCurrent.clear(); + storageFileName.clear(); + imageFileNames.clear(); + widthMultiplier.clear(); + heightMultiplier.clear(); + currentWindow = "mainWindow"; + remove(); + set_title("Network Creator"); + set_border_width(10); + add(m_sw1); + m_grid1.set_column_spacing (10); + m_grid1.set_row_spacing (50); + // m_sw1.add(m_grid1); + m_sw1.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); + // m_grid1.show(); + show_all_children(); + m_sw1.show(); + } + + void Annotation::showWindow_imageLoad(Glib::ustring data) + { + cv::Mat img = cv::imread(filename, 1); + cv::imwrite("../examples/objectdetector/Annotation/temp.png", img); + currentWindow = "imageLoadWindow"; + remove(); + set_title("Image Window"); + set_border_width(10); + add(m_sw_imageLoad); + m_grid_imageLoad.set_column_spacing (10); + m_grid_imageLoad.set_row_spacing (10); + + if(rbutton_markBB.get_active() == 1) + { + label_annotationType.set_text("Annotion Type: Selecting Single Bounding Box"); + } + else if(rbutton_cropBB.get_active() == 1) + { + label_annotationType.set_text("Annotion Type: Cropping Single Bounding Box"); + } + else if(rbutton_markBBWithLabel.get_active() == 1) + { + label_annotationType.set_text("Annotion Type: Selecting Multiple Bounding Boxes and labelling them"); + } + + label_annotationType.set_line_wrap(); + label_annotationType.set_justify(Gtk::JUSTIFY_FILL); + // m_grid_imageLoad.attach(label_annotationType,0,0,2,1); + label_annotationType.show(); + + + if(rbutton_markBB.get_active() == 1 && file_folder == "file") + { + + label_outputFile.hide(); + text_outputFile.hide(); + button_resetMarkings.hide(); + button_selectRoi.hide(); + button_saveMarked.hide(); + button_loadAnotherImage.hide(); + button_saveCropMarked.hide(); + button_resetMarkingsCurrent.hide(); + button_selectRoiCurrent.hide(); + label_annotationLabel.hide(); + text_annotationLabel.hide(); + button_saveMarkedMultiple.hide(); + button_loadNextImage.hide(); + button_saveCropMarkedMultiple.hide(); + + loadOriginalImage(data, 0); + button_resetMarkings.show(); + button_selectRoi.show(); + button_loadAnotherImage.show(); + + label_outputFile.set_text("OutPut Text File Name: "); + label_outputFile.set_line_wrap(); + label_outputFile.set_justify(Gtk::JUSTIFY_FILL); + // m_grid_imageLoad.attach(label_outputFile,0,2,2,1); + label_outputFile.show(); + + + text_outputFile.set_max_length(100); + text_outputFile.set_text("../examples/objectdetector/Annotation/output.txt"); + text_outputFile.select_region(0, text_outputFile.get_text_length()); + // m_grid_imageLoad.attach(text_outputFile,2,2,1,1); + text_outputFile.show(); + + button_saveMarked.show(); + + } + else if(rbutton_cropBB.get_active() == 1 && file_folder == "file") + { + label_outputFile.hide(); + text_outputFile.hide(); + button_resetMarkings.hide(); + button_selectRoi.hide(); + button_saveMarked.hide(); + button_loadAnotherImage.hide(); + button_saveCropMarked.hide(); + button_resetMarkingsCurrent.hide(); + button_selectRoiCurrent.hide(); + label_annotationLabel.hide(); + text_annotationLabel.hide(); + button_saveMarkedMultiple.hide(); + button_loadNextImage.hide(); + button_saveCropMarkedMultiple.hide(); + + + loadOriginalImage(data, 0); + button_resetMarkings.show(); + button_selectRoi.show(); + button_loadAnotherImage.show(); + + label_outputFile.set_text("OutPut Text File Name: "); + label_outputFile.set_line_wrap(); + label_outputFile.set_justify(Gtk::JUSTIFY_FILL); + // m_grid_imageLoad.attach(label_outputFile,0,2,2,1); + label_outputFile.show(); + + + text_outputFile.set_max_length(100); + text_outputFile.set_text("../examples/objectdetector/Annotation/output.txt"); + text_outputFile.select_region(0, text_outputFile.get_text_length()); + // m_grid_imageLoad.attach(text_outputFile,2,2,1,1); + text_outputFile.show(); + + button_saveCropMarked.show(); + } + else if(rbutton_markBBWithLabel.get_active() == 1 && file_folder == "file") + { + label_outputFile.hide(); + text_outputFile.hide(); + button_resetMarkings.hide(); + button_selectRoi.hide(); + button_saveMarked.hide(); + button_loadAnotherImage.hide(); + button_saveCropMarked.hide(); + button_resetMarkingsCurrent.hide(); + button_selectRoiCurrent.hide(); + label_annotationLabel.hide(); + text_annotationLabel.hide(); + button_saveMarkedMultiple.hide(); + button_loadNextImage.hide(); + button_saveCropMarkedMultiple.hide(); + + loadOriginalImage(data, 0); + button_resetMarkingsCurrent.show(); + + label_annotationLabel.set_text("Give a label to ROI: "); + label_annotationLabel.set_line_wrap(); + label_annotationLabel.set_justify(Gtk::JUSTIFY_FILL); + // m_grid_imageLoad.attach(label_annotationLabel,0,4,2,1); + label_annotationLabel.show(); + + + text_annotationLabel.set_max_length(100); + text_annotationLabel.set_text(""); + text_annotationLabel.select_region(0, text_annotationLabel.get_text_length()); + // m_grid_imageLoad.attach(text_outputFile,2,4,1,1); + text_annotationLabel.show(); + + button_selectRoiCurrent.show(); + button_loadAnotherImage.show(); + + label_outputFile.set_text("OutPut Text File Name: "); + label_outputFile.set_line_wrap(); + label_outputFile.set_justify(Gtk::JUSTIFY_FILL); + // m_grid_imageLoad.attach(label_outputFile,0,2,2,1); + label_outputFile.show(); + + + text_outputFile.set_max_length(100); + text_outputFile.set_text("../examples/objectdetector/Annotation/output.txt"); + text_outputFile.select_region(0, text_outputFile.get_text_length()); + // m_grid_imageLoad.attach(text_outputFile,2,2,1,1); + text_outputFile.show(); + + button_saveMarkedMultiple.show(); + + + } + else if(rbutton_cropMultipleBB.get_active() == 1 && file_folder == "file") + { + label_outputFile.hide(); + text_outputFile.hide(); + button_resetMarkings.hide(); + button_selectRoi.hide(); + button_saveMarked.hide(); + button_loadAnotherImage.hide(); + button_saveCropMarked.hide(); + button_resetMarkingsCurrent.hide(); + button_selectRoiCurrent.hide(); + label_annotationLabel.hide(); + text_annotationLabel.hide(); + button_saveMarkedMultiple.hide(); + button_loadNextImage.hide(); + button_saveCropMarkedMultiple.hide(); + + loadOriginalImage(data, 0); + button_resetMarkingsCurrent.show(); + + label_annotationLabel.set_text("Give a label to ROI: "); + label_annotationLabel.set_line_wrap(); + label_annotationLabel.set_justify(Gtk::JUSTIFY_FILL); + // m_grid_imageLoad.attach(label_annotationLabel,0,4,2,1); + label_annotationLabel.show(); + + + text_annotationLabel.set_max_length(100); + text_annotationLabel.set_text(""); + text_annotationLabel.select_region(0, text_annotationLabel.get_text_length()); + // m_grid_imageLoad.attach(text_outputFile,2,4,1,1); + text_annotationLabel.show(); + + button_selectRoiCurrent.show(); + button_loadAnotherImage.show(); + + label_outputFile.set_text("OutPut Text File Name: "); + label_outputFile.set_line_wrap(); + label_outputFile.set_justify(Gtk::JUSTIFY_FILL); + // m_grid_imageLoad.attach(label_outputFile,0,2,2,1); + label_outputFile.show(); + + + text_outputFile.set_max_length(100); + text_outputFile.set_text("../examples/objectdetector/Annotation/output.txt"); + text_outputFile.select_region(0, text_outputFile.get_text_length()); + // m_grid_imageLoad.attach(text_outputFile,2,2,1,1); + text_outputFile.show(); + + button_saveCropMarkedMultiple.show(); + + + } + else if(rbutton_segnetMaskedBased.get_active() == 1 && file_folder == "file") + { + label_outputFile.hide(); + text_outputFile.hide(); + button_resetMarkings.hide(); + button_selectRoi.hide(); + button_saveMarked.hide(); + button_saveCropMarked.hide(); + button_loadAnotherImage.hide(); + button_resetMarkingsCurrent.hide(); + button_selectRoiCurrent.hide(); + label_annotationLabel.hide(); + text_annotationLabel.hide(); + button_saveMarkedMultiple.hide(); + button_loadNextImage.hide(); + button_saveCropMarkedMultiple.hide(); + button_resetSegnetMaskCurrent.hide(); + button_selectSegnetMaskCurrent.hide(); + button_saveSegnetMask.hide(); + + loadOriginalImage(data, 0); + button_resetSegnetMaskCurrent.show(); + button_selectSegnetMaskCurrent.show(); + + label_annotationLabel.set_text("Give a label to ROI: "); + label_annotationLabel.set_line_wrap(); + label_annotationLabel.set_justify(Gtk::JUSTIFY_FILL); + // m_grid_imageLoad.attach(label_annotationLabel,0,4,2,1); + label_annotationLabel.show(); + + + text_annotationLabel.set_max_length(100); + text_annotationLabel.set_text(""); + text_annotationLabel.select_region(0, text_annotationLabel.get_text_length()); + // m_grid_imageLoad.attach(text_outputFile,2,4,1,1); + text_annotationLabel.show(); + + button_loadAnotherImage.show(); + + label_outputFile.set_text("OutPut Text File Name: "); + label_outputFile.set_line_wrap(); + label_outputFile.set_justify(Gtk::JUSTIFY_FILL); + // m_grid_imageLoad.attach(label_outputFile,0,2,2,1); + label_outputFile.show(); + + + text_outputFile.set_max_length(100); + text_outputFile.set_text("../examples/objectdetector/Annotation/output.txt"); + text_outputFile.select_region(0, text_outputFile.get_text_length()); + // m_grid_imageLoad.attach(text_outputFile,2,2,1,1); + text_outputFile.show(); + + button_saveSegnetMask.show(); + } + else if(rbutton_markBB.get_active() == 1 && file_folder == "folder") + { + label_outputFile.hide(); + text_outputFile.hide(); + button_resetMarkings.hide(); + button_selectRoi.hide(); + button_saveMarked.hide(); + button_loadAnotherImage.hide(); + button_saveCropMarked.hide(); + button_resetMarkingsCurrent.hide(); + button_selectRoiCurrent.hide(); + label_annotationLabel.hide(); + text_annotationLabel.hide(); + button_saveMarkedMultiple.hide(); + button_loadNextImage.hide(); + button_saveCropMarkedMultiple.hide(); + + + DIR *dpdf; + struct dirent *epdf; + + dpdf = opendir(foldername.c_str()); + if (dpdf != NULL) + { + while (epdf = readdir(dpdf)) + { + std::string val = epdf->d_name; + if(val.length() > 3) + { + std::string fName = foldername + "/" + epdf->d_name; + std::cout << fName << std::endl; + imageFileNames.push_back(fName); + imagesInFolder++; + } + } + } + filename = imageFileNames[--imagesInFolder]; + loadOriginalImage(filename, 0); + + button_resetMarkings.show(); + button_selectRoi.show(); + button_loadNextImage.show(); + + button_loadAnotherImage.show(); + + label_outputFile.set_text("OutPut Text File Name: "); + label_outputFile.set_line_wrap(); + label_outputFile.set_justify(Gtk::JUSTIFY_FILL); + // m_grid_imageLoad.attach(label_outputFile,0,2,2,1); + label_outputFile.show(); + + + text_outputFile.set_max_length(100); + text_outputFile.set_text("../examples/objectdetector/Annotation/output.txt"); + text_outputFile.select_region(0, text_outputFile.get_text_length()); + // m_grid_imageLoad.attach(text_outputFile,2,2,1,1); + text_outputFile.show(); + + button_saveMarked.show(); + } + else if(rbutton_cropBB.get_active() == 1 && file_folder == "folder") + { + label_outputFile.hide(); + text_outputFile.hide(); + button_resetMarkings.hide(); + button_selectRoi.hide(); + button_saveMarked.hide(); + button_loadAnotherImage.hide(); + button_saveCropMarked.hide(); + button_resetMarkingsCurrent.hide(); + button_selectRoiCurrent.hide(); + label_annotationLabel.hide(); + text_annotationLabel.hide(); + button_saveMarkedMultiple.hide(); + button_loadNextImage.hide(); + button_saveCropMarkedMultiple.hide(); + + + DIR *dpdf; + struct dirent *epdf; + + dpdf = opendir(foldername.c_str()); + if (dpdf != NULL) + { + while (epdf = readdir(dpdf)) + { + std::string val = epdf->d_name; + if(val.length() > 3) + { + std::string fName = foldername + "/" + epdf->d_name; + std::cout << fName << std::endl; + imageFileNames.push_back(fName); + imagesInFolder++; + } + } + } + filename = imageFileNames[--imagesInFolder]; + loadOriginalImage(filename, 0); + + button_resetMarkings.show(); + button_selectRoi.show(); + button_loadNextImage.show(); + + button_loadAnotherImage.show(); + + label_outputFile.set_text("OutPut Text File Name: "); + label_outputFile.set_line_wrap(); + label_outputFile.set_justify(Gtk::JUSTIFY_FILL); + // m_grid_imageLoad.attach(label_outputFile,0,2,2,1); + label_outputFile.show(); + + + text_outputFile.set_max_length(100); + text_outputFile.set_text("../examples/objectdetector/Annotation/output.txt"); + text_outputFile.select_region(0, text_outputFile.get_text_length()); + // m_grid_imageLoad.attach(text_outputFile,2,2,1,1); + text_outputFile.show(); + + button_saveCropMarked.show(); + + } + else if(rbutton_markBBWithLabel.get_active() == 1 && file_folder == "folder") + { + label_outputFile.hide(); + text_outputFile.hide(); + button_resetMarkings.hide(); + button_selectRoi.hide(); + button_saveMarked.hide(); + button_loadAnotherImage.hide(); + button_saveCropMarked.hide(); + button_resetMarkingsCurrent.hide(); + button_selectRoiCurrent.hide(); + label_annotationLabel.hide(); + text_annotationLabel.hide(); + button_saveMarkedMultiple.hide(); + button_loadNextImage.hide(); + button_saveCropMarkedMultiple.hide(); + + + DIR *dpdf; + struct dirent *epdf; + + dpdf = opendir(foldername.c_str()); + if (dpdf != NULL) + { + while (epdf = readdir(dpdf)) + { + std::string val = epdf->d_name; + if(val.length() > 3) + { + std::string fName = foldername + "/" + epdf->d_name; + std::cout << fName << std::endl; + imageFileNames.push_back(fName); + imagesInFolder++; + } + } + } + filename = imageFileNames[--imagesInFolder]; + loadOriginalImage(filename, 0); + + button_resetMarkingsCurrent.show(); + + label_annotationLabel.set_text("Give a label to ROI: "); + label_annotationLabel.set_line_wrap(); + label_annotationLabel.set_justify(Gtk::JUSTIFY_FILL); + // m_grid_imageLoad.attach(label_annotationLabel,0,4,2,1); + label_annotationLabel.show(); + + + text_annotationLabel.set_max_length(100); + text_annotationLabel.set_text(""); + text_annotationLabel.select_region(0, text_annotationLabel.get_text_length()); + // m_grid_imageLoad.attach(text_outputFile,2,4,1,1); + text_annotationLabel.show(); + + button_selectRoiCurrent.show(); + button_loadAnotherImage.show(); + button_loadNextImage.show(); + + label_outputFile.set_text("OutPut Text File Name: "); + label_outputFile.set_line_wrap(); + label_outputFile.set_justify(Gtk::JUSTIFY_FILL); + // m_grid_imageLoad.attach(label_outputFile,0,2,2,1); + label_outputFile.show(); + + + text_outputFile.set_max_length(100); + text_outputFile.set_text("../examples/objectdetector/Annotation/output.txt"); + text_outputFile.select_region(0, text_outputFile.get_text_length()); + // m_grid_imageLoad.attach(text_outputFile,2,2,1,1); + text_outputFile.show(); + + button_saveMarkedMultiple.show(); + } + else if(rbutton_cropMultipleBB.get_active() == 1 && file_folder == "folder") + { + label_outputFile.hide(); + text_outputFile.hide(); + button_resetMarkings.hide(); + button_selectRoi.hide(); + button_saveMarked.hide(); + button_loadAnotherImage.hide(); + button_saveCropMarked.hide(); + button_resetMarkingsCurrent.hide(); + button_selectRoiCurrent.hide(); + label_annotationLabel.hide(); + text_annotationLabel.hide(); + button_saveMarkedMultiple.hide(); + button_loadNextImage.hide(); + button_saveCropMarkedMultiple.hide(); + + + DIR *dpdf; + struct dirent *epdf; + + dpdf = opendir(foldername.c_str()); + if (dpdf != NULL) + { + while (epdf = readdir(dpdf)) + { + std::string val = epdf->d_name; + if(val.length() > 3) + { + std::string fName = foldername + "/" + epdf->d_name; + std::cout << fName << std::endl; + imageFileNames.push_back(fName); + imagesInFolder++; + } + } + } + filename = imageFileNames[--imagesInFolder]; + loadOriginalImage(filename, 0); + + button_resetMarkingsCurrent.show(); + + label_annotationLabel.set_text("Give a label to ROI: "); + label_annotationLabel.set_line_wrap(); + label_annotationLabel.set_justify(Gtk::JUSTIFY_FILL); + // m_grid_imageLoad.attach(label_annotationLabel,0,4,2,1); + label_annotationLabel.show(); + + + text_annotationLabel.set_max_length(100); + text_annotationLabel.set_text(""); + text_annotationLabel.select_region(0, text_annotationLabel.get_text_length()); + // m_grid_imageLoad.attach(text_outputFile,2,4,1,1); + text_annotationLabel.show(); + + button_selectRoiCurrent.show(); + button_loadAnotherImage.show(); + button_loadNextImage.show(); + + label_outputFile.set_text("OutPut Text File Name: "); + label_outputFile.set_line_wrap(); + label_outputFile.set_justify(Gtk::JUSTIFY_FILL); + // m_grid_imageLoad.attach(label_outputFile,0,2,2,1); + label_outputFile.show(); + + + text_outputFile.set_max_length(100); + text_outputFile.set_text("../examples/objectdetector/Annotation/output.txt"); + text_outputFile.select_region(0, text_outputFile.get_text_length()); + // m_grid_imageLoad.attach(text_outputFile,2,2,1,1); + text_outputFile.show(); + + button_saveCropMarkedMultiple.show(); + } + else if(rbutton_segnetMaskedBased.get_active() == 1 && file_folder == "folder") + { + label_outputFile.hide(); + text_outputFile.hide(); + button_resetMarkings.hide(); + button_selectRoi.hide(); + button_saveMarked.hide(); + button_loadAnotherImage.hide(); + button_saveCropMarked.hide(); + button_resetMarkingsCurrent.hide(); + button_selectRoiCurrent.hide(); + label_annotationLabel.hide(); + text_annotationLabel.hide(); + button_saveMarkedMultiple.hide(); + button_loadNextImage.hide(); + button_saveCropMarkedMultiple.hide(); + button_resetSegnetMaskCurrent.hide(); + button_selectSegnetMaskCurrent.hide(); + button_saveSegnetMask.hide(); + + + DIR *dpdf; + struct dirent *epdf; + + dpdf = opendir(foldername.c_str()); + if (dpdf != NULL) + { + while (epdf = readdir(dpdf)) + { + std::string val = epdf->d_name; + if(val.length() > 3) + { + std::string fName = foldername + "/" + epdf->d_name; + std::cout << fName << std::endl; + imageFileNames.push_back(fName); + imagesInFolder++; + } + } + } + filename = imageFileNames[--imagesInFolder]; + loadOriginalImage(filename, 0); + button_resetSegnetMaskCurrent.show(); + button_selectSegnetMaskCurrent.show(); + + label_annotationLabel.set_text("Give a label to ROI: "); + label_annotationLabel.set_line_wrap(); + label_annotationLabel.set_justify(Gtk::JUSTIFY_FILL); + // m_grid_imageLoad.attach(label_annotationLabel,0,4,2,1); + label_annotationLabel.show(); + + + text_annotationLabel.set_max_length(100); + text_annotationLabel.set_text(""); + text_annotationLabel.select_region(0, text_annotationLabel.get_text_length()); + // m_grid_imageLoad.attach(text_outputFile,2,4,1,1); + text_annotationLabel.show(); + + button_loadAnotherImage.show(); + button_loadNextImage.show(); + + label_outputFile.set_text("OutPut Text File Name: "); + label_outputFile.set_line_wrap(); + label_outputFile.set_justify(Gtk::JUSTIFY_FILL); + // m_grid_imageLoad.attach(label_outputFile,0,2,2,1); + label_outputFile.show(); + + + text_outputFile.set_max_length(100); + text_outputFile.set_text("../examples/objectdetector/Annotation/output.txt"); + text_outputFile.select_region(0, text_outputFile.get_text_length()); + // m_grid_imageLoad.attach(text_outputFile,2,2,1,1); + text_outputFile.show(); + + button_saveSegnetMask.show(); + } + else + { + label_outputFile.hide(); + text_outputFile.hide(); + button_resetMarkings.hide(); + button_selectRoi.hide(); + button_saveMarked.hide(); + button_loadAnotherImage.hide(); + button_saveCropMarked.hide(); + button_resetMarkingsCurrent.hide(); + button_selectRoiCurrent.hide(); + label_annotationLabel.hide(); + text_annotationLabel.hide(); + button_saveMarkedMultiple.hide(); + button_loadNextImage.hide(); + button_saveCropMarkedMultiple.hide(); + } + + + + button_loadMainWindow.show(); + + + m_sw_imageLoad.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); + m_grid_imageLoad.show(); + // show_all_children(); + m_sw_imageLoad.show(); + } + + + bool Annotation::on_button_press_event(GdkEventButton *event) + { + // Check if the event is a left button click. + if (event->button == 1) + { + // Memorize pointer position + lastXMouse=event->x; + lastYMouse=event->y; + xPressed=event->x; + yPressed=event->y; + // Start moving the view + moveFlag=true; + // Event has been handled + // cout << lastXMouse << " " << lastYMouse << endl; + if(currentWindow == "imageLoadWindow") + { + // std::cout << lastXMouse - 10 << " " << lastYMouse - 10 << std::endl; + createDot(lastXMouse-10, lastYMouse-35); + } + if(rbutton_segnetMaskedBased.get_active() == 1) + { + std::vector <int> temp; + std::string label = text_annotationLabel.get_text(); + int Result; + std::stringstream convert(label); + if ( !(convert >> Result) ) + Result = 0; + temp.push_back(Result); + temp.push_back(lastXMouse-10); + temp.push_back(lastYMouse-35); + roiPointsForMask.push_back(temp); + } + return true; + } + + // Check if the event is a right button click. + if(event->button == 4) + { + // Memorize mouse coordinates + lastXMouse=event->x; + lastYMouse=event->y; + xReleased=event->x; + yReleased=event->y; + if(currentWindow == "imageLoadWindow") + { + createVisualROI(xPressed-10, yPressed-35, xReleased-10, yReleased-35); + } + // Display the popup menu + // m_Menu_Popup.popup(event->button, event->time); + // The event has been handled. + return true; + } + + // Check if the event is a right button click. + if(event->button == 3) + { + // Memorize mouse coordinates + lastXMouse=event->x; + lastYMouse=event->y; + xReleased=event->x; + yReleased=event->y; + createVisualROI(xPressed-10, yPressed-35, xReleased-10, yReleased-35); + // Display the popup menu + // m_Menu_Popup.popup(event->button, event->time); + // The event has been handled. + return true; + } + return false; + } + + + void Annotation::createDot(int x, int y) + { + cv::Mat img = cv::imread("../examples/objectdetector/Annotation/temp_resized_dotted.png", 1); + cv::circle(img, cv::Point(x,y), 3, cv::Scalar(255,0,0), 3, 8, 0 ); + cv::imwrite("../examples/objectdetector/Annotation/temp_resized_dotted.png", img); + image.set("../examples/objectdetector/Annotation/temp_resized_dotted.png"); + image.show(); + } + + void Annotation::createVisualROI(int x1, int y1, int x2, int y2) + { + std::string text = text_annotationLabel.get_text(); + int fontFace = cv::FONT_HERSHEY_SCRIPT_SIMPLEX; + double fontScale = 1; + int thickness = 2; + cv::Point textOrg(x1, y1); + cv::Mat img = cv::imread("../examples/objectdetector/Annotation/temp_resized_dotted.png", 1); + cv::rectangle(img, cv::Point(x1,y1), cv::Point(x2,y2), cv::Scalar(0,255,0), 3, 8, 0 ); + cv::putText(img, text, textOrg, fontFace, fontScale, cv::Scalar(0,0,255), thickness,8); + cv::imwrite("../examples/objectdetector/Annotation/temp_resized_dotted.png", img); + image.set("../examples/objectdetector/Annotation/temp_resized_dotted.png"); + image.show(); + } + + void Annotation::loadOriginalImage(std::string data, bool st) + { + cv::Mat img = cv::imread(data, 1); + int c = img.cols; + int r = img.rows; + float wTemp = 1; + float hTemp = 1; + // if(img.rows > 480 or img.cols > 640) + // { + cv::resize(img, img, cv::Size(640,480)); + wTemp = (float)c/640.0; + hTemp = (float)r/480.0; + // } + if(st) + { + widthMultiplier.push_back(wTemp); + heightMultiplier.push_back(hTemp); + } + cv::imwrite("../examples/objectdetector/Annotation/temp_resized.png", img); + cv::imwrite("../examples/objectdetector/Annotation/temp_resized_dotted.png", img); + image.set("../examples/objectdetector/Annotation/temp_resized.png"); + image.show(); + } + + void Annotation::loadOriginalImageWithSavedMarkings(std::string data, std::vector<std::vector<int> > & roi, bool st) + { + cv::Mat img = cv::imread(data, 1); + int c = img.cols; + int r = img.rows; + float wTemp = 1; + float hTemp = 1; + // if(img.rows > 480 or img.cols > 640) + // { + cv::resize(img, img, cv::Size(640,480)); + wTemp = (float)c/640.0; + hTemp = (float)r/480.0; + // } + if(st) + { + widthMultiplier.push_back(wTemp); + heightMultiplier.push_back(hTemp); + } + cv::imwrite("../examples/objectdetector/Annotation/temp_resized.png", img); + for(int i = 0; i < roi.size(); i++) + { + if(storageFileName[i] == data) + cv::rectangle(img, cv::Point(roi[i][1],roi[i][2]), cv::Point(roi[i][3],roi[i][4]), cv::Scalar(0,255,0), 3, 8, 0 ); + } + cv::imwrite("../examples/objectdetector/Annotation/temp_resized_dotted.png", img); + image.set("../examples/objectdetector/Annotation/temp_resized_dotted.png"); + image.show(); + } + + void Annotation::loadResetedMarkings(std::string data, std::vector<std::vector < int > > & roi, bool st) + { + + + cv::Mat img1 = cv::imread(data, 1); + int c = img1.cols; + int r = img1.rows; + float wTemp = 1.0; + float hTemp = 1.0; + // if(img1.rows > 480 or img1.cols > 640) + // { + cv::resize(img1, img1, cv::Size(640,480)); + wTemp = (float)c/640.0; + hTemp = (float)r/480.0; + // } + // if(st) + // { + // widthMultiplier.push_back(wTemp); + // heightMultiplier.push_back(hTemp); + // } + cv::imwrite("../examples/objectdetector/Annotation/temp_resized.png", img1); + if(roi.size() > 0) + { + for(int i = 0; i < roi.size()-1; i++) + { + // cout << roi.size() << " " << roi[i+1][3] << " " << roi[i][3] << endl; + if((roi[i+1][0] == roi[i][0]) && (roi[i+1][3] == roi[i][3])) + { + if(storageFileName[i] == filename) + cv::line(img1, cv::Point(roi[i][1],roi[i][2]), cv::Point(roi[i+1][1],roi[i+1][2]), cv::Scalar(0,255,0), 3, 8, 0 ); + } + } + } + cv::imwrite("../examples/objectdetector/Annotation/temp_resized_dotted.png", img1); + image.set("../examples/objectdetector/Annotation/temp_resized_dotted.png"); + image.show(); + } + + + + + Annotation::Annotation(): + button_selectImageFileName("Select The Image Location"), + button_loadImage("Load the image"), + button_loadAnotherImage("Load Next Image from a different location"), + rbutton_markBB("Save points for a single Bounding Box per Image"), + rbutton_cropBB("Save points and crop single Bounding Box per image"), + rbutton_markBBWithLabel("Save Multiple Bounding Boxes' Points with attached class labels"), + rbutton_cropMultipleBB("Save points and crop multiple Bounding Boxes per image with attached class labels"), + rbutton_segnetMaskedBased("Create masks based on labels, as required by segnet"), + label_annotationType(""), + text_outputFile(), + label_outputFile(""), + button_resetMarkings("Reset Markings"), + button_loadMainWindow("Load Main Window"), + button_selectRoi("Select the ROI"), + button_saveMarked("Save the ROI details of all images into the stated file"), + button_saveCropMarked("Save the ROI details with of all images into the stated file and crop the ROI and save"), + button_resetMarkingsCurrent("Reset the current Marking"), + button_selectRoiCurrent("Select the roi with mentioned label"), + button_saveMarkedMultiple("Save the ROI details with labels of all images into the stated file"), + button_saveCropMarkedMultiple("Save and crop the ROIs with labels of all images into the stated file"), + text_annotationLabel(), + label_annotationLabel(""), + button_selectDatasetFolder("Select the Dataset Folder"), + button_loadNextImage("Load Next Image from same folder"), + button_resetSegnetMaskCurrent("Reset Current Markings"), + button_selectSegnetMaskCurrent("Save the region's points with stated label"), + button_saveSegnetMask("Save all Masks"), + + button_quit("Quit") + { + storage = 0; + storageID=0; + imagesInFolder = 0; + currentWindow = "mainWindow"; + set_title("Annotator"); + set_border_width(10); + add(m_sw1); + m_grid1.set_column_spacing (10); + m_grid1.set_row_spacing (50); + + + button_selectImageFileName.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &Annotation::on_button_clicked), "selectImageFileName")); + m_grid1.attach(button_selectImageFileName,0,1,1,1); + button_selectImageFileName.show(); + + button_selectDatasetFolder.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &Annotation::on_button_clicked), "selectDatasetFolder")); + m_grid1.attach(button_selectDatasetFolder,1,1,1,1); + button_selectDatasetFolder.show(); + + Gtk::RadioButton::Group group1 = rbutton_markBB.get_group(); + rbutton_cropBB.set_group(group1); + rbutton_markBBWithLabel.set_group(group1); + rbutton_cropMultipleBB.set_group(group1); + rbutton_segnetMaskedBased.set_group(group1); + rbutton_markBB.set_active(); + m_grid1.attach(rbutton_markBB,0,2,1,1); + rbutton_markBB.show(); + m_grid1.attach(rbutton_cropBB,1,2,1,1); + rbutton_cropBB.show(); + m_grid1.attach(rbutton_markBBWithLabel,2,2,1,1); + rbutton_markBBWithLabel.show(); + m_grid1.attach(rbutton_cropMultipleBB,0,3,2,1); + rbutton_cropMultipleBB.show(); + m_grid1.attach(rbutton_segnetMaskedBased,2,3,2,1); + rbutton_segnetMaskedBased.show(); + + button_loadImage.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &Annotation::on_button_clicked), "loadImage")); + m_grid1.attach(button_loadImage,0,4,1,1); + button_loadImage.show(); + + /* + Gtk::RadioButton::Group group1 = rbutton_extraLayerLMDB.get_group(); + rbutton_extraLayerLEVELDB.set_group(group15); + rbutton_extraLayerLMDB.set_active(); + m_grid_extraLayerType.attach(rbutton_extraLayerLMDB,2,16,1,1); + rbutton_extraLayerLMDB.show(); + m_grid_extraLayerType.attach(rbutton_extraLayerLEVELDB,3,16,1,1); + rbutton_extraLayerLEVELDB.show(); + */ + button_quit.signal_pressed().connect(sigc::mem_fun(*this,&Annotation::close)); + m_grid1.attach(button_quit,0,10,1,1); + + + + //imageLoadWindow + + button_loadMainWindow.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &Annotation::on_button_clicked), "loadMainWindow")); + m_grid_imageLoad.attach(button_loadMainWindow,0,10,1,1); + button_resetMarkings.show(); + + m_grid_imageLoad.attach(image,0,1,2,1); + + + + label_annotationType.set_text("Annotion Type: Selecting Single Bounding Box"); + label_annotationType.set_line_wrap(); + label_annotationType.set_justify(Gtk::JUSTIFY_FILL); + m_grid_imageLoad.attach(label_annotationType,0,0,2,1); + label_annotationType.show(); + + button_resetMarkings.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &Annotation::on_button_clicked), "resetMarkings")); + m_grid_imageLoad.attach(button_resetMarkings,0,3,1,1); + button_resetMarkings.show(); + + button_resetMarkingsCurrent.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &Annotation::on_button_clicked), "resetMarkingsCurrent")); + m_grid_imageLoad.attach(button_resetMarkingsCurrent,0,3,1,1); + button_resetMarkingsCurrent.show(); + + button_resetSegnetMaskCurrent.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &Annotation::on_button_clicked), "resetSegnetMaskCurrent")); + m_grid_imageLoad.attach(button_resetSegnetMaskCurrent,0,3,1,1); + button_resetSegnetMaskCurrent.show(); + + button_selectRoi.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &Annotation::on_button_clicked), "selectRoi")); + m_grid_imageLoad.attach(button_selectRoi,0,4,1,1); + button_selectRoi.show(); + + label_annotationLabel.set_text("Give a label to ROI: "); + label_annotationLabel.set_line_wrap(); + label_annotationLabel.set_justify(Gtk::JUSTIFY_FILL); + m_grid_imageLoad.attach(label_annotationLabel,0,4,1,1); + label_annotationLabel.show(); + + + text_annotationLabel.set_max_length(100); + text_annotationLabel.set_text(""); + text_annotationLabel.select_region(0, text_annotationLabel.get_text_length()); + m_grid_imageLoad.attach(text_annotationLabel,1,4,1,1); + text_annotationLabel.show(); + + button_selectRoiCurrent.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &Annotation::on_button_clicked), "selectRoiCurrent")); + m_grid_imageLoad.attach(button_selectRoiCurrent,2,4,1,1); + button_selectRoiCurrent.show(); + + button_selectSegnetMaskCurrent.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &Annotation::on_button_clicked), "selectSegnetMaskCurrent")); + m_grid_imageLoad.attach(button_selectSegnetMaskCurrent,2,4,1,1); + button_selectSegnetMaskCurrent.show(); + + button_loadAnotherImage.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &Annotation::on_button_clicked), "loadAnotherImage")); + m_grid_imageLoad.attach(button_loadAnotherImage,0,5,2,1); + button_loadMainWindow.show(); + + button_loadNextImage.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &Annotation::on_button_clicked), "loadNextImage")); + m_grid_imageLoad.attach(button_loadNextImage,2,5,1,1); + button_loadNextImage.show(); + + label_outputFile.set_text("OutPut Text File Name: "); + label_outputFile.set_line_wrap(); + label_outputFile.set_justify(Gtk::JUSTIFY_FILL); + m_grid_imageLoad.attach(label_outputFile,0,6,1,1); + label_outputFile.show(); + + + text_outputFile.set_max_length(100); + text_outputFile.set_text("../examples/objectdetector/Annotation/output.txt"); + text_outputFile.select_region(0, text_outputFile.get_text_length()); + m_grid_imageLoad.attach(text_outputFile,1,6,1,1); + text_outputFile.show(); + + button_saveMarked.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &Annotation::on_button_clicked), "saveMarked")); + m_grid_imageLoad.attach(button_saveMarked,2,6,1,1); + button_saveMarked.show(); + + button_saveCropMarked.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &Annotation::on_button_clicked), "saveCropMarked")); + m_grid_imageLoad.attach(button_saveCropMarked,2,6,2,1); + button_saveCropMarked.show(); + + button_saveMarkedMultiple.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &Annotation::on_button_clicked), "saveMarkedMultiple")); + m_grid_imageLoad.attach(button_saveMarkedMultiple,2,6,1,1); + button_saveMarkedMultiple.show(); + + button_saveCropMarkedMultiple.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &Annotation::on_button_clicked), "saveCropMarkedMultiple")); + m_grid_imageLoad.attach(button_saveCropMarkedMultiple,2,6,1,1); + button_saveCropMarkedMultiple.show(); + + button_saveSegnetMask.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &Annotation::on_button_clicked), "saveSegnetMask")); + m_grid_imageLoad.attach(button_saveSegnetMask,2,6,1,1); + button_saveSegnetMask.show(); + + m_sw_imageLoad.add(m_grid_imageLoad); + + m_sw1.add(m_grid1); + m_sw1.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); + // m_grid1.show(); + show_all_children(); + m_sw1.show(); + + } + + Annotation::~Annotation() + {} + + void Annotation::on_button_clicked(Glib::ustring data) + { + if(data == "selectImageFileName" || data == "loadAnotherImage") + { + if(data == "selectImageFileName") + file_folder = "file"; + // Create the dialog box FileChooser + Gtk::FileChooserDialog dialog("Please choose a file",Gtk::FILE_CHOOSER_ACTION_OPEN); + dialog.set_transient_for(*this); + + //Add response buttons the the dialog: + dialog.add_button("_Cancel", Gtk::RESPONSE_CANCEL); + dialog.add_button("_Open", Gtk::RESPONSE_OK); + + //Add filters, so that only certain file types can be selected: + Glib::RefPtr<Gtk::FileFilter> filter_any = Gtk::FileFilter::create(); + filter_any->set_name("Any files"); + filter_any->add_pattern("*"); + dialog.add_filter(filter_any); + + Glib::RefPtr<Gtk::FileFilter> filter_text = Gtk::FileFilter::create(); + filter_text->set_name("Text files"); + filter_text->add_mime_type("text/plain"); + dialog.add_filter(filter_text); + + Glib::RefPtr<Gtk::FileFilter> filter_cpp = Gtk::FileFilter::create(); + filter_cpp->set_name("C/C++ files"); + filter_cpp->add_mime_type("text/x-c"); + filter_cpp->add_mime_type("text/x-c++"); + filter_cpp->add_mime_type("text/x-c-header"); + dialog.add_filter(filter_cpp); + + + + //Show the dialog and wait for a user response: + int result = dialog.run(); + + //Handle the response: + switch(result) + { + case(Gtk::RESPONSE_OK): + { + // The user selected a file + std::cout << "Open clicked." << std::endl; + filename = dialog.get_filename(); + std::cout << "File selected: " << filename << std::endl; + break; + } + case(Gtk::RESPONSE_CANCEL): + { + // The user clicked cancel + std::cout << "Cancel clicked." << std::endl; + break; + } + default: + { + // The user closed the dialog box + std::cout << "Unexpected button clicked." << std::endl; + break; + } + } + if(data == "loadAnotherImage") + loadOriginalImage(filename, 1); + } + else if(data == "selectDatasetFolder") + { + file_folder = "folder"; + // Create the dialog box FileChooser + Gtk::FileChooserDialog dialog("Please choose a file",Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER); + dialog.set_transient_for(*this); + + //Add response buttons the the dialog: + dialog.add_button("_Cancel", Gtk::RESPONSE_CANCEL); + dialog.add_button("_Open", Gtk::RESPONSE_OK); + + //Add filters, so that only certain file types can be selected: + Glib::RefPtr<Gtk::FileFilter> filter_any = Gtk::FileFilter::create(); + filter_any->set_name("Any files"); + filter_any->add_pattern("*"); + dialog.add_filter(filter_any); + + Glib::RefPtr<Gtk::FileFilter> filter_text = Gtk::FileFilter::create(); + filter_text->set_name("Text files"); + filter_text->add_mime_type("text/plain"); + dialog.add_filter(filter_text); + + Glib::RefPtr<Gtk::FileFilter> filter_cpp = Gtk::FileFilter::create(); + filter_cpp->set_name("C/C++ files"); + filter_cpp->add_mime_type("text/x-c"); + filter_cpp->add_mime_type("text/x-c++"); + filter_cpp->add_mime_type("text/x-c-header"); + dialog.add_filter(filter_cpp); + + + + //Show the dialog and wait for a user response: + int result = dialog.run(); + + //Handle the response: + switch(result) + { + case(Gtk::RESPONSE_OK): + { + // The user selected a file + std::cout << "Open clicked." << std::endl; + foldername = dialog.get_filename(); + std::cout << "File selected: " << filename << std::endl; + break; + } + case(Gtk::RESPONSE_CANCEL): + { + // The user clicked cancel + std::cout << "Cancel clicked." << std::endl; + break; + } + default: + { + // The user closed the dialog box + std::cout << "Unexpected button clicked." << std::endl; + break; + } + } + if(data == "loadAnotherImage") + loadOriginalImage(filename, 1); + } + else if(data == "loadNextImage") + { + if(imagesInFolder > 0) + { + filename = imageFileNames[--imagesInFolder]; + loadOriginalImage(filename, 1); + } + } + else if(data == "loadImage") + { + showWindow_imageLoad(filename); + } + else if(data == "loadMainWindow") + { + showWindow_main(); + } + else if(data == "resetMarkings") + { + loadOriginalImage(filename, 0); + Gtk::MessageDialog dialog(*this, "Message"); + dialog.set_secondary_text("Recently marked Points have been discarded"); + dialog.run(); + } + else if(data == "resetMarkingsCurrent") + { + loadOriginalImageWithSavedMarkings(filename, storageROILocationCurrent, 0); + Gtk::MessageDialog dialog(*this, "Message"); + dialog.set_secondary_text("Recently marked Points have been discarded"); + dialog.run(); + } + else if(data == "resetSegnetMaskCurrent") + { + /* int length = roiPointsForMask.size(); + std::string label = text_annotationLabel.get_text(); + int Result; + istringstream convert(label); + if ( !(convert >> Result) ) + Result = 0; + for( int i = length-1; i > -1; i--) + { + if(roiPointsForMask[roiPointsForMask.size()-1][0] == Result) + roiPointsForMask.pop_back(); + else + break; + } + */ + roiPointsForMask.clear(); + loadResetedMarkings(filename, roiPointsForMaskPermanent, 0); + Gtk::MessageDialog dialog(*this, "Message"); + dialog.set_secondary_text("Recently marked Points have been discarded"); + dialog.run(); + } + else if(data == "selectRoi") + { + std::vector<int> temp; + temp.push_back(xPressed-10); + temp.push_back(yPressed-35); + temp.push_back(xReleased-10); + temp.push_back(yReleased-35); + + storageROILocation.push_back(temp); + storageFileName.push_back(filename); + storage++; + loadOriginalImage(filename, 1); + Gtk::MessageDialog dialog(*this, "Message"); + dialog.set_secondary_text("ROI has been added to the list"); + dialog.run(); + } + else if(data == "selectRoiCurrent") + { + std::vector<int> temp; + std::string label = text_annotationLabel.get_text(); + int Result; + std::stringstream convert(label); + if(!(convert >> Result)) + Result = 0; + temp.push_back(Result); + temp.push_back(xPressed-10); + temp.push_back(yPressed-35); + temp.push_back(xReleased-10); + temp.push_back(yReleased-35); + storageROILocationCurrent.push_back(temp); + storageFileName.push_back(filename); + storage++; + std::cout << "filename = " << filename << std::endl; + loadOriginalImageWithSavedMarkings(filename, storageROILocationCurrent, 1); + Gtk::MessageDialog dialog(*this, "Message"); + dialog.set_secondary_text("ROI has been added to the list"); + dialog.run(); + + } + else if(data == "selectSegnetMaskCurrent") + { + storageID++; + cv::Mat img1 = cv::imread(filename, 1); + int c = img1.cols; + int r = img1.rows; + cv::resize(img1, img1, cv::Size(640,480)); + float wTemp = (float)c/640.0; + float hTemp = (float)r/480.0; + + for(unsigned int i = 0; i < roiPointsForMask.size(); ++i) + { + std::vector<int> temp; + temp.push_back(roiPointsForMask[i][0]); + temp.push_back(roiPointsForMask[i][1]); + temp.push_back(roiPointsForMask[i][2]); + temp.push_back(storageID); + roiPointsForMaskPermanent.push_back(temp); + storageFileName.push_back(filename); + widthMultiplier.push_back(wTemp); + heightMultiplier.push_back(hTemp); + storage++; + } + roiPointsForMask.clear(); + + loadResetedMarkings(filename, roiPointsForMaskPermanent, 1); + Gtk::MessageDialog dialog(*this, "Message"); + dialog.set_secondary_text("ROI has been added to the list"); + dialog.run(); + // roiPointsForMaskPermanent + } + else if(data == "saveMarked") + { + std::ofstream myfile; + myfile.open(text_outputFile.get_text()); + for(unsigned int i = 0; i < storage; ++i) + { + myfile << storageFileName[i] << " " << int(storageROILocation[i][0]*widthMultiplier[i]) << " " << int(storageROILocation[i][1]*heightMultiplier[i]) << " " << int(storageROILocation[i][2]*widthMultiplier[i]) << " " << int(storageROILocation[i][3]*heightMultiplier[i]) << std::endl; + } + Gtk::MessageDialog dialog(*this, "Message"); + dialog.set_secondary_text("The details have been saved into the file " + text_outputFile.get_text()); + + dialog.run(); + } + else if(data == "saveMarkedMultiple") + { + std::ofstream myfile; + myfile.open(text_outputFile.get_text()); + std::string tempFileName(storageFileName[0]); + + for(unsigned int i = 0; i < storage; i++) + { + if(i == 0) + { + myfile << storageFileName[i] << " " << storageROILocationCurrent[i][0] << " " << int(storageROILocationCurrent[i][1]*widthMultiplier[i]) << " " << int(storageROILocationCurrent[i][2]*heightMultiplier[i]) << " " << int(storageROILocationCurrent[i][3]*widthMultiplier[i]) << " " << int(storageROILocationCurrent[i][4]*heightMultiplier[i]) << " "; + } + else + { + if(tempFileName == storageFileName[i]) + { + myfile << storageROILocationCurrent[i][0] << " " << int(storageROILocationCurrent[i][1]*widthMultiplier[i]) << " " << int(storageROILocationCurrent[i][2]*heightMultiplier[i]) << " " << int(storageROILocationCurrent[i][3]*widthMultiplier[i]) << " " << int(storageROILocationCurrent[i][4]*heightMultiplier[i]) << " "; + } + else + { + myfile << std::endl; + myfile << storageFileName[i] << " " << storageROILocationCurrent[i][0] << " " << int(storageROILocationCurrent[i][1]*widthMultiplier[i]) << " " << int(storageROILocationCurrent[i][2]*heightMultiplier[i]) << " " << int(storageROILocationCurrent[i][3]*widthMultiplier[i]) << " " << int(storageROILocationCurrent[i][4]*heightMultiplier[i]) << " "; + tempFileName = storageFileName[i]; + } + } + } + Gtk::MessageDialog dialog(*this, "Message"); + dialog.set_secondary_text("The details have been saved into the file " + text_outputFile.get_text()); + + dialog.run(); + } + else if(data == "saveCropMarked") + { + std::ofstream myfile; + myfile.open(text_outputFile.get_text()); + for(int i = 0; i < storage; i++) + { + myfile << storageFileName[i] << " " << int(storageROILocation[i][0]*widthMultiplier[i]) << " " << int(storageROILocation[i][1]*heightMultiplier[i]) << " " << int(storageROILocation[i][2]*widthMultiplier[i]) << " " << int(storageROILocation[i][3]*heightMultiplier[i]) << std::endl; + std::string str2 = storageFileName[i].substr (0,storageFileName[i].length()-4); + str2 += "_cropped.png"; + cv::Rect roi(int(storageROILocation[i][0]*widthMultiplier[i]), int(storageROILocation[i][1]*heightMultiplier[i]), int(storageROILocation[i][2]*widthMultiplier[i]) - int(storageROILocation[i][0]*widthMultiplier[i]), int(storageROILocation[i][3]*heightMultiplier[i]) - int(storageROILocation[i][1]*heightMultiplier[i])); + cv::Mat img = cv::imread(storageFileName[i],1); + cv::Mat image_roi = img(roi); + imwrite(str2, image_roi); + } + + //Create the cv::Mat with the ROI you need, where "image" is the cv::Mat you want to extract the ROI from + + Gtk::MessageDialog dialog(*this, "Message"); + dialog.set_secondary_text("The details have been saved into the file " + text_outputFile.get_text() + " and the cropped images have been saved"); + + // dialog.run(); + } + else if(data == "saveSegnetMask") + { + std::ofstream myfile; + myfile.open(text_outputFile.get_text()); + std::string tempFileName = storageFileName[0]; + + for(unsigned int i = 0; i < storage; i++) + { + if(i == 0) + { + myfile << storageFileName[i] << " ID" << roiPointsForMaskPermanent[i][3] << " " << roiPointsForMaskPermanent[i][0] << " " << int(roiPointsForMaskPermanent[i][1]*widthMultiplier[i]) << " " << int(roiPointsForMaskPermanent[i][2]*heightMultiplier[i]) << " "; + } + else + { + if(tempFileName == storageFileName[i]) + { + if(roiPointsForMaskPermanent[i][3] == roiPointsForMaskPermanent[i-1][3]) + myfile << int(roiPointsForMaskPermanent[i][1]*widthMultiplier[i]) << " " << int(roiPointsForMaskPermanent[i][2]*heightMultiplier[i]) << " "; + else + myfile << " ID" << roiPointsForMaskPermanent[i][3] << " " << roiPointsForMaskPermanent[i][0] << " " << int(roiPointsForMaskPermanent[i][1]*widthMultiplier[i]) << " " << int(roiPointsForMaskPermanent[i][2]*heightMultiplier[i]) << " "; + } + else + { + myfile << std::endl; + myfile << storageFileName[i] << " ID" << roiPointsForMaskPermanent[i][3] << " " << roiPointsForMaskPermanent[i][0] << " " << int(roiPointsForMaskPermanent[i][1]*widthMultiplier[i]) << " " << int(roiPointsForMaskPermanent[i][2]*heightMultiplier[i]) << " "; + tempFileName = storageFileName[i]; + } + } + } + Gtk::MessageDialog dialog(*this, "Message"); + dialog.set_secondary_text("The details have been saved into the file " + text_outputFile.get_text()); + dialog.run(); + + } + else if(data == "saveCropMarkedMultiple") + { + std::ofstream myfile; + myfile.open(text_outputFile.get_text()); + std::string tempFileName = storageFileName[0]; + + for(unsigned int i = 0; i < storage; i++) + { + // cout << storageFileName[i] << " " << storageROILocationCurrent[i][0] << " " << widthMultiplier[i] << " " << heightMultiplier[i] << endl; + if(i == 0) + { + myfile << storageFileName[i] << " " << storageROILocationCurrent[i][0] << " " << int(storageROILocationCurrent[i][1]*widthMultiplier[i]) << " " << int(storageROILocationCurrent[i][2]*heightMultiplier[i]) << " " << int(storageROILocationCurrent[i][3]*widthMultiplier[i]) << " " << int(storageROILocationCurrent[i][4]*heightMultiplier[i]) << " "; + } + else + { + if(tempFileName == storageFileName[i]) + { + myfile << storageROILocationCurrent[i][0] << " " << int(storageROILocationCurrent[i][1]*widthMultiplier[i]) << " " << int(storageROILocationCurrent[i][2]*heightMultiplier[i]) << " " << int(storageROILocationCurrent[i][3]*widthMultiplier[i]) << " " << int(storageROILocationCurrent[i][4]*heightMultiplier[i]) << " "; + } + else + { + myfile << std::endl; + myfile << storageFileName[i] << " " << storageROILocationCurrent[i][0] << " " << int(storageROILocationCurrent[i][1]*widthMultiplier[i]) << " " << int(storageROILocationCurrent[i][2]*heightMultiplier[i]) << " " << int(storageROILocationCurrent[i][3]*widthMultiplier[i]) << " " << int(storageROILocationCurrent[i][4]*heightMultiplier[i]) << " "; + tempFileName = storageFileName[i]; + } + } + std::string str2 = storageFileName[i].substr (0,storageFileName[i].length()-4); + std::string result; + std::ostringstream convert; + convert << i; + result = convert.str(); + str2 = str2 + "_cropped_"; + std::string label; + std::ostringstream convert1; + convert1 << storageROILocationCurrent[i][0]; + label = convert.str(); + str2 = str2 + label + "_" + result + ".png"; + cv::Rect roi(int(storageROILocationCurrent[i][1]*widthMultiplier[i]), int(storageROILocationCurrent[i][2]*heightMultiplier[i]), int(storageROILocationCurrent[i][3]*widthMultiplier[i]) - int(storageROILocationCurrent[i][1]*widthMultiplier[i]), int(storageROILocationCurrent[i][4]*heightMultiplier[i]) - int(storageROILocationCurrent[i][2]*heightMultiplier[i])); + cv::Mat img = cv::imread(storageFileName[i],1); + cv::Mat image_roi = img(roi); + imwrite(str2, image_roi); + } + Gtk::MessageDialog dialog(*this, "Message"); + dialog.set_secondary_text("The details have been saved into the file " + text_outputFile.get_text()); + dialog.run(); + } + } + + } +} \ No newline at end of file diff --git a/detectors/src/global2D/annotation/Annotator.cpp b/detectors/src/global2D/annotation/Annotator.cpp new file mode 100644 index 00000000..b46efe5c --- /dev/null +++ b/detectors/src/global2D/annotation/Annotator.cpp @@ -0,0 +1,20 @@ +#include "od/detectors/global2D/annotation/Annotator.h" + +namespace od +{ + namespace g2d + { + int Annotator::train() + { + return 1; + } + + void Annotator::startAnnotator(int argc, char *argv[]) + { + auto app = Gtk::Application::create(argc, argv, std::string("org.gtkmm.example")); + Annotation annotation; + annotation.set_default_geometry(10000, 10000); + app->run(annotation); + } + } +} diff --git a/detectors/src/global2D/detection/ConvClassification.cpp b/detectors/src/global2D/detection/ConvClassification.cpp index 93068de3..5e64bf96 100644 --- a/detectors/src/global2D/detection/ConvClassification.cpp +++ b/detectors/src/global2D/detection/ConvClassification.cpp @@ -102,6 +102,34 @@ namespace od std::cout << "classified image is digit " << max_i << std::endl << std::endl; } + std::vector<float> ConvClassification::classifyMultiLabel() + { +#if(WITH_GPU) + caffe::Caffe::SetDevice(0); + caffe::Caffe::set_mode(caffe::Caffe::GPU); +#else + caffe::Caffe::set_mode(caffe::Caffe::CPU); +#endif + caffe::Net<float> net(networkFileLocation, caffe::TEST); + net.CopyTrainedLayersFrom(weightModelFileLoaction); + + float type = 0.0; + const std::vector<caffe::Blob<float>*> & result = net.Forward(inputBlob, &type); + std::cout << std::endl << "****** OUTPUT *******" << std::endl; +/* cout << "The AAM points are " << endl; + cout << net.output_blobs()[0] << endl; + for (int i = 0; i < 15; i++) + { + cout << result[0]->cpu_data()[2*i]*255 << " " << result[0]->cpu_data()[2*i+1]*255 << " "; + } + cout << endl; +*/ + caffe::Blob<float> * output_layer = net.output_blobs()[0]; + const float * begin = output_layer->cpu_data(); + const float * end = begin + output_layer->channels(); + return std::vector<float>(begin, end); + } + void ConvClassification::init() { } @@ -121,6 +149,119 @@ namespace od std::cout << "not implemented, use with shared_ptr<SceneImage>" <<std::endl; return nullptr; } + + std::vector<float> ConvClassification::runMultiClassClassifier() + { +#if(WITH_GPU) + caffe::Caffe::SetDevice(0); + caffe::Caffe::set_mode(caffe::Caffe::GPU); +#else + caffe::Caffe::set_mode(caffe::Caffe::CPU); +#endif + /* Load the network. */ + caffe::Net<float> net_m(networkFileLocation, caffe::TEST); + net_m.CopyTrainedLayersFrom(weightModelFileLoaction); + caffe::Blob<float>* input_layer = net_m.input_blobs()[0]; + num_channels_ = input_layer->channels(); + input_geometry_ = cv::Size(input_layer->width(), input_layer->height()); + input_layer->Reshape(1, num_channels_, + input_geometry_.height, input_geometry_.width); + net_m.Reshape(); + std::vector<cv::Mat> input_channels; + + int width = input_layer->width(); + int height = input_layer->height(); + float* input_data = input_layer->mutable_cpu_data(); + for (unsigned int i = 0; i < input_layer->channels(); ++i) + { + cv::Mat channel(height, width, CV_32FC1, input_data); + input_channels.push_back(channel); + input_data += width * height; + } + + +// net_m.Forward(); + caffe::Blob<float>* output_layer = net_m.output_blobs()[0]; + const float * begin = output_layer->cpu_data(); + const float * end = begin + output_layer->channels(); + return std::vector<float>(begin, end); + } + + int ConvClassification::runMultiClassClassifierPythonMode() + { + std::string mode = ""; +#if(WITH_GPU) + mode = "gpu"; +#else + mode = "cpu"; +#endif + std::string cmd = "python ../examples/objectdetector/AAM_Classify/classify.py " + networkFileLocation + " " + weightModelFileLoaction + " " + imageFileLocation + " " + outputFileLocation + " " + mode; + return system(cmd.c_str()); + + } + + void ConvClassification::setSegnetLocation(const std::string & location) + { + segnetLocation = location; + } + void ConvClassification::setImageGroundTruthFileLocation(const std::string & location) + { + imageGroundTruthFileLocation = location; + } + void ConvClassification::setColorLocation(const std::string & location) + { + colorLocation = location; + } + int ConvClassification::runSegnetBasedClassifierPythonMode() + { + std::string mode = ""; +#if(WITH_GPU) + mode = "gpu"; +#else + mode = "cpu"; +#endif +// string segnet_location = argv[1]; +// string model_file = argv[2]; +// string trained_file = argv[3]; +// string test_image = argv[4]; +// string test_image_gt = argv[5]; +// string color = argv[6]; +// string mode = argv[7]; + std::string cmd = "python ../examples/objectdetector/Segnet_Classify/test.py " + segnetLocation + " " + networkFileLocation + " " + weightModelFileLoaction + " " + imageFileLocation + " " + imageGroundTruthFileLocation + " " + colorLocation + " " + mode; + return system(cmd.c_str()); + } +/* + std::vector<float> ConvClassification::Predict(const cv::Mat& img) + { + Blob<float>* input_layer = net_m.input_blobs()[0]; + input_layer->Reshape(1, num_channels_, + input_geometry_.height, input_geometry_.width); + net_m.Reshape(); + + std::vector<cv::Mat> input_channels; + WrapInputLayer(&input_channels); + net_m.Forward(); + Blob<float>* output_layer = net_m.output_blobs()[0]; + const float* begin = output_layer->cpu_data(); + const float* end = begin + output_layer->channels(); + return std::vector<float>(begin, end); + } + + void ConvClassification::WrapInputLayer(std::vector<cv::Mat>* input_channels) + { + Blob<float>* input_layer = net_m.input_blobs()[0]; + + int width = input_layer->width(); + int height = input_layer->height(); + float* input_data = input_layer->mutable_cpu_data(); + for (int i = 0; i < input_layer->channels(); ++i) + { + cv::Mat channel(height, width, CV_32FC1, input_data); + input_channels->push_back(channel); + input_data += width * height; + } + } +*/ diff --git a/detectors/src/global2D/training/Network.cpp b/detectors/src/global2D/training/Network.cpp index 927ba7b2..24216d49 100644 --- a/detectors/src/global2D/training/Network.cpp +++ b/detectors/src/global2D/training/Network.cpp @@ -8,8 +8,8 @@ #include "od/detectors/global2D/training/ExtraWindow.h" #include "od/detectors/global2D/training/Node.h" -struct Node * newHead; -struct Node * headLayer = new Node; +struct Node *newHead; +struct Node *headLayer = new Node; NetworkCreator::NetworkCreator(): label_networkFileName(""), @@ -138,25 +138,76 @@ NetworkCreator::NetworkCreator(): text_lossLayerTop(), text_lossLayerBottom1(), text_lossLayerBottom2(), + text_lossLayerBottom3(), text_lossLayerName(), label_lossLayerTop(""), label_lossLayerBottom1(""), label_lossLayerBottom2(""), + label_lossLayerBottom3(""), label_lossLayerName(""), button_setLossParameters("Add this Loss Layer"), label_lossLayerNormalization(""), + rbutton_lossLayerFull("FULL"), + rbutton_lossLayerValid("VALID"), + rbutton_lossLayerBatch("BATCH_SIZE"), + rbutton_lossLayerL1("L1"), + rbutton_lossLayerL2("L2"), + label_lossLayerNorm(""), button_addMoreLayer5("Add More Layers"), + button_setExtraParameters("Add this Layer"), title_normalizationLayerType(""), title_lossLayerType(""), title_extraLayerType(""), + label_extraLayerTop1(""), + label_extraLayerTop2(""), + label_extraLayerBottom1(""), + label_extraLayerName(""), + text_extraLayerTop1(), + text_extraLayerTop2(), + text_extraLayerBottom1(), + text_extraLayerName(), + text_extraLayerTopK(), + label_extraLayerTopK(""), + label_extraLayerOutMaxVal(""), + rbutton_extraLayerOutMaxValTrue("true"), + rbutton_extraLayerOutMaxValFalse("false"), + text_extraLayerBottom2(), + label_extraLayerBottom2(""), + label_extraLayerPhase(""), + rbutton_extraLayerTrain("TRAIN"), + rbutton_extraLayerTest("TEST"), + label_extraLayerScale(""), + label_extraLayerNewHeight(""), + label_extraLayerNewWidth(""), + label_extraLayerCropSize(""), + label_extraLayerMeanFile(""), + rbutton_extraLayerScaleYes("Uncomment"), rbutton_extraLayerScaleNo("Leave Commented"), + rbutton_extraLayerNewHeightYes("Uncomment"), rbutton_extraLayerNewHeightNo("Leave Commented"), + rbutton_extraLayerNewWidthYes("Uncomment"), rbutton_extraLayerNewWidthNo("Leave Commented"), + rbutton_extraLayerCropSizeYes("Uncomment"), rbutton_extraLayerCropSizeNo("Leave Commented"), + rbutton_extraLayerMeanFileYes("Uncomment"), rbutton_extraLayerMeanFileNo("Leave Commented"), + text_extraLayerScale(), + text_extraLayerNewHeight(), + text_extraLayerNewWidth(), + text_extraLayerCropSize(), + text_extraLayerMeanFile(), + label_extraLayerSource(""), + label_extraLayerBatchSize(""), + text_extraLayerSource(), + text_extraLayerBatchSize(), + label_extraLayerBackend(""), + rbutton_extraLayerLMDB("LMBD"), rbutton_extraLayerLEVELDB("LEVELDB"), button_displayCnnLayers("Display the Network"), - button_editMore("Add more layers"), + button_editMore("Add more layers at end"), box_fullCnnLayerMatter(Gtk::ORIENTATION_VERTICAL), button_deleteLayerAtEnd("Delete Layer at the end"), + button_deleteSelectedLayer("Delete the Selected Layer"), + button_appendLayerAfter("Add Layer After Selected Layer"), button_saveFile("Save File") { + appendStatus = false; numLayers = 0; set_title("Network Creator"); set_border_width(10); @@ -400,11 +451,6 @@ NetworkCreator::NetworkCreator(): row_lossLayerType = *(ref_lossLayerType->append()); row_lossLayerType[column_lossLayerType.m_col_id] = 7; - row_lossLayerType[column_lossLayerType.m_col_name] = "EuclideanLoss"; - row_lossLayerType[column_lossLayerType.m_col_extra] = "Euclidean Loss Layer"; - - row_lossLayerType = *(ref_lossLayerType->append()); - row_lossLayerType[column_lossLayerType.m_col_id] = 8; row_lossLayerType[column_lossLayerType.m_col_name] = "SigmoidCrossEntropyLoss"; row_lossLayerType[column_lossLayerType.m_col_extra] = "Sigmoid Cross Entropy Loss Layer"; @@ -424,7 +470,7 @@ NetworkCreator::NetworkCreator(): //level 5 - label_extraLayerType.set_text("6) A few extra layers:\n(Append these layer as next layer) "); + label_extraLayerType.set_text("6) Data Layers and a few extra layers:\n(Append these layer as next layer) "); label_extraLayerType.set_line_wrap(); label_extraLayerType.set_justify(Gtk::JUSTIFY_FILL); m_grid1.attach(label_extraLayerType,0,5,2,1); @@ -451,8 +497,18 @@ NetworkCreator::NetworkCreator(): row_extraLayerType = *(ref_extraLayerType->append()); row_extraLayerType[column_extraLayerType.m_col_id] = 4; - row_extraLayerType[column_extraLayerType.m_col_name] = "Eltwise"; - row_extraLayerType[column_extraLayerType.m_col_extra] = "Element Wise Operation Layer"; + row_extraLayerType[column_extraLayerType.m_col_name] = "ImageData"; + row_extraLayerType[column_extraLayerType.m_col_extra] = "Data layer in image format"; + + row_extraLayerType = *(ref_extraLayerType->append()); + row_extraLayerType[column_extraLayerType.m_col_id] = 5; + row_extraLayerType[column_extraLayerType.m_col_name] = "Data"; + row_extraLayerType[column_extraLayerType.m_col_extra] = "Data layer in LMDB/LEVELDB format"; + + row_extraLayerType = *(ref_extraLayerType->append()); + row_extraLayerType[column_extraLayerType.m_col_id] = 5; + row_extraLayerType[column_extraLayerType.m_col_name] = "HDF5Data"; + row_extraLayerType[column_extraLayerType.m_col_extra] = "Data layer in HDF5 format"; combo_extraLayerType.pack_start(column_extraLayerType.m_col_id); combo_extraLayerType.pack_start(column_extraLayerType.m_col_name); @@ -1139,49 +1195,89 @@ NetworkCreator::NetworkCreator(): m_grid_lossLayerType.attach(text_lossLayerBottom2,2,2,1,1); text_lossLayerBottom2.show(); + label_lossLayerBottom3.set_text("Bottom3 Layer Name: "); + label_lossLayerBottom3.set_line_wrap(); + label_lossLayerBottom3.set_justify(Gtk::JUSTIFY_FILL); + m_grid_lossLayerType.attach(label_lossLayerBottom3,0,3,2,1); + label_lossLayerBottom3.show(); + + text_lossLayerBottom3.set_max_length(100); + text_lossLayerBottom3.set_text(""); + text_lossLayerBottom3.select_region(0, text_lossLayerBottom3.get_text_length()); + m_grid_lossLayerType.attach(text_lossLayerBottom3,2,3,1,1); + text_lossLayerBottom3.show(); + + label_lossLayerTop.set_text("Top Layer Name: "); label_lossLayerTop.set_line_wrap(); label_lossLayerTop.set_justify(Gtk::JUSTIFY_FILL); - m_grid_lossLayerType.attach(label_lossLayerTop,0,3,2,1); + m_grid_lossLayerType.attach(label_lossLayerTop,0,4,2,1); label_lossLayerTop.show(); text_lossLayerTop.set_max_length(100); text_lossLayerTop.set_text(""); text_lossLayerTop.select_region(0, text_lossLayerTop.get_text_length()); - m_grid_lossLayerType.attach(text_lossLayerTop,2,3,1,1); + m_grid_lossLayerType.attach(text_lossLayerTop,2,4,1,1); text_lossLayerTop.show(); label_lossLayerName.set_text("Current Layer Name: "); label_lossLayerName.set_line_wrap(); label_lossLayerName.set_justify(Gtk::JUSTIFY_FILL); - m_grid_lossLayerType.attach(label_lossLayerName,0,4,2,1); + m_grid_lossLayerType.attach(label_lossLayerName,0,5,2,1); label_lossLayerName.show(); text_lossLayerName.set_max_length(100); text_lossLayerName.set_text(""); text_lossLayerName.select_region(0, text_lossLayerName.get_text_length()); - m_grid_lossLayerType.attach(text_lossLayerName,2,4,1,1); + m_grid_lossLayerType.attach(text_lossLayerName,2,5,1,1); text_lossLayerName.show(); label_lossLayerNormalize.set_text("Normalize: \n(bool value)"); label_lossLayerNormalize.set_line_wrap(); label_lossLayerNormalize.set_justify(Gtk::JUSTIFY_FILL); - m_grid_lossLayerType.attach(label_lossLayerNormalize,0,5,2,1); + m_grid_lossLayerType.attach(label_lossLayerNormalize,0,6,2,1); label_lossLayerNormalize.show(); text_lossLayerNormalize.set_max_length(100); text_lossLayerNormalize.set_text(""); text_lossLayerNormalize.select_region(0, text_lossLayerNormalize.get_text_length()); - m_grid_lossLayerType.attach(text_lossLayerNormalize,2,5,1,1); + m_grid_lossLayerType.attach(text_lossLayerNormalize,2,6,1,1); text_lossLayerNormalize.show(); - label_lossLayerNormalize.set_text("Normalization: \n(select type)"); - label_lossLayerNormalize.set_line_wrap(); - label_lossLayerNormalize.set_justify(Gtk::JUSTIFY_FILL); - m_grid_lossLayerType.attach(label_lossLayerNormalize,0,6,2,1); - label_lossLayerNormalize.show(); + label_lossLayerNormalization.set_text("Normalization: \n(select type)"); + label_lossLayerNormalization.set_line_wrap(); + label_lossLayerNormalization.set_justify(Gtk::JUSTIFY_FILL); + m_grid_lossLayerType.attach(label_lossLayerNormalization,0,7,2,1); + label_lossLayerNormalization.show(); + + Gtk::RadioButton::Group group6 = rbutton_lossLayerFull.get_group(); + rbutton_lossLayerValid.set_group(group6); + rbutton_lossLayerBatch.set_group(group6); + rbutton_lossLayerFull.set_active(); + m_grid_lossLayerType.attach(rbutton_lossLayerFull,2,7,1,1); + rbutton_lossLayerFull.show(); + m_grid_lossLayerType.attach(rbutton_lossLayerValid,3,7,1,1); + rbutton_lossLayerValid.show(); + m_grid_lossLayerType.attach(rbutton_lossLayerBatch,4,7,1,1); + rbutton_lossLayerBatch.show(); + + label_lossLayerNorm.set_text("Norm: \n(select type)"); + label_lossLayerNorm.set_line_wrap(); + label_lossLayerNorm.set_justify(Gtk::JUSTIFY_FILL); + m_grid_lossLayerType.attach(label_lossLayerNorm,0,8,2,1); + label_lossLayerNorm.show(); + + Gtk::RadioButton::Group group7 = rbutton_lossLayerL1.get_group(); + rbutton_lossLayerL2.set_group(group7); + rbutton_lossLayerL1.set_active(); + m_grid_lossLayerType.attach(rbutton_lossLayerL1,2,8,1,1); + rbutton_lossLayerL1.show(); + m_grid_lossLayerType.attach(rbutton_lossLayerL2,3,8,1,1); + rbutton_lossLayerL2.show(); + + m_sw_lossLayerType.add(m_grid_lossLayerType); @@ -1192,12 +1288,264 @@ NetworkCreator::NetworkCreator(): m_grid_extraLayerType.attach(title_extraLayerType,0,0,2,1); title_extraLayerType.show(); + label_extraLayerBottom1.set_text("Bottom1 Layer Name: "); + label_extraLayerBottom1.set_line_wrap(); + label_extraLayerBottom1.set_justify(Gtk::JUSTIFY_FILL); + m_grid_extraLayerType.attach(label_extraLayerBottom1,0,1,2,1); + label_extraLayerBottom1.show(); + + text_extraLayerBottom1.set_max_length(100); + text_extraLayerBottom1.set_text(""); + text_extraLayerBottom1.select_region(0, text_extraLayerBottom1.get_text_length()); + m_grid_extraLayerType.attach(text_extraLayerBottom1,2,1,1,1); + text_extraLayerBottom1.show(); + + label_extraLayerBottom2.set_text("Bottom2 Layer Name: "); + label_extraLayerBottom2.set_line_wrap(); + label_extraLayerBottom2.set_justify(Gtk::JUSTIFY_FILL); + m_grid_extraLayerType.attach(label_extraLayerBottom2,0,2,2,1); + label_extraLayerBottom2.show(); + + text_extraLayerBottom2.set_max_length(100); + text_extraLayerBottom2.set_text(""); + text_extraLayerBottom2.select_region(0, text_extraLayerBottom2.get_text_length()); + m_grid_extraLayerType.attach(text_extraLayerBottom2,2,2,1,1); + text_extraLayerBottom2.show(); + + label_extraLayerTop1.set_text("Top1 Layer Name: "); + label_extraLayerTop1.set_line_wrap(); + label_extraLayerTop1.set_justify(Gtk::JUSTIFY_FILL); + m_grid_extraLayerType.attach(label_extraLayerTop1,0,3,2,1); + label_extraLayerTop1.show(); + + text_extraLayerTop1.set_max_length(100); + text_extraLayerTop1.set_text(""); + text_extraLayerTop1.select_region(0, text_extraLayerTop1.get_text_length()); + m_grid_extraLayerType.attach(text_extraLayerTop1,2,3,1,1); + text_extraLayerTop1.show(); + + label_extraLayerTop2.set_text("Top2 Layer Name: "); + label_extraLayerTop2.set_line_wrap(); + label_extraLayerTop2.set_justify(Gtk::JUSTIFY_FILL); + m_grid_extraLayerType.attach(label_extraLayerTop2,0,4,2,1); + label_extraLayerTop2.show(); + + text_extraLayerTop2.set_max_length(100); + text_extraLayerTop2.set_text(""); + text_extraLayerTop2.select_region(0, text_extraLayerTop2.get_text_length()); + m_grid_extraLayerType.attach(text_extraLayerTop2,2,4,1,1); + text_extraLayerTop2.show(); + + label_extraLayerName.set_text("Current Layer Name: "); + label_extraLayerName.set_line_wrap(); + label_extraLayerName.set_justify(Gtk::JUSTIFY_FILL); + m_grid_extraLayerType.attach(label_extraLayerName,0,5,2,1); + label_extraLayerName.show(); + + text_extraLayerName.set_max_length(100); + text_extraLayerName.set_text(""); + text_extraLayerName.select_region(0, text_extraLayerName.get_text_length()); + m_grid_extraLayerType.attach(text_extraLayerName,2,5,1,1); + text_extraLayerName.show(); + + label_extraLayerTopK.set_text("top_k: \n(Top K Classifications)"); + label_extraLayerTopK.set_line_wrap(); + label_extraLayerTopK.set_justify(Gtk::JUSTIFY_FILL); + m_grid_extraLayerType.attach(label_extraLayerTopK,0,6,2,1); + label_extraLayerTopK.show(); + + text_extraLayerTopK.set_max_length(100); + text_extraLayerTopK.set_text(""); + text_extraLayerTopK.select_region(0, text_extraLayerTopK.get_text_length()); + m_grid_extraLayerType.attach(text_extraLayerTopK,2,6,1,1); + text_extraLayerTopK.show(); + + label_extraLayerOutMaxVal.set_text("out_max_val: \n(if true returns pair \n{max_index, max_value} the input)"); + label_extraLayerOutMaxVal.set_line_wrap(); + label_extraLayerOutMaxVal.set_justify(Gtk::JUSTIFY_FILL); + m_grid_extraLayerType.attach(label_extraLayerOutMaxVal,0,7,2,1); + label_extraLayerOutMaxVal.show(); + + Gtk::RadioButton::Group group8 = rbutton_extraLayerOutMaxValTrue.get_group(); + rbutton_extraLayerOutMaxValFalse.set_group(group8); + rbutton_extraLayerOutMaxValFalse.set_active(); + m_grid_extraLayerType.attach(rbutton_extraLayerOutMaxValTrue,2,7,1,1); + rbutton_extraLayerOutMaxValTrue.show(); + m_grid_extraLayerType.attach(rbutton_extraLayerOutMaxValFalse,3,7,1,1); + rbutton_extraLayerOutMaxValFalse.show(); + + label_extraLayerPhase.set_text("phase: "); + label_extraLayerPhase.set_line_wrap(); + label_extraLayerPhase.set_justify(Gtk::JUSTIFY_FILL); + m_grid_extraLayerType.attach(label_extraLayerPhase,0,8,2,1); + label_extraLayerPhase.show(); + + Gtk::RadioButton::Group group9 = rbutton_extraLayerTrain.get_group(); + rbutton_extraLayerTest.set_group(group9); + rbutton_extraLayerTrain.set_active(); + m_grid_extraLayerType.attach(rbutton_extraLayerTrain,2,8,1,1); + rbutton_extraLayerTrain.show(); + m_grid_extraLayerType.attach(rbutton_extraLayerTest,3,8,1,1); + rbutton_extraLayerTest.show(); + + label_extraLayerScale.set_text("scale: "); + label_extraLayerScale.set_line_wrap(); + label_extraLayerScale.set_justify(Gtk::JUSTIFY_FILL); + m_grid_extraLayerType.attach(label_extraLayerScale,0,9,2,1); + label_extraLayerScale.show(); + + Gtk::RadioButton::Group group10 = rbutton_extraLayerScaleYes.get_group(); + rbutton_extraLayerScaleNo.set_group(group10); + rbutton_extraLayerScaleNo.set_active(); + m_grid_extraLayerType.attach(rbutton_extraLayerScaleYes,2,9,1,1); + rbutton_extraLayerScaleYes.show(); + m_grid_extraLayerType.attach(rbutton_extraLayerScaleNo,3,9,1,1); + rbutton_extraLayerScaleNo.show(); + + text_extraLayerScale.set_max_length(100); + text_extraLayerScale.set_text(""); + text_extraLayerScale.select_region(0, text_extraLayerScale.get_text_length()); + m_grid_extraLayerType.attach(text_extraLayerScale,4,9,1,1); + text_extraLayerScale.show(); + + label_extraLayerNewHeight.set_text("new_height: "); + label_extraLayerNewHeight.set_line_wrap(); + label_extraLayerNewHeight.set_justify(Gtk::JUSTIFY_FILL); + m_grid_extraLayerType.attach(label_extraLayerNewHeight,0,10,2,1); + label_extraLayerNewHeight.show(); + + Gtk::RadioButton::Group group11 = rbutton_extraLayerNewHeightYes.get_group(); + rbutton_extraLayerNewHeightNo.set_group(group11); + rbutton_extraLayerNewHeightNo.set_active(); + m_grid_extraLayerType.attach(rbutton_extraLayerNewHeightYes,2,10,1,1); + rbutton_extraLayerNewHeightYes.show(); + m_grid_extraLayerType.attach(rbutton_extraLayerNewHeightNo,3,10,1,1); + rbutton_extraLayerNewHeightNo.show(); + + text_extraLayerNewHeight.set_max_length(100); + text_extraLayerNewHeight.set_text(""); + text_extraLayerNewHeight.select_region(0, text_extraLayerNewHeight.get_text_length()); + m_grid_extraLayerType.attach(text_extraLayerNewHeight,4,10,1,1); + text_extraLayerNewHeight.show(); + + label_extraLayerNewWidth.set_text("new_width: "); + label_extraLayerNewWidth.set_line_wrap(); + label_extraLayerNewWidth.set_justify(Gtk::JUSTIFY_FILL); + m_grid_extraLayerType.attach(label_extraLayerNewWidth,0,11,2,1); + label_extraLayerNewWidth.show(); + + Gtk::RadioButton::Group group12 = rbutton_extraLayerNewWidthYes.get_group(); + rbutton_extraLayerNewWidthNo.set_group(group12); + rbutton_extraLayerNewWidthNo.set_active(); + m_grid_extraLayerType.attach(rbutton_extraLayerNewWidthYes,2,11,1,1); + rbutton_extraLayerNewWidthYes.show(); + m_grid_extraLayerType.attach(rbutton_extraLayerNewWidthNo,3,11,1,1); + rbutton_extraLayerNewWidthNo.show(); + + text_extraLayerNewWidth.set_max_length(100); + text_extraLayerNewWidth.set_text(""); + text_extraLayerNewWidth.select_region(0, text_extraLayerNewWidth.get_text_length()); + m_grid_extraLayerType.attach(text_extraLayerNewWidth,4,11,1,1); + text_extraLayerNewWidth.show(); + + label_extraLayerCropSize.set_text("crop_size: "); + label_extraLayerCropSize.set_line_wrap(); + label_extraLayerCropSize.set_justify(Gtk::JUSTIFY_FILL); + m_grid_extraLayerType.attach(label_extraLayerCropSize,0,12,2,1); + label_extraLayerCropSize.show(); + + Gtk::RadioButton::Group group13 = rbutton_extraLayerCropSizeYes.get_group(); + rbutton_extraLayerCropSizeNo.set_group(group13); + rbutton_extraLayerCropSizeNo.set_active(); + m_grid_extraLayerType.attach(rbutton_extraLayerCropSizeYes,2,12,1,1); + rbutton_extraLayerCropSizeYes.show(); + m_grid_extraLayerType.attach(rbutton_extraLayerCropSizeNo,3,12,1,1); + rbutton_extraLayerCropSizeNo.show(); + + text_extraLayerCropSize.set_max_length(100); + text_extraLayerCropSize.set_text(""); + text_extraLayerCropSize.select_region(0, text_extraLayerCropSize.get_text_length()); + m_grid_extraLayerType.attach(text_extraLayerCropSize,4,12,1,1); + text_extraLayerCropSize.show(); + + + label_extraLayerMeanFile.set_text("mean_file: "); + label_extraLayerMeanFile.set_line_wrap(); + label_extraLayerMeanFile.set_justify(Gtk::JUSTIFY_FILL); + m_grid_extraLayerType.attach(label_extraLayerMeanFile,0,13,2,1); + label_extraLayerMeanFile.show(); + + Gtk::RadioButton::Group group14 = rbutton_extraLayerMeanFileYes.get_group(); + rbutton_extraLayerMeanFileNo.set_group(group14); + rbutton_extraLayerMeanFileNo.set_active(); + m_grid_extraLayerType.attach(rbutton_extraLayerMeanFileYes,2,13,1,1); + rbutton_extraLayerMeanFileYes.show(); + m_grid_extraLayerType.attach(rbutton_extraLayerMeanFileNo,3,13,1,1); + rbutton_extraLayerMeanFileNo.show(); + + text_extraLayerMeanFile.set_max_length(100); + text_extraLayerMeanFile.set_text(""); + text_extraLayerMeanFile.select_region(0, text_extraLayerMeanFile.get_text_length()); + m_grid_extraLayerType.attach(text_extraLayerMeanFile,4,13,1,1); + text_extraLayerMeanFile.show(); + + label_extraLayerSource.set_text("source: "); + label_extraLayerSource.set_line_wrap(); + label_extraLayerSource.set_justify(Gtk::JUSTIFY_FILL); + m_grid_extraLayerType.attach(label_extraLayerSource,0,14,2,1); + label_extraLayerSource.show(); + + text_extraLayerSource.set_max_length(100); + text_extraLayerSource.set_text(""); + text_extraLayerSource.select_region(0, text_extraLayerSource.get_text_length()); + m_grid_extraLayerType.attach(text_extraLayerSource,2,14,1,1); + text_extraLayerSource.show(); + + label_extraLayerBatchSize.set_text("batch_size: "); + label_extraLayerBatchSize.set_line_wrap(); + label_extraLayerBatchSize.set_justify(Gtk::JUSTIFY_FILL); + m_grid_extraLayerType.attach(label_extraLayerBatchSize,0,15,2,1); + label_extraLayerBatchSize.show(); + + text_extraLayerBatchSize.set_max_length(100); + text_extraLayerBatchSize.set_text(""); + text_extraLayerBatchSize.select_region(0, text_extraLayerBatchSize.get_text_length()); + m_grid_extraLayerType.attach(text_extraLayerBatchSize,2,15,1,1); + text_extraLayerBatchSize.show(); + + label_extraLayerBackend.set_text("backend: "); + label_extraLayerBackend.set_line_wrap(); + label_extraLayerBackend.set_justify(Gtk::JUSTIFY_FILL); + m_grid_extraLayerType.attach(label_extraLayerBackend,0,16,2,1); + label_extraLayerBackend.show(); + + Gtk::RadioButton::Group group15 = rbutton_extraLayerLMDB.get_group(); + rbutton_extraLayerLEVELDB.set_group(group15); + rbutton_extraLayerLMDB.set_active(); + m_grid_extraLayerType.attach(rbutton_extraLayerLMDB,2,16,1,1); + rbutton_extraLayerLMDB.show(); + m_grid_extraLayerType.attach(rbutton_extraLayerLEVELDB,3,16,1,1); + rbutton_extraLayerLEVELDB.show(); + button_addMoreLayer5.signal_clicked().connect(sigc::bind<Glib::ustring>( sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "addMoreLayer5")); - m_grid_extraLayerType.attach(button_addMoreLayer5,0,2,1,1); + m_grid_extraLayerType.attach(button_addMoreLayer5,2,20,1,1); + + button_setExtraParameters.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "setExtraParameters")); + m_grid_extraLayerType.attach(button_setExtraParameters,0,20,2,1); + button_setExtraParameters.show(); + m_sw_extraLayerType.add(m_grid_extraLayerType); + + + + + + + //Display Window set_title("Activation Layer"); @@ -1217,6 +1565,31 @@ NetworkCreator::NetworkCreator(): buttonBox_fullCnnLayerMatter.set_spacing(5); buttonBox_fullCnnLayerMatter.set_layout(Gtk::BUTTONBOX_END); buffer_fullCnnLayerMatter = Gtk::TextBuffer::create(); + + + ref_currentLayers = Gtk::ListStore::create(column_currentLayers); + combo_currentLayers.set_model(ref_currentLayers); + Gtk::TreeModel::Row row_currentLayers = *(ref_currentLayers->append()); + row_currentLayers[column_currentLayers.m_col_id] = 0; + row_currentLayers[column_currentLayers.m_col_name] = "Layers"; + row_currentLayers[column_currentLayers.m_col_extra] = "All Layers"; + combo_currentLayers.set_active(row_currentLayers); + combo_currentLayers.pack_start(column_currentLayers.m_col_id); + combo_currentLayers.pack_start(column_currentLayers.m_col_name); + combo_currentLayers.set_cell_data_func(cell_currentLayers, sigc::mem_fun(*this, &NetworkCreator::on_cell_data_extra)); + combo_currentLayers.pack_start(cell_currentLayers); + + buttonBox_fullCnnLayerMatter.pack_start(combo_currentLayers, Gtk::PACK_SHRINK); + combo_currentLayers.signal_changed().connect( sigc::mem_fun(*this, &NetworkCreator::on_combo_changed) ); + + button_deleteSelectedLayer.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "deleteSelectedLayer")); + buttonBox_fullCnnLayerMatter.pack_start(button_deleteSelectedLayer, Gtk::PACK_SHRINK); + + + button_appendLayerAfter.signal_clicked().connect(sigc::bind<Glib::ustring>( + sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "appendLayerAfter")); + buttonBox_fullCnnLayerMatter.pack_start(button_appendLayerAfter, Gtk::PACK_SHRINK); } NetworkCreator::~NetworkCreator() @@ -1230,6 +1603,10 @@ void NetworkCreator::on_button_clicked(Glib::ustring data) { networkFileName = text_networkFileName.get_text(); std::cout << "Network File Name set as: " << networkFileName << std::endl; + Gtk::MessageDialog dialog(*this, "File Name set"); + dialog.set_secondary_text("File NAme set as : " + networkFileName); + dialog.run(); + } else if(data == "activationLayerType") { @@ -1302,16 +1679,42 @@ void NetworkCreator::on_button_clicked(Glib::ustring data) // std::cout << activationLayerTypeMatter << std::endl; if(numLayers == 0) { - initializeLayer(headLayer,activationLayerTypeMatter); + initializeLayer(headLayer,activationLayerTypeMatter, text_activationLayerName.get_text()); numLayers++; fullCnnLayers.push_back(activationLayerTypeMatter); + fullCnnLayersName.push_back(text_activationLayerName.get_text()); } else { - appendLayer(headLayer,activationLayerTypeMatter); +/* + appendLayer(headLayer,activationLayerTypeMatter, text_activationLayerName.get_text()); numLayers++; fullCnnLayers.push_back(activationLayerTypeMatter); - } + fullCnnLayersName.push_back(text_activationLayerName.get_text()); +*/ + if(appendStatus == false) + { + appendLayer(headLayer,activationLayerTypeMatter, text_activationLayerName.get_text()); + numLayers++; + fullCnnLayers.push_back(activationLayerTypeMatter); + fullCnnLayersName.push_back(text_activationLayerName.get_text()); + } + else + { + std::vector<Glib::ustring>::iterator it; + it = std::find(fullCnnLayersName.begin(), fullCnnLayersName.end(), currentLayersName); + int pos = std::distance(fullCnnLayersName.begin(),it); + insertLayer(headLayer, pos, activationLayerTypeMatter, text_activationLayerName.get_text()); + numLayers++; + fullCnnLayers.insert(fullCnnLayers.begin()+pos+1, activationLayerTypeMatter); + fullCnnLayersName.insert(fullCnnLayersName.begin()+pos+1, text_activationLayerName.get_text()); +// fullCnnLayers.push_back(extraLayerTypeMatter); +// fullCnnLayersName.push_back(text_extraLayerName.get_text()); + } + } + Gtk::MessageDialog dialog(*this, "Layer Added"); + dialog.set_secondary_text("Layer Added: " + text_activationLayerName.get_text()); + dialog.run(); } else if(data == "displayCnnLayers") { @@ -1320,6 +1723,7 @@ void NetworkCreator::on_button_clicked(Glib::ustring data) } else if(data == "editMore") { + appendStatus = false; showWindow_main(); } else if(data == "deleteLayerAtEnd") @@ -1329,6 +1733,7 @@ void NetworkCreator::on_button_clicked(Glib::ustring data) Glib::ustring lastLayer = fullCnnLayers[numLayers-1]; // std::cout << lastLayer << std:: endl; fullCnnLayers.pop_back(); + fullCnnLayersName.pop_back(); numLayers--; Node *layer = searchLayer(headLayer,lastLayer); if(deleteLayer(&headLayer,layer)) @@ -1336,7 +1741,62 @@ void NetworkCreator::on_button_clicked(Glib::ustring data) fullCnnLayerMatter = displayCNN(headLayer); showWindow_displayWindow(); } - + Gtk::MessageDialog dialog(*this, "Layer Deleted"); + dialog.set_secondary_text("Layer at the end Deleted"); + dialog.run(); + } + else if(data == "deleteSelectedLayer") + { + if(numLayers>1 and currentLayersName != headLayer->name) //cannot delete the first created layer + { + std::vector<Glib::ustring>::iterator it; + it=find(fullCnnLayersName.begin(),fullCnnLayersName.end(),currentLayersName); + Glib::ustring currentLayer = fullCnnLayers[it-fullCnnLayersName.begin()]; + std::cout << currentLayer << std:: endl; + Node *layer = searchLayer(headLayer,currentLayer); + if(deleteLayer(&headLayer,layer)) + std::cout << "numLayers = "<< numLayers << "\n"; + fullCnnLayerMatter = displayCNN(headLayer); + std::vector <Glib::ustring> string_to_remove1; + std::vector <Glib::ustring> string_to_remove2; + int length = fullCnnLayers.size(); + for(int i = length-1; i > -1; i--) + { + string_to_remove1.push_back(fullCnnLayers[fullCnnLayers.size()-1]); + string_to_remove2.push_back(fullCnnLayersName[fullCnnLayersName.size()-1]); + fullCnnLayers.pop_back(); + fullCnnLayersName.pop_back(); + } + for(int i = length-1; i > -1; i--) + { + Glib::ustring temp1 = string_to_remove1[string_to_remove1.size()-1]; + Glib::ustring temp2 = string_to_remove2[string_to_remove2.size()-1]; + string_to_remove1.pop_back(); + string_to_remove2.pop_back(); + if(temp2 != currentLayersName) + { + fullCnnLayers.push_back(temp1); + fullCnnLayersName.push_back(temp2); + } + } +// fullCnnLayers.erase(std::remove(fullCnnLayers.begin(), fullCnnLayers.end(), string_to_remove), fullCnnLayers.end()); +// fullCnnLayersName.erase(std::remove(fullCnnLayersName.begin(), fullCnnLayersName.end(), string_to_remove), fullCnnLayersName.end()); + std::cout << fullCnnLayers.size() << std::endl; +// fullCnnLayers[it-fullCnnLayersName.begin()] = ""; +// fullCnnLayersName[it-fullCnnLayersName.begin()] = ""; + numLayers--; +/* + Glib::ustring lastLayer = fullCnnLayers[numLayers-1]; +// std::cout << lastLayer << std:: endl; + fullCnnLayers.pop_back(); + fullCnnLayersName.pop_back(); + numLayers--; +*/ + showWindow_displayWindow(); + } + Gtk::MessageDialog dialog(*this, "Layer Deleted"); + dialog.set_secondary_text(currentLayersName + " Layer Deleted"); + dialog.run(); } else if(data == "criticalLayerType") { @@ -1557,16 +2017,42 @@ void NetworkCreator::on_button_clicked(Glib::ustring data) } if(numLayers == 0) { - initializeLayer(headLayer,criticalLayerTypeMatter); + initializeLayer(headLayer,criticalLayerTypeMatter, text_criticalLayerName.get_text()); numLayers++; fullCnnLayers.push_back(criticalLayerTypeMatter); + fullCnnLayersName.push_back(text_criticalLayerName.get_text()); } else { - appendLayer(headLayer,criticalLayerTypeMatter); +/* + appendLayer(headLayer,criticalLayerTypeMatter, text_criticalLayerName.get_text()); numLayers++; fullCnnLayers.push_back(criticalLayerTypeMatter); + fullCnnLayersName.push_back(text_criticalLayerName.get_text()); +*/ + if(appendStatus == false) + { + appendLayer(headLayer,criticalLayerTypeMatter, text_criticalLayerName.get_text()); + numLayers++; + fullCnnLayers.push_back(criticalLayerTypeMatter); + fullCnnLayersName.push_back(text_criticalLayerName.get_text()); + } + else + { + std::vector<Glib::ustring>::iterator it; + it = std::find(fullCnnLayersName.begin(), fullCnnLayersName.end(), currentLayersName); + int pos = std::distance(fullCnnLayersName.begin(),it); + insertLayer(headLayer, pos, criticalLayerTypeMatter, text_criticalLayerName.get_text()); + numLayers++; + fullCnnLayers.insert(fullCnnLayers.begin()+pos+1, criticalLayerTypeMatter); + fullCnnLayersName.insert(fullCnnLayersName.begin()+pos+1, text_criticalLayerName.get_text()); +// fullCnnLayers.push_back(extraLayerTypeMatter); +// fullCnnLayersName.push_back(text_extraLayerName.get_text()); + } } + Gtk::MessageDialog dialog(*this, "Layer Added"); + dialog.set_secondary_text("New Layer Added: " + text_criticalLayerName.get_text()); + dialog.run(); } else if(data == "normalizationLayerType") { @@ -1621,16 +2107,328 @@ void NetworkCreator::on_button_clicked(Glib::ustring data) } if(numLayers == 0) { - initializeLayer(headLayer,normalizationLayerTypeMatter); + initializeLayer(headLayer,normalizationLayerTypeMatter, text_normalizationLayerName.get_text()); numLayers++; fullCnnLayers.push_back(normalizationLayerTypeMatter); + fullCnnLayersName.push_back(text_normalizationLayerName.get_text()); } else { - appendLayer(headLayer,normalizationLayerTypeMatter); +/* + appendLayer(headLayer,normalizationLayerTypeMatter, text_normalizationLayerName.get_text()); numLayers++; fullCnnLayers.push_back(normalizationLayerTypeMatter); + fullCnnLayersName.push_back(text_normalizationLayerName.get_text()); +*/ + if(appendStatus == false) + { + appendLayer(headLayer,normalizationLayerTypeMatter, text_normalizationLayerName.get_text()); + numLayers++; + fullCnnLayers.push_back(normalizationLayerTypeMatter); + fullCnnLayersName.push_back(text_normalizationLayerName.get_text()); + } + else + { + std::vector<Glib::ustring>::iterator it; + it = std::find(fullCnnLayersName.begin(), fullCnnLayersName.end(), currentLayersName); + int pos = std::distance(fullCnnLayersName.begin(),it); + insertLayer(headLayer, pos, normalizationLayerTypeMatter, text_normalizationLayerName.get_text()); + numLayers++; + fullCnnLayers.insert(fullCnnLayers.begin()+pos+1, normalizationLayerTypeMatter); + fullCnnLayersName.insert(fullCnnLayersName.begin()+pos+1, text_normalizationLayerName.get_text()); +// fullCnnLayers.push_back(extraLayerTypeMatter); +// fullCnnLayersName.push_back(text_extraLayerName.get_text()); + } + } + Gtk::MessageDialog dialog(*this, "Layer Added"); + dialog.set_secondary_text("New Layer Added: " + text_normalizationLayerName.get_text()); + dialog.run(); + } + else if(data == "setLossParameters") + { + lossLayerTypeMatter = ""; + if(lossLayerTypeData == "" or lossLayerTypeData == "SoftmaxWithLoss") + { + lossLayerTypeMatter = "layer{"; + lossLayerTypeMatter += "\n\tbottom: \"" + text_lossLayerBottom1.get_text() + "\""; + lossLayerTypeMatter += "\n\tbottom: \"" + text_lossLayerBottom2.get_text() + "\""; + lossLayerTypeMatter += "\n\ttop: \"" + text_lossLayerTop.get_text() + "\""; + lossLayerTypeMatter += "\n\tname: \"" + text_lossLayerName.get_text() + "\""; + if(lossLayerTypeData == "") + lossLayerTypeData = "SoftmaxWithLoss"; + lossLayerTypeMatter += "\n\ttype: \"" + lossLayerTypeData + "\""; + lossLayerTypeMatter += "\n\tloss_param{"; + lossLayerTypeMatter += "\n\t\tnormalize:" + text_lossLayerNormalize.get_text(); + if(rbutton_lossLayerFull.get_active() == 1) + lossLayerTypeMatter += "\n\t\tnormalization: FULL"; + else if(rbutton_lossLayerValid.get_active() == 1) + lossLayerTypeMatter += "\n\t\tnormalization: VALID"; + else + lossLayerTypeMatter += "\n\t\tnormalization: BATCH_SIZE"; + lossLayerTypeMatter += "\n\t}"; + lossLayerTypeMatter += "\n}\n"; + } + else if(lossLayerTypeData == "HingeLoss") + { + lossLayerTypeMatter = "layer{"; + lossLayerTypeMatter += "\n\tbottom: \"" + text_lossLayerBottom1.get_text() + "\""; + lossLayerTypeMatter += "\n\tbottom: \"" + text_lossLayerBottom2.get_text() + "\""; + lossLayerTypeMatter += "\n\ttop: \"" + text_lossLayerTop.get_text() + "\""; + lossLayerTypeMatter += "\n\tname: \"" + text_lossLayerName.get_text() + "\""; + lossLayerTypeMatter += "\n\ttype: \"" + lossLayerTypeData + "\""; + lossLayerTypeMatter += "\n\tloss_param{"; + lossLayerTypeMatter += "\n\t\tnormalize:" + text_lossLayerNormalize.get_text(); + if(rbutton_lossLayerFull.get_active() == 1) + lossLayerTypeMatter += "\n\t\tnormalization: FULL"; + else if(rbutton_lossLayerValid.get_active() == 1) + lossLayerTypeMatter += "\n\t\tnormalization: VALID"; + else + lossLayerTypeMatter += "\n\t\tnormalization: BATCH_SIZE"; + lossLayerTypeMatter += "\n\t}"; + lossLayerTypeMatter += "\n\thinge_loss_param{"; + if(rbutton_lossLayerL1.get_active() == 1) + lossLayerTypeMatter += "\n\t\tnorm: L1"; + else if(rbutton_lossLayerL2.get_active() == 1) + lossLayerTypeMatter += "\n\t\tnorm: L2"; + lossLayerTypeMatter += "\n\t}"; + lossLayerTypeMatter += "\n}\n"; + } + else if(lossLayerTypeData == "ContrastiveLoss") + { + lossLayerTypeMatter = "layer{"; + lossLayerTypeMatter += "\n\tbottom: \"" + text_lossLayerBottom1.get_text() + "\""; + lossLayerTypeMatter += "\n\tbottom: \"" + text_lossLayerBottom2.get_text() + "\""; + lossLayerTypeMatter += "\n\tbottom: \"" + text_lossLayerBottom3.get_text() + "\""; + lossLayerTypeMatter += "\n\ttop: \"" + text_lossLayerTop.get_text() + "\""; + lossLayerTypeMatter += "\n\tname: \"" + text_lossLayerName.get_text() + "\""; + lossLayerTypeMatter += "\n\ttype: \"" + lossLayerTypeData + "\""; + lossLayerTypeMatter += "\n}\n"; + } + else if(lossLayerTypeData == "EuclideanLoss" or lossLayerTypeData == "MultinomialLogisticLoss" or lossLayerTypeData == "SigmoidCrossEntropyLoss") + { + lossLayerTypeMatter = "layer{"; + lossLayerTypeMatter += "\n\tbottom: \"" + text_lossLayerBottom1.get_text() + "\""; + lossLayerTypeMatter += "\n\tbottom: \"" + text_lossLayerBottom2.get_text() + "\""; + lossLayerTypeMatter += "\n\ttop: \"" + text_lossLayerTop.get_text() + "\""; + lossLayerTypeMatter += "\n\tname: \"" + text_lossLayerName.get_text() + "\""; + lossLayerTypeMatter += "\n\ttype: \"" + lossLayerTypeData + "\""; + lossLayerTypeMatter += "\n}\n"; + } + if(numLayers == 0) + { + initializeLayer(headLayer,lossLayerTypeMatter, text_lossLayerName.get_text()); + numLayers++; + fullCnnLayers.push_back(lossLayerTypeMatter); + fullCnnLayersName.push_back(text_lossLayerName.get_text()); + } + else + { +/* + appendLayer(headLayer,lossLayerTypeMatter, text_lossLayerName.get_text()); + numLayers++; + fullCnnLayers.push_back(lossLayerTypeMatter); + fullCnnLayersName.push_back(text_lossLayerName.get_text()); +*/ + if(appendStatus == false) + { + appendLayer(headLayer,lossLayerTypeMatter, text_lossLayerName.get_text()); + numLayers++; + fullCnnLayers.push_back(lossLayerTypeMatter); + fullCnnLayersName.push_back(text_lossLayerName.get_text()); + } + else + { + std::vector<Glib::ustring>::iterator it; + it = std::find(fullCnnLayersName.begin(), fullCnnLayersName.end(), currentLayersName); + int pos = std::distance(fullCnnLayersName.begin(),it); + insertLayer(headLayer, pos, lossLayerTypeMatter, text_lossLayerName.get_text()); + numLayers++; + fullCnnLayers.insert(fullCnnLayers.begin()+pos+1, lossLayerTypeMatter); + fullCnnLayersName.insert(fullCnnLayersName.begin()+pos+1, text_lossLayerName.get_text()); +// fullCnnLayers.push_back(extraLayerTypeMatter); +// fullCnnLayersName.push_back(text_extraLayerName.get_text()); + } + } + Gtk::MessageDialog dialog(*this, "Layer Added"); + dialog.set_secondary_text("New Layer Added: " + text_lossLayerName.get_text()); + dialog.run(); + + } + else if(data == "setExtraParameters") + { + extraLayerTypeMatter = ""; + if(extraLayerTypeData == "" or extraLayerTypeData == "ArgMax") + { + if(extraLayerTypeMatter == "") + extraLayerTypeMatter = "ArgMax"; + extraLayerTypeMatter = "layer{"; + extraLayerTypeMatter += "\n\tbottom: \"" + text_extraLayerBottom1.get_text() + "\""; + extraLayerTypeMatter += "\n\ttop: \"" + text_extraLayerTop1.get_text() + "\""; + extraLayerTypeMatter += "\n\tname: \"" + text_extraLayerName.get_text() + "\""; + extraLayerTypeMatter += "\n\ttype: \"" + extraLayerTypeData + "\""; + extraLayerTypeMatter += "\n\targmax_param{"; + extraLayerTypeMatter += "\n\t\ttop_k: " + text_extraLayerTopK.get_text(); + extraLayerTypeMatter += "\n\t}"; + extraLayerTypeMatter += "\n}\n"; + } + else if(extraLayerTypeData == "BNLL") + { + extraLayerTypeMatter = "layer{"; + extraLayerTypeMatter += "\n\tbottom: \"" + text_extraLayerBottom1.get_text() + "\""; + extraLayerTypeMatter += "\n\ttop: \"" + text_extraLayerTop1.get_text() + "\""; + extraLayerTypeMatter += "\n\tname: \"" + text_extraLayerName.get_text() + "\""; + extraLayerTypeMatter += "\n\ttype: \"" + extraLayerTypeData + "\""; + extraLayerTypeMatter += "\n}\n"; + } + else if(extraLayerTypeData == "Eltwise") + { + extraLayerTypeMatter = "layer{"; + extraLayerTypeMatter += "\n\tbottom: \"" + text_extraLayerBottom1.get_text() + "\""; + extraLayerTypeMatter += "\n\tbottom: \"" + text_extraLayerBottom2.get_text() + "\""; + extraLayerTypeMatter += "\n\ttop: \"" + text_extraLayerTop1.get_text() + "\""; + extraLayerTypeMatter += "\n\tname: \"" + text_extraLayerName.get_text() + "\""; + extraLayerTypeMatter += "\n\ttype: \"" + extraLayerTypeData + "\""; + extraLayerTypeMatter += "\n}\n"; } + else if(extraLayerTypeData == "ImageData") + { + extraLayerTypeMatter = "layer{"; + extraLayerTypeMatter += "\n\tname: \"" + text_extraLayerName.get_text() + "\""; + extraLayerTypeMatter += "\n\ttop: \"" + text_extraLayerTop1.get_text() + "\""; + extraLayerTypeMatter += "\n\ttop: \"" + text_extraLayerTop2.get_text() + "\""; + extraLayerTypeMatter += "\n\ttype: \"" + extraLayerTypeData + "\""; + extraLayerTypeMatter += "\n\tinclude{"; + if(rbutton_extraLayerTrain.get_active() == 1) + extraLayerTypeMatter += "\n\t\tphase: TRAIN"; + else + extraLayerTypeMatter += "\n\t\tphase: TEST"; + extraLayerTypeMatter += "\n\t}"; + extraLayerTypeMatter += "\n\ttransform_param{"; + if(rbutton_extraLayerScaleYes.get_active() == 1) + extraLayerTypeMatter += "\n\t\tscale: " + text_extraLayerScale.get_text(); + else + extraLayerTypeMatter += "\n\t\t#scale: "; + if(rbutton_extraLayerNewHeightYes.get_active() == 1) + extraLayerTypeMatter += "\n\t\tnew_height: " + text_extraLayerNewHeight.get_text(); + else + extraLayerTypeMatter += "\n\t\t#new_height: "; + if(rbutton_extraLayerNewWidthYes.get_active() == 1) + extraLayerTypeMatter += "\n\t\tnew_width: " + text_extraLayerNewWidth.get_text(); + else + extraLayerTypeMatter += "\n\t\t#new_width: "; + if(rbutton_extraLayerCropSizeYes.get_active() == 1) + extraLayerTypeMatter += "\n\t\tcrop_size: " + text_extraLayerCropSize.get_text(); + else + extraLayerTypeMatter += "\n\t\t#crop_size: "; + if(rbutton_extraLayerMeanFileYes.get_active() == 1) + extraLayerTypeMatter += "\n\t\tmean_file: \"" + text_extraLayerMeanFile.get_text() + "\""; + else + extraLayerTypeMatter += "\n\t\t#mean_file: "; + extraLayerTypeMatter += "\n\t}"; + extraLayerTypeMatter += "\n\timage_data_param{"; + extraLayerTypeMatter += "\n\t\tsource: \"" + text_extraLayerSource.get_text() + "\""; + extraLayerTypeMatter += "\n\t\tbatch_size: " + text_extraLayerBatchSize.get_text(); + extraLayerTypeMatter += "\n\t}"; + extraLayerTypeMatter += "\n}\n"; + } + else if(extraLayerTypeData == "Data") + { + extraLayerTypeMatter = "layer{"; + extraLayerTypeMatter += "\n\tname: \"" + text_extraLayerName.get_text() + "\""; + extraLayerTypeMatter += "\n\ttop: \"" + text_extraLayerTop1.get_text() + "\""; + extraLayerTypeMatter += "\n\ttop: \"" + text_extraLayerTop2.get_text() + "\""; + extraLayerTypeMatter += "\n\ttype: \"" + extraLayerTypeData + "\""; + extraLayerTypeMatter += "\n\tinclude{"; + if(rbutton_extraLayerTrain.get_active() == 1) + extraLayerTypeMatter += "\n\t\tphase: TRAIN"; + else + extraLayerTypeMatter += "\n\t\tphase: TEST"; + extraLayerTypeMatter += "\n\t}"; + extraLayerTypeMatter += "\n\ttransform_param{"; + if(rbutton_extraLayerScaleYes.get_active() == 1) + extraLayerTypeMatter += "\n\t\tscale: " + text_extraLayerScale.get_text(); + else + extraLayerTypeMatter += "\n\t\t#scale: "; + if(rbutton_extraLayerNewHeightYes.get_active() == 1) + extraLayerTypeMatter += "\n\t\tnew_height: " + text_extraLayerNewHeight.get_text(); + else + extraLayerTypeMatter += "\n\t\t#new_height: "; + if(rbutton_extraLayerNewWidthYes.get_active() == 1) + extraLayerTypeMatter += "\n\t\tnew_width: " + text_extraLayerNewWidth.get_text(); + else + extraLayerTypeMatter += "\n\t\t#new_width: "; + if(rbutton_extraLayerCropSizeYes.get_active() == 1) + extraLayerTypeMatter += "\n\t\tcrop_size: " + text_extraLayerCropSize.get_text(); + else + extraLayerTypeMatter += "\n\t\t#crop_size: "; + if(rbutton_extraLayerMeanFileYes.get_active() == 1) + extraLayerTypeMatter += "\n\t\tmean_file: \"" + text_extraLayerMeanFile.get_text() + "\""; + else + extraLayerTypeMatter += "\n\t\t#mean_file: "; + extraLayerTypeMatter += "\n\t}"; + extraLayerTypeMatter += "\n\tdata_param{"; + extraLayerTypeMatter += "\n\t\tsource: \"" + text_extraLayerSource.get_text() + "\""; + extraLayerTypeMatter += "\n\t\tbatch_size: " + text_extraLayerBatchSize.get_text(); + if(rbutton_extraLayerLMDB.get_active() == 1) + extraLayerTypeMatter += "\n\t\tbackend: LMDB"; + else + extraLayerTypeMatter += "\n\t\tbackend: LEVELDB"; + extraLayerTypeMatter += "\n\t}"; + extraLayerTypeMatter += "\n}\n"; + } + else if(extraLayerTypeData == "HDF5Data") + { + extraLayerTypeMatter = "layer{"; + extraLayerTypeMatter += "\n\tname: \"" + text_extraLayerName.get_text() + "\""; + extraLayerTypeMatter += "\n\ttop: \"" + text_extraLayerTop1.get_text() + "\""; + extraLayerTypeMatter += "\n\ttop: \"" + text_extraLayerTop2.get_text() + "\""; + extraLayerTypeMatter += "\n\ttype: \"" + extraLayerTypeData + "\""; + extraLayerTypeMatter += "\n\tinclude{"; + if(rbutton_extraLayerTrain.get_active() == 1) + extraLayerTypeMatter += "\n\t\tphase: TRAIN"; + else + extraLayerTypeMatter += "\n\t\tphase: TEST"; + extraLayerTypeMatter += "\n\t}"; + extraLayerTypeMatter += "\n\tdata_param{"; + extraLayerTypeMatter += "\n\t\tsource: \"" + text_extraLayerSource.get_text() + "\""; + extraLayerTypeMatter += "\n\t\tbatch_size: " + text_extraLayerBatchSize.get_text(); + extraLayerTypeMatter += "\n\t}"; + extraLayerTypeMatter += "\n}\n"; + } + if(numLayers == 0) + { + initializeLayer(headLayer,extraLayerTypeMatter, text_extraLayerName.get_text()); + numLayers++; + fullCnnLayers.push_back(extraLayerTypeMatter); + fullCnnLayersName.push_back(text_extraLayerName.get_text()); + } + else + { + std::cout << "in here " << appendStatus << std::endl; + if(appendStatus == false) + { + + appendLayer(headLayer,extraLayerTypeMatter, text_extraLayerName.get_text()); + numLayers++; + fullCnnLayers.push_back(extraLayerTypeMatter); + fullCnnLayersName.push_back(text_extraLayerName.get_text()); + } + else + { + std::vector<Glib::ustring>::iterator it; + it = std::find(fullCnnLayersName.begin(), fullCnnLayersName.end(), currentLayersName); + int pos = std::distance(fullCnnLayersName.begin(),it); + insertLayer(headLayer, pos, extraLayerTypeMatter, text_extraLayerName.get_text()); + numLayers++; + fullCnnLayers.insert(fullCnnLayers.begin()+pos+1, extraLayerTypeMatter); + fullCnnLayersName.insert(fullCnnLayersName.begin()+pos+1, text_extraLayerName.get_text()); +// fullCnnLayers.push_back(extraLayerTypeMatter); +// fullCnnLayersName.push_back(text_extraLayerName.get_text()); + } + } + Gtk::MessageDialog dialog(*this, "Layer Added"); + dialog.set_secondary_text("New Layer Added: " + text_extraLayerName.get_text()); + dialog.run(); } else if(data == "addMoreLayer3") { @@ -1652,6 +2450,11 @@ void NetworkCreator::on_button_clicked(Glib::ustring data) { showWindow_main(); } + else if(data == "appendLayerAfter") + { + appendStatus = true; + showWindow_main(); + } else if(data == "saveFile") { networkFileName = text_networkFileName.get_text(); @@ -1659,7 +2462,14 @@ void NetworkCreator::on_button_clicked(Glib::ustring data) myfile.open(networkFileName); std::cout << "Network File Name saved as: " << networkFileName << std::endl; myfile << "#File generated using OpenDetection" << std::endl; + for(int i = 0; i < numLayers; i++) + { + myfile << fullCnnLayers[i]; + } myfile.close(); + Gtk::MessageDialog dialog(*this, "File Saved"); + dialog.set_secondary_text("File saved as: " + networkFileName); + dialog.run(); } } diff --git a/detectors/src/global2D/training/Solver.cpp b/detectors/src/global2D/training/Solver.cpp index 636a2b24..43217ce4 100644 --- a/detectors/src/global2D/training/Solver.cpp +++ b/detectors/src/global2D/training/Solver.cpp @@ -133,7 +133,7 @@ SolverProperties::SolverProperties(): //level1 - label_trainNetworkFileType.set_text("2)Select type of training network file type.\nUsually these exists two types,\nfirst adds validation and training in the same file,\nWhile other adds them in two different files"); + label_trainNetworkFileType.set_text("2)Select type of training network file type.\nUsually trese exists two types,\nfirst adds validation and training in the same file,\nWhile other adds them in two different files"); label_trainNetworkFileType.set_line_wrap(); label_trainNetworkFileType.set_justify(Gtk::JUSTIFY_FILL); m_grid1.attach(label_trainNetworkFileType,0,1,2,1); @@ -676,11 +676,17 @@ void SolverProperties::on_button_clicked(Glib::ustring data) { solverFileName = text_solverFileName.get_text(); std::cout << "Solver File Name set as: " << solverFileName << std::endl; + Gtk::MessageDialog dialog(*this, "FileName Updated"); + dialog.set_secondary_text("New name and location: " + solverFileName); + dialog.run(); } else if(data == "trainNetworkFileName") { trainNetworkFileName = text_trainNetworkFileName.get_text(); std::cout << "Train Network File Name set as: " << trainNetworkFileName << std::endl; + Gtk::MessageDialog dialog(*this, "FileName Updated"); + dialog.set_secondary_text("New name and location: " + trainNetworkFileName); + dialog.run(); } else if(data == "testNetworkFileName") { @@ -692,6 +698,9 @@ void SolverProperties::on_button_clicked(Glib::ustring data) dialog.set_secondary_text("\"test_net\" parameter is only required when \"train_net\" parameter is specified"); dialog.run(); } + Gtk::MessageDialog dialog(*this, "FileName Updated"); + dialog.set_secondary_text("New name and location: " + trainNetworkFileName); + dialog.run(); } else if(data == "testIter") { @@ -709,6 +718,9 @@ void SolverProperties::on_button_clicked(Glib::ustring data) dialog.set_secondary_text("Validation parameters can be updated only after enabling them"); dialog.run(); } + Gtk::MessageDialog dialog(*this, "test_iter Updated"); + dialog.set_secondary_text("test_iter: " + testIter); + dialog.run(); } else if(data == "testInterval") { @@ -726,6 +738,9 @@ void SolverProperties::on_button_clicked(Glib::ustring data) dialog.set_secondary_text("Validation parameters can be updated only after enabling them"); dialog.run(); } + Gtk::MessageDialog dialog(*this, "test_interval Updated"); + dialog.set_secondary_text("test_interval: " + testInterval); + dialog.run(); } else if(data == "averageLoss") { @@ -737,6 +752,9 @@ void SolverProperties::on_button_clicked(Glib::ustring data) dialog.set_secondary_text("Average Loss Parameter can be updated only after enabling it"); dialog.run(); } + Gtk::MessageDialog dialog(*this, "Parameter Updated"); + dialog.set_secondary_text("averageLoss: " + averageLoss); + dialog.run(); } else if(data == "randomSample") { @@ -748,11 +766,17 @@ void SolverProperties::on_button_clicked(Glib::ustring data) dialog.set_secondary_text("Random Sample Parameter can be updated only after enabling it"); dialog.run(); } + Gtk::MessageDialog dialog(*this, "Parameter Updated"); + dialog.set_secondary_text("random_sample: " + randomSample); + dialog.run(); } else if(data == "display") { display = text_display.get_text(); - std::cout << "Display after every: " << display << " iterations" << std::endl; + std::cout << "Display after every: " << display << " iterations" << std::endl; + Gtk::MessageDialog dialog(*this, "Parameter Updated"); + dialog.set_secondary_text("display: " + display); + dialog.run(); } else if(data == "debugInfo") { @@ -761,11 +785,17 @@ void SolverProperties::on_button_clicked(Glib::ustring data) else if(rbutton_enableDebugInfo_no.get_active()) debugInfo = "0"; std::cout << "Debug Information: " << debugInfo << std::endl; + Gtk::MessageDialog dialog(*this, "Parameter Updated"); + dialog.set_secondary_text("debug_info: " + debugInfo); + dialog.run(); } else if(data == "snapshot") { snapshot = text_snapshot.get_text(); std::cout << "Snapshot saved after every: " << snapshot << " iterations" << std::endl; + Gtk::MessageDialog dialog(*this, "Parameter Updated"); + dialog.set_secondary_text("snapshot: " + snapshot); + dialog.run(); } else if(data == "testComputeLoss") { @@ -773,17 +803,26 @@ void SolverProperties::on_button_clicked(Glib::ustring data) testComputeLoss = "1"; else if(rbutton_enableTestComputeLoss_no.get_active()) testComputeLoss = "0"; - std::cout << "Compute Loss in Validation Phase: " << testComputeLoss << std::endl; + std::cout << "Compute Loss in Validation Phase: " << testComputeLoss << std::endl; + Gtk::MessageDialog dialog(*this, "Parameter Updated"); + dialog.set_secondary_text("testComputeLoss: " + testComputeLoss); + dialog.run(); } else if(data == "snapshotPrefix") { snapshotPrefix = text_snapshotPrefix.get_text(); std::cout << "Snapshot saved with prefix: " << snapshotPrefix << std::endl; + Gtk::MessageDialog dialog(*this, "Parameter Updated"); + dialog.set_secondary_text("snapshot_prefix: " + snapshotPrefix); + dialog.run(); } else if(data == "maxIter") { maxIter = text_maxIter.get_text(); std::cout << "Maximum iterations set as: " << maxIter << std::endl; + Gtk::MessageDialog dialog(*this, "Parameter Updated"); + dialog.set_secondary_text("max_iter: " + maxIter); + dialog.run(); } else if(data == "type") { @@ -799,7 +838,10 @@ void SolverProperties::on_button_clicked(Glib::ustring data) type = "RMSProp"; else if(rbutton_typeNesterov_yes.get_active()) type = "Nesterov"; - std::cout << "Solver Type Set as: " << type << std::endl; + std::cout << "Solver Type Set as: " << type << std::endl; + Gtk::MessageDialog dialog(*this, "Parameter Updated"); + dialog.set_secondary_text("type: " + type); + dialog.run(); } else if(data == "learningRatePolicy") { @@ -819,41 +861,65 @@ void SolverProperties::on_button_clicked(Glib::ustring data) learningRatePolicy = "sigmoid"; std::cout << "Learning rate policy is set as: " << learningRatePolicy << std::endl; + Gtk::MessageDialog dialog(*this, "Parameter Updated"); + dialog.set_secondary_text("lr_policy: " + learningRatePolicy); + dialog.run(); } else if(data == "baseLearningRate") { baseLearningRate = text_baseLearningRate.get_text(); std::cout << "Initial learning rate set as: " << baseLearningRate << std::endl; + Gtk::MessageDialog dialog(*this, "Parameter Updated"); + dialog.set_secondary_text("base_lr: " + baseLearningRate); + dialog.run(); } else if(data == "gamma") { gamma = text_gamma.get_text(); - std::cout << "Gamma set as: " << gamma << std::endl; + std::cout << "Gamma set as: " << gamma << std::endl; + Gtk::MessageDialog dialog(*this, "Parameter Updated"); + dialog.set_secondary_text("gamma: " + gamma); + dialog.run(); } else if(data == "power") { power = text_power.get_text(); - std::cout << "Power set as: " << power << std::endl; + std::cout << "Power set as: " << power << std::endl; + Gtk::MessageDialog dialog(*this, "Parameter Updated"); + dialog.set_secondary_text("power: " + power); + dialog.run(); } else if(data == "stepSize") { stepSize = text_stepSize.get_text(); - std::cout << "stepSize set as: " << stepSize << std::endl; + std::cout << "stepSize set as: " << stepSize << std::endl; + Gtk::MessageDialog dialog(*this, "Parameter Updated"); + dialog.set_secondary_text("step_size: " + stepSize); + dialog.run(); } else if(data == "stepSizeValue") { stepSizeValue = text_stepSizeValue.get_text(); - std::cout << "stepSizeValue set as: " << stepSizeValue << std::endl; + std::cout << "stepSizeValue set as: " << stepSizeValue << std::endl; + Gtk::MessageDialog dialog(*this, "Parameter Updated"); + dialog.set_secondary_text("step_size_value: " + stepSizeValue); + dialog.run(); } else if(data == "weightDecay") { weightDecay = text_weightDecay.get_text(); std::cout << "weightDecay set as: " << weightDecay << std::endl; + Gtk::MessageDialog dialog(*this, "Parameter Updated"); + dialog.set_secondary_text("weight_decay: " + weightDecay); + dialog.run(); } else if(data == "momentum") { momentum = text_momentum.get_text(); - std::cout << "momentum set as: " << momentum << std::endl; + std::cout << "momentum set as: " << momentum << std::endl; + Gtk::MessageDialog dialog(*this, "Parameter Updated"); + dialog.set_secondary_text("momentum: " + momentum); + dialog.run(); } else if(data == "saveFile") { @@ -996,6 +1062,9 @@ void SolverProperties::on_button_clicked(Glib::ustring data) myfile.close(); } + Gtk::MessageDialog dialog(*this, "File Saved"); + dialog.set_secondary_text("File saved as: " + solverFileName); + dialog.run(); } } From 2f952f44ea0499e53bdfa662c7e544e2a91ecbd4 Mon Sep 17 00:00:00 2001 From: giacomo <giacomo.dabisias@gmail.com> Date: Sat, 20 Aug 2016 17:08:39 +0200 Subject: [PATCH 66/79] updates blog --- doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md index 810f06c8..f8c7c5f5 100644 --- a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md +++ b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md @@ -7,6 +7,8 @@ GSoC 2016 Blog - Giacomo {#gsoc2016_blog_giacomo} ==== - [Link to Proposal](https://docs.google.com/document/d/16Wyd0h5b9-7DaG7ZYJT30a2i096krviFUCcDYwg-jZc/edit?usp=sharing) - [Link to GSoC2016 Project Page](https://summerofcode.withgoogle.com/organizations/6007728078061568/#5675882488266752) + + - [Link to commit list](https://github.com/giacomodabisias/opendetection/commits/master) ##About Me## @@ -408,4 +410,8 @@ After that there is the need to integrate the other google summer of code Projec I integrated the work from Abhishek, who did the other gsoc project in opendetection. I had to adapt the fiel structure and naming convention to the new struture and change some code to use shared pointers; there is still some work to do to adapt and clean the code which has many flaws, but thats something which should be done as part of the other gsoc project. Anyway I added two new options in the cmake: **WITH_CAFFE** and **WITH_GTKMM**; the first one enables to build the OD library with Caffe support, while the second one adds to caffe also the visualization part. If the flag **WITH_GPU** is added then the Caffe gpu part will be used. I also restructured a bit the cmake structure adding some more searated cmake files and fixed the naming on the cmake files as for the other files. -THe only thing missing now is a bit of documentation and then everything should be done :) \ No newline at end of file +THe only thing missing now is a bit of documentation and then everything should be done :) + +##Integrating the caffe gsoc project 2 20/08/16## + +I finished integrating the other gsoc project except the examples which are havy in content, as they contain a lot of images etc which should be outside of the git repository. \ No newline at end of file From 9fe86ba19a14d1952cd3f6e6e6852d2a1ed2a589 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Fri, 2 Sep 2016 11:04:57 +0200 Subject: [PATCH 67/79] removes Abhisheks work --- cmake/Dependency.cmake | 2 + cmake/Options.cmake | 4 +- .../global2D/detection/ConvClassification.h | 55 - .../global2D/localization/Convolve.h | 67 - .../global2D/localization/CreateModel_test.h | 597 ------ .../global2D/localization/Disjoint-set.h | 77 - .../detectors/global2D/localization/Filter.h | 97 - .../detectors/global2D/localization/Image.h | 98 - .../detectors/global2D/localization/Imconv.h | 175 -- .../detectors/global2D/localization/Imutil.h | 63 - .../od/detectors/global2D/localization/Misc.h | 63 - .../detectors/global2D/localization/Pnmfile.h | 208 -- .../global2D/localization/Segment-graph.h | 81 - .../global2D/localization/Segment-image.h | 152 -- .../localization/SelectiveSearchBase.h | 67 - .../localization/SelectiveSearchModel.h | 95 - .../global2D/training/ActivationWindow.h | 458 ----- .../detectors/global2D/training/ConvTrainer.h | 42 - .../global2D/training/CriticalWindow.h | 1240 ------------ .../global2D/training/DisplayWindow.h | 14 - .../detectors/global2D/training/ExtraWindow.h | 24 - .../detectors/global2D/training/LossWindow.h | 127 -- .../detectors/global2D/training/MainWindow.h | 146 -- .../od/detectors/global2D/training/Network.h | 239 --- .../od/detectors/global2D/training/Node.h | 86 - .../global2D/training/NormalizationWindow.h | 345 ---- .../od/detectors/global2D/training/Solver.h | 131 -- detectors/src/global2D/CMakeLists.txt | 2 + .../global2D/detection/ConvClassification.cpp | 128 -- .../localization/SelectiveSearchBase.cpp | 145 -- .../localization/SelectiveSearchModel.cpp | 604 ------ .../src/global2D/training/ConvTrainer.cpp | 53 - detectors/src/global2D/training/Network.cpp | 1667 ----------------- detectors/src/global2D/training/Solver.cpp | 1001 ---------- examples/objectdetector/CMakeLists.txt | 4 +- .../cnn_mnist_classification.cpp | 63 - .../cnn_mnist_train_customSolver.cpp | 11 - .../objectdetector/cnn_mnist_train_simple.cpp | 11 - 38 files changed, 9 insertions(+), 8433 deletions(-) delete mode 100644 detectors/include/od/detectors/global2D/detection/ConvClassification.h delete mode 100755 detectors/include/od/detectors/global2D/localization/Convolve.h delete mode 100644 detectors/include/od/detectors/global2D/localization/CreateModel_test.h delete mode 100755 detectors/include/od/detectors/global2D/localization/Disjoint-set.h delete mode 100755 detectors/include/od/detectors/global2D/localization/Filter.h delete mode 100755 detectors/include/od/detectors/global2D/localization/Image.h delete mode 100755 detectors/include/od/detectors/global2D/localization/Imconv.h delete mode 100755 detectors/include/od/detectors/global2D/localization/Imutil.h delete mode 100755 detectors/include/od/detectors/global2D/localization/Misc.h delete mode 100755 detectors/include/od/detectors/global2D/localization/Pnmfile.h delete mode 100755 detectors/include/od/detectors/global2D/localization/Segment-graph.h delete mode 100755 detectors/include/od/detectors/global2D/localization/Segment-image.h delete mode 100644 detectors/include/od/detectors/global2D/localization/SelectiveSearchBase.h delete mode 100644 detectors/include/od/detectors/global2D/localization/SelectiveSearchModel.h delete mode 100644 detectors/include/od/detectors/global2D/training/ActivationWindow.h delete mode 100644 detectors/include/od/detectors/global2D/training/ConvTrainer.h delete mode 100644 detectors/include/od/detectors/global2D/training/CriticalWindow.h delete mode 100644 detectors/include/od/detectors/global2D/training/DisplayWindow.h delete mode 100644 detectors/include/od/detectors/global2D/training/ExtraWindow.h delete mode 100644 detectors/include/od/detectors/global2D/training/LossWindow.h delete mode 100644 detectors/include/od/detectors/global2D/training/MainWindow.h delete mode 100644 detectors/include/od/detectors/global2D/training/Network.h delete mode 100644 detectors/include/od/detectors/global2D/training/Node.h delete mode 100644 detectors/include/od/detectors/global2D/training/NormalizationWindow.h delete mode 100644 detectors/include/od/detectors/global2D/training/Solver.h delete mode 100644 detectors/src/global2D/detection/ConvClassification.cpp delete mode 100644 detectors/src/global2D/localization/SelectiveSearchBase.cpp delete mode 100644 detectors/src/global2D/localization/SelectiveSearchModel.cpp delete mode 100644 detectors/src/global2D/training/ConvTrainer.cpp delete mode 100644 detectors/src/global2D/training/Network.cpp delete mode 100644 detectors/src/global2D/training/Solver.cpp delete mode 100644 examples/objectdetector/cnn_mnist_classification.cpp delete mode 100644 examples/objectdetector/cnn_mnist_train_customSolver.cpp delete mode 100644 examples/objectdetector/cnn_mnist_train_simple.cpp diff --git a/cmake/Dependency.cmake b/cmake/Dependency.cmake index db008085..e796db9f 100644 --- a/cmake/Dependency.cmake +++ b/cmake/Dependency.cmake @@ -3,6 +3,7 @@ find_package(OpenCV 3 REQUIRED) find_package(VTK REQUIRED) find_package(Boost 1.40 COMPONENTS program_options REQUIRED) +if(0) if(WITH_CAFFE) find_package(Caffe REQUIRED) if(Caffe_FOUND) @@ -18,6 +19,7 @@ if(WITH_CAFFE) message("Caffe not found!") endif() endif() +endif(0) # Needed by od_common. There is a circular dependency between od_common and od_gpu_common include dirs. include_directories(GPU_COMMON_INCLUDE_DIRS ${OD_SOURCE_DIR}/gpu/common/include) diff --git a/cmake/Options.cmake b/cmake/Options.cmake index 846170f4..0d954038 100644 --- a/cmake/Options.cmake +++ b/cmake/Options.cmake @@ -32,8 +32,8 @@ option(WITH_GPU "Build GPU modules" ON) option(WITH_EXAMPLES "Build examples" OFF) option(WITH_SVMLIGHT "Build with svmlight support" ON) option(WITH_STD_SHARED_PTR "use std::shared_ptr instead of boost::shared_ptr" OFF) -option(WITH_CAFFE "build caffe support" OFF) -option(WITH_GTKMM "build gtkmm support" OFF) +#option(WITH_CAFFE "build caffe support" OFF) +#option(WITH_GTKMM "build gtkmm support" OFF) option(BUILD_GLOBAL_2D_DETECTION "build global 2D detection" ON) option(BUILD_GLOBAL_3D_DETECTION "build global 3D detection" ON) diff --git a/detectors/include/od/detectors/global2D/detection/ConvClassification.h b/detectors/include/od/detectors/global2D/detection/ConvClassification.h deleted file mode 100644 index 46cc6bfa..00000000 --- a/detectors/include/od/detectors/global2D/detection/ConvClassification.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include "od/common/pipeline/Detector.h" -#include "od/common/pipeline/Scene.h" -#include "od/common/utils/Shared_pointers.h" - -#include <opencv2/opencv.hpp> -#include <vector> -#include <string> -#include <iostream> - -#include <caffe/caffe.hpp> -#include <caffe/util/io.hpp> -#include <caffe/blob.hpp> - - -namespace od -{ - namespace g2d - { - class ConvClassification : public Detector2D - { - public: - - ConvClassification(const std::string & trained_data_location = std::string("")); - - void setWeightModelFileLocation(const std::string & location); - void setNetworkModelFileLocation(const std::string & location); - void setImageFileLocation(const std::string & location); - - std::string getWeightModelFileLocation(); - std::string getNetworkModelFileLocation(); - std::string getImageFileLocation(); - - void setTestBlob(int num_channels, int img_height, int img_width); - void classify(); - - void init(); - shared_ptr<Detections2D> detectOmni(shared_ptr<SceneImage> scene); - shared_ptr<Detections> detect(shared_ptr<SceneImage> scene); - shared_ptr<Detections> detectOmni(shared_ptr<Scene> scene); - - private: - std::string weightModelFileLoaction; - std::string networkFileLocation; - std::string imageFileLocation; - caffe::Datum strucBlob; - caffe::BlobProto protoBlob; - std::vector<caffe::Blob<float>*> inputBlob; - - }; - } -} - - diff --git a/detectors/include/od/detectors/global2D/localization/Convolve.h b/detectors/include/od/detectors/global2D/localization/Convolve.h deleted file mode 100755 index f0e824d7..00000000 --- a/detectors/include/od/detectors/global2D/localization/Convolve.h +++ /dev/null @@ -1,67 +0,0 @@ -/* -Copyright (C) 2006 Pedro Felzenszwalb - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -/* convolution */ - -#pragma once - -#include <vector> -#include <algorithm> -#include <cmath> -#include "od/detectors/global2D/localization/Image.h" - -/* convolve src with mask. dst is flipped! */ -static void convolve_even(image<float> *src, image<float> *dst, - std::vector<float> &mask) { - int width = src->width(); - int height = src->height(); - int len = mask.size(); - - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - float sum = mask[0] * imRef(src, x, y); - for (int i = 1; i < len; i++) { - sum += mask[i] * - (imRef(src, std::max(x-i,0), y) + - imRef(src, std::min(x+i, width-1), y)); - } - imRef(dst, y, x) = sum; - } - } -} - -/* convolve src with mask. dst is flipped! */ -static void convolve_odd(image<float> *src, image<float> *dst, - std::vector<float> &mask) { - int width = src->width(); - int height = src->height(); - int len = mask.size(); - - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - float sum = mask[0] * imRef(src, x, y); - for (int i = 1; i < len; i++) { - sum += mask[i] * - (imRef(src, std::max(x-i,0), y) - - imRef(src, std::min(x+i, width-1), y)); - } - imRef(dst, y, x) = sum; - } - } -} - diff --git a/detectors/include/od/detectors/global2D/localization/CreateModel_test.h b/detectors/include/od/detectors/global2D/localization/CreateModel_test.h deleted file mode 100644 index f391e9fb..00000000 --- a/detectors/include/od/detectors/global2D/localization/CreateModel_test.h +++ /dev/null @@ -1,597 +0,0 @@ -#pragma once - -#include <opencv2/objdetect/objdetect.hpp> -#include <opencv2/highgui/highgui.hpp> -#include <opencv2/imgproc/imgproc.hpp> -#include <opencv2/core/core.hpp> - -#include <cmath> -#include <cstdio> -#include <vector> -#include <iostream> -#include <algorithm> -#include <stdio.h> -#include <math.h> -#include <stdlib.h> -#include <time.h> -# include <cstdlib> -# include <iomanip> -# include <fstream> -# include <ctime> -# include <cstring> -#include <sstream> -#include <string> - -class regionProperties -{ - public: - void setLabel(int l); - void setBoundaries(int ix, int iy, int ax, int ay); - int label; - int min_x; - int max_x; - int min_y; - int max_y; - int size; - Mat xx_hist, xy_hist, yy_hist, orientation_image_hist,differential_excitation_hist, color_hist; - vector <int> neighbors; - bool validity; -}; - -void regionProperties::setLabel(int l) -{ - label = l; -} - -Mat get_hess_hist_xx(Mat regionMask, int histSize, float hist_range_min, float hist_range_max) -{ - Mat kernel_filter_1(7,7, CV_32FC1,1); - kernel_filter_1 = (Mat_<double>(7,7) << 1.57130243e-04, 7.17839338e-04, 0, -1.76805171e-03, 0, 7.17839338e-04, 1.57130243e-04, - 1.91423823e-03, 8.74507340e-03, 0, -2.15392793e-02, 0, 8.74507340e-03, 1.91423823e-03, - 8.57902057e-03, 3.91926999e-02, 0, -9.65323526e-02, 0, 3.91926999e-02, 8.57902057e-03, - 1.41444137e-02, 6.46178379e-02, 0, -1.59154943e-01, 0, 6.46178379e-02, 1.41444137e-02, - 8.57902057e-03, 3.91926999e-02, 0, -9.65323526e-02, 0, 3.91926999e-02, 8.57902057e-03, - 1.91423823e-03, 8.74507340e-03, 0, -2.15392793e-02, 0, 8.74507340e-03, 1.91423823e-03, - 1.57130243e-04, 7.17839338e-04, 0, -1.76805171e-03, 0, 7.17839338e-04, 1.57130243e-04 - ); - Mat image_filtered_xx; - filter2D(regionMask, image_filtered_xx, -1, kernel_filter_1,Point(-1,-1), 0,BORDER_DEFAULT ); - Mat xx_hist; - float range[] = { hist_range_min, hist_range_max } ; - const float* histRange = { range }; - bool uniform = true; bool accumulate = false; - calcHist( &image_filtered_xx, 1, 0, Mat(), xx_hist, 1, &histSize, &histRange, uniform, accumulate ); - return xx_hist; -} - -Mat get_hess_hist_xy(Mat regionMask, int histSize, float hist_range_min, float hist_range_max) -{ - Mat kernel_filter_2(7,7, CV_32FC1,1); - kernel_filter_2 = (Mat_<double>(7,7) << 0.00017677, 0.00143568, 0.00321713, 0, -0.00321713, -0.00143568, -0.00017677, - 0.00143568, 0.0116601, 0.02612847, 0, -0.02612847, -0.0116601, -0.00143568, - 0.00321713, 0.02612847, 0.05854983, 0, -0.05854983, -0.02612847, -0.00321713, - 0, 0, 0, 0, 0, 0, 0, - -0.00321713, -0.02612847, -0.05854983, 0, 0.05854983, 0.02612847, 0.00321713, - -0.00143568, -0.0116601, -0.02612847, 0, 0.02612847, 0.0116601, 0.00143568, - -0.00017677, -0.00143568, -0.00321713, 0, 0.00321713, 0.00143568, 0.00017677 - ); - Mat image_filtered_xy; - filter2D(regionMask, image_filtered_xy, -1, kernel_filter_2,Point(-1,-1), 0,BORDER_DEFAULT ); - Mat xy_hist; - float range[] = { hist_range_min, hist_range_max } ; - const float* histRange = { range }; - bool uniform = true; bool accumulate = false; - calcHist( &image_filtered_xy, 1, 0, Mat(), xy_hist, 1, &histSize, &histRange, uniform, accumulate ); - return xy_hist; -} - -Mat get_hess_hist_yy(Mat regionMask, int histSize, float hist_range_min, float hist_range_max) -{ - Mat kernel_filter_3(7,7, CV_32FC1,1); - kernel_filter_3 = (Mat_<double>(7,7) << - 1.57130243e-04, 1.91423823e-03, 8.57902057e-03, 1.41444137e-02, 8.57902057e-03, 1.91423823e-03, 1.57130243e-04, - 7.17839338e-04, 8.74507340e-03, 3.91926999e-02, 6.46178379e-02, 3.91926999e-02, 8.74507340e-03, 7.17839338e-04, - 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, - -1.76805171e-03, -2.15392793e-02, -9.65323526e-02, -1.59154943e-01, -9.65323526e-02, -2.15392793e-02, -1.76805171e-03, - 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, - 7.17839338e-04, 8.74507340e-03, 3.91926999e-02, 6.46178379e-02, 3.91926999e-02, 8.74507340e-03, 7.17839338e-04, - 1.57130243e-04, 1.91423823e-03, 8.57902057e-03, 1.41444137e-02, 8.57902057e-03, 1.91423823e-03, 1.57130243e-04 - ); - Mat image_filtered_yy; - filter2D(regionMask, image_filtered_yy, -1, kernel_filter_3,Point(-1,-1), 0,BORDER_DEFAULT ); - Mat yy_hist; - float range[] = { hist_range_min, hist_range_max } ; - const float* histRange = { range }; - bool uniform = true; bool accumulate = false; - - calcHist( &image_filtered_yy, 1, 0, Mat(), yy_hist, 1, &histSize, &histRange, uniform, accumulate ); - return yy_hist; -} - -Mat get_orientation_hist(Mat regionMask, int histSize, float hist_range_min, float hist_range_max) -{ - Mat kernel_filter_1(3,3, CV_32FC1,1); - kernel_filter_1 = (Mat_<double>(3,3) << 1, 1, 1, 1, -8, 1, 1, 1, 1); - Mat kernel_filter_2(3,3, CV_32FC1,1); - kernel_filter_2 = (Mat_<double>(3,3) << 0,0,0,0,1,0,0,0,0); - Mat kernel_filter_3(3,3, CV_32FC1,1); - kernel_filter_3 = (Mat_<double>(3,3) << 1,2,1,0,0,0,-1,-2,-1); - Mat kernel_filter_4(3,3, CV_32FC1,1); - kernel_filter_4 = (Mat_<double>(3,3) << 1,0,-1,2,0,-2,1,0,-1); - Mat image_filtered_v1; - Mat image_filtered_v2; - Mat image_filtered_v3; - Mat image_filtered_v4; - int temp30; - //filtering - filter2D(regionMask, image_filtered_v1, -1, kernel_filter_1,Point(-1,-1), 0,BORDER_DEFAULT ); - filter2D(regionMask, image_filtered_v2, -1, kernel_filter_2,Point(-1,-1), 0,BORDER_DEFAULT ); - filter2D(regionMask, image_filtered_v3, -1, kernel_filter_3,Point(-1,-1), 0,BORDER_DEFAULT ); - filter2D(regionMask, image_filtered_v4, -1, kernel_filter_4,Point(-1,-1), 0,BORDER_DEFAULT ); - - //Orientation New - float temp_5; - float temp_6; - float temp_7_theta; - float temp_8_theta_dash; - int temp_9_theta_dash_quantized; - float quantized_1[12] = {0,1,2,3,4,5,6,7,8,9,10,11}; - int quantized_count_1 = 0; - float orientation_image_matrix[regionMask.rows][regionMask.cols]; - Mat orientation_image = regionMask.clone(); - for(int r = 0; r < regionMask.rows; r++) - { - for(int c = 0; c < regionMask.cols; c++) - { - orientation_image_matrix[r][c] = 0; - } - } - for(int r = 0; r < regionMask.rows; r++) - { - for(int c = 0; c < regionMask.cols; c++) - { - temp_5 = image_filtered_v3.at<schar>(r,c); - temp_6 = image_filtered_v4.at<schar>(r,c); - if(temp_6 != 0 && temp_5 != 0) - { - temp_7_theta = atan(temp_5/temp_6); - } - else if(temp_6 == 0 && temp_5 > 0) - { - temp_7_theta = M_PI/2; - } - else if(temp_6 == 0 && temp_5 < 0) - { - temp_7_theta = -M_PI/2; - } - else if(temp_6 == 0 && temp_5 == 0) - { - temp_7_theta = 0; - } - else if(temp_6 != 0 && temp_5 == 0) - { - temp_7_theta = 0; - } - - if(temp_5 >= 0 && temp_6 >= 0) - { - temp_8_theta_dash = temp_7_theta; - } - else if(temp_5 < 0 && temp_6 >= 0) - { - temp_8_theta_dash = temp_7_theta + M_PI; - } - else if(temp_5 < 0 && temp_6 < 0) - { - temp_8_theta_dash = temp_7_theta + M_PI; - } - else if(temp_5 >= 0 && temp_6 < 0) - { - temp_8_theta_dash = temp_7_theta + 2*M_PI; - } - temp_9_theta_dash_quantized = floor((temp_8_theta_dash*11)/(2*M_PI)); - orientation_image_matrix[r][c] = temp_9_theta_dash_quantized; - orientation_image.at<uchar>(r,c) = temp_9_theta_dash_quantized; - } - } -/* for(int r = 0; r < img.rows; r++) - { - for(int c = 0; c < img.cols; c++) - { - cout << orientation_image_matrix[r][c] << " "; - } - cout << endl; - } -*/ - float range[] = { hist_range_min, hist_range_max } ; - const float* histRange = { range }; - bool uniform = true; bool accumulate = false; - Mat orientation_image_hist; - - /// Compute the histograms: - calcHist( &orientation_image, 1, 0, Mat(), orientation_image_hist, 1, &histSize, &histRange, uniform, accumulate ); - return orientation_image_hist; -} - -Mat get_diff_exci_hist(Mat regionMask, int histSize, float hist_range_min, float hist_range_max) -{ - Mat kernel_filter_1(3,3, CV_32FC1,1); - kernel_filter_1 = (Mat_<double>(3,3) << 1, 1, 1, 1, -8, 1, 1, 1, 1); - Mat kernel_filter_2(3,3, CV_32FC1,1); - kernel_filter_2 = (Mat_<double>(3,3) << 0,0,0,0,1,0,0,0,0); - Mat kernel_filter_3(3,3, CV_32FC1,1); - kernel_filter_3 = (Mat_<double>(3,3) << 1,2,1,0,0,0,-1,-2,-1); - Mat kernel_filter_4(3,3, CV_32FC1,1); - kernel_filter_4 = (Mat_<double>(3,3) << 1,0,-1,2,0,-2,1,0,-1); - Mat image_filtered_v1; - Mat image_filtered_v2; - Mat image_filtered_v3; - Mat image_filtered_v4; - int temp30; - //filtering - filter2D(regionMask, image_filtered_v1, -1, kernel_filter_1,Point(-1,-1), 0,BORDER_DEFAULT ); - filter2D(regionMask, image_filtered_v2, -1, kernel_filter_2,Point(-1,-1), 0,BORDER_DEFAULT ); - filter2D(regionMask, image_filtered_v3, -1, kernel_filter_3,Point(-1,-1), 0,BORDER_DEFAULT ); - filter2D(regionMask, image_filtered_v4, -1, kernel_filter_4,Point(-1,-1), 0,BORDER_DEFAULT ); - - //Differential Excitation New - float temp_1; - float temp_2; - float temp_3_alpha; - float temp_4_quantized_alpha; - int quantized[8] = {0,1,2,3,4,5,6,7}; - int quantized_count = 0; - int differential_excitation_image_matrix[regionMask.rows][regionMask.cols]; - Mat differential_excitation_image = regionMask.clone(); - - for(int r = 0; r < regionMask.rows; r++) - { - for(int c = 0; c < regionMask.cols; c++) - { - differential_excitation_image_matrix[r][c] = 0; - } - } - for(int r = 0; r < regionMask.rows; r++) - { - for(int c = 0; c < regionMask.cols; c++) - { - temp_1 = image_filtered_v1.at<schar>(r,c); - temp_2 = image_filtered_v2.at<schar>(r,c); - if(temp_2 != 0) - { - temp_3_alpha = atan(temp_1/temp_2); - } - else if(temp_2 == 0 && temp_1 > 0) - { - temp_3_alpha = M_PI/2; - } - else if(temp_2 == 0 && temp_1 < 0) - { - temp_3_alpha = -M_PI/2; - } - else if(temp_2 == 0 && temp_1 == 0) - { - temp_3_alpha = 0; - } - temp_4_quantized_alpha = floor(((temp_3_alpha + M_PI/2)/M_PI)*7); - differential_excitation_image_matrix[r][c] = temp_4_quantized_alpha; - differential_excitation_image.at<uchar>(r,c) = temp_4_quantized_alpha; - } - } -/* for(int r = 0; r < img.rows; r++) - { - for(int c = 0; c < img.cols; c++) - { - cout << differential_excitation_image_matrix[r][c] << " "; - } - cout << endl; - } -*/ - float range[] = { hist_range_min, hist_range_max } ; - const float* histRange = { range }; - bool uniform = true; bool accumulate = false; - Mat differential_excitation_hist; - - /// Compute the histograms: - calcHist( &differential_excitation_image, 1, 0, Mat(), differential_excitation_hist, 1, &histSize, &histRange, uniform, accumulate ); - return differential_excitation_hist; -} - -void refineRegions(vector < vector <int> > sp, int total_masks, regionProperties regions[], int min_height, int min_width) -{ - for (int i = 0; i < total_masks; i++) - { - regions[i].setLabel(i); - regions[i].min_x = 100000; - regions[i].min_y = 100000; - regions[i].max_x = -1; - regions[i].max_y = -1; - } - for (int r = 0; r < sp.size(); r++) - { - for(int c = 0; c < sp[0].size(); c++) - { - regions[sp[r][c]].size++; - if(regions[sp[r][c]].min_x > c) - regions[sp[r][c]].min_x = c; - if(regions[sp[r][c]].min_y > r) - regions[sp[r][c]].min_y = r; - if(regions[sp[r][c]].max_x < c) - regions[sp[r][c]].max_x = c; - if(regions[sp[r][c]].max_y < r) - regions[sp[r][c]].max_y = r; - } - } - for (int i = 0; i < total_masks; i++) - { - if((regions[i].max_x - regions[i].min_x > min_width) and (regions[i].max_y - regions[i].min_y > min_height)) - regions[i].validity = true; - else - regions[i].validity = false; - } -} - - - -void createModel(vector < vector <int> > sp, int total_masks, Mat grayMask, regionProperties regions[], int histSize, float hist_range_min, float hist_range_max) -{ - Mat regionMask = grayMask.clone(); - - - for (int i = 0; i < total_masks; i++) - { - regionMask = grayMask.clone(); -// cout << i << endl; - for (int r = 0; r < sp.size(); r++) - { - for(int c = 0; c < sp[0].size(); c++) - { - if(sp[r][c] != i) - regionMask.at<uchar>(r,c) = 0; - } - } - if(regions[i].size<200) - regions[i].validity = false; -// cout << i << " hessian" << endl; - //Hessian Matrix - regions[i].xx_hist = get_hess_hist_xx(regionMask, histSize, hist_range_min, hist_range_max); - regions[i].xy_hist = get_hess_hist_xy(regionMask, histSize, hist_range_min, hist_range_max); - regions[i].yy_hist = get_hess_hist_yy(regionMask, histSize, hist_range_min, hist_range_max); - -// cout << i << " orien" << endl; - //orientation Matrix - regions[i].orientation_image_hist = get_orientation_hist(regionMask, histSize, hist_range_min, hist_range_max); - -// cout << i << " diff" << endl; - //Differential Excitation Matrix - regions[i].differential_excitation_hist = get_diff_exci_hist(regionMask, histSize, hist_range_min, hist_range_max); - -// cout << i << " color" << endl; - //Color Histogram - float range[] = { hist_range_min, hist_range_max } ; - const float* histRange = { range }; - bool uniform = true; bool accumulate = false; - calcHist(&grayMask, 1, 0, Mat(), regions[i].color_hist, 1, &histSize, &histRange, uniform, accumulate ); - - - } -} - -bool checkNeighbors(regionProperties a, regionProperties b) -{ - if( - ((a.min_x < b.min_x) and (b.min_x < a.max_x) and (a.min_y < b.min_y) and (b.min_y < a.max_y)) - or - ((a.min_x < b.max_x) and (b.max_x < a.max_x) and (a.min_y < b.max_y) and (b.max_y < a.max_y)) - or - ((a.min_x < b.min_x) and (b.min_x < a.max_x) and (a.min_y < b.max_y) and (b.max_y < a.max_y)) - or - ((a.min_x < b.max_x) and (b.min_x < a.max_x) and (a.min_y < b.max_y) and (b.max_y < a.max_y)) - ) - { - - return true; - } - - return false; -} - -vector < vector <int> > findNeighbors(regionProperties regions[], int total_masks, Mat regionMask, vector < vector <int> > sp) -{ - vector < vector <int> > neighbors; - vector <int> rows; - rows.push_back(0); - rows.push_back(0); - int num = 0; - for(int i = 1; i < total_masks-1; i++) - { - for(int j = i+1; j < i+20; j++) - { - if(j<total_masks-2) - { - if(checkNeighbors(regions[i], regions[j]) and regions[i].validity == true and regions[j].validity == true) - { - /* cout << i << " " << j << endl; - cout << regions[i].min_x << " " << regions[i].min_y << " " << regions[i].max_x << " " << regions[i].max_y << endl; - cout << regions[j].min_x << " " << regions[j].min_y << " " << regions[j].max_x << " " << regions[j].max_y << endl; - for (int r = 0; r < regionMask.rows; r++) - { - for(int c = 0; c < regionMask.cols; c++) - { - if(sp[r][c] != i) - { - regionMask.at<Vec3b>(r,c)[0] = 0; - regionMask.at<Vec3b>(r,c)[1] = 0; - regionMask.at<Vec3b>(r,c)[2] = 0; - } - else - { - regionMask.at<Vec3b>(r,c)[0] = 0; - regionMask.at<Vec3b>(r,c)[1] = 0; - regionMask.at<Vec3b>(r,c)[2] = 255; - } - } - } - for (int r = 0; r < regionMask.rows; r++) - { - for(int c = 0; c < regionMask.cols; c++) - { - if(sp[r][c] != j and regionMask.at<Vec3b>(r,c)[2] != 255) - { - regionMask.at<Vec3b>(r,c)[0] = 0; - regionMask.at<Vec3b>(r,c)[1] = 0; - regionMask.at<Vec3b>(r,c)[2] = 0; - } - else - { - regionMask.at<Vec3b>(r,c)[0] = 0; - regionMask.at<Vec3b>(r,c)[1] = 255; - regionMask.at<Vec3b>(r,c)[2] = 0; - } - } - } - imshow("regionMask", regionMask); - waitKey(0); - */ - rows[0] = i; - rows[1] = j; - neighbors.push_back(rows); - } - } - } - } - return neighbors; -} - -float calcSimilarities(regionProperties a, regionProperties b, float spSize) -{ - double sim = 0.0; - sim += compareHist( a.xx_hist, b.xx_hist, 1); - sim += compareHist( a.xy_hist, b.xy_hist, 1); - sim += compareHist( a.yy_hist, b.yy_hist, 1); - sim += compareHist( a.orientation_image_hist, b.orientation_image_hist, 1); - sim += compareHist( a.differential_excitation_hist, b.differential_excitation_hist, 1); - sim += compareHist( a.color_hist, b.color_hist, 1); - sim += 100 * ((a.size + b.size)/spSize); - double bbsize = ((max(a.max_x, b.max_x) - min(a.min_x, b.min_x))* (max(a.max_y, b.max_y) - min(a.min_y, b.min_y)) ); - sim += 100*((bbsize - a.size - b.size) / spSize); - return sim; -} - -void mergeRegions(int value, regionProperties regions[], vector < vector <int> > sp_neighbors, Mat grayMask, vector < vector <int> > sp, int minRegionSize, int histSize, float hist_range_min, float hist_range_max) -{ -/* - void setBoundaries(int ix, int iy, int ax, int ay); - int label; - int min_x; - int max_x; - int min_y; - int max_y; - int size; - Mat xx_hist, xy_hist, yy_hist, orientation_image_hist,differential_excitation_hist, color_hist; - vector <int> neighbors; - bool validity; -*/ - regions[sp_neighbors[value][0]].validity = true; - regions[sp_neighbors[value][1]].validity = false; - regions[sp_neighbors[value][0]].min_x = min(regions[sp_neighbors[value][0]].min_x, regions[sp_neighbors[value][1]].min_x); - regions[sp_neighbors[value][0]].max_y = max(regions[sp_neighbors[value][0]].max_y, regions[sp_neighbors[value][1]].max_y); - regions[sp_neighbors[value][0]].min_x = min(regions[sp_neighbors[value][0]].min_x, regions[sp_neighbors[value][1]].min_x); - regions[sp_neighbors[value][0]].max_y = max(regions[sp_neighbors[value][0]].max_y, regions[sp_neighbors[value][1]].max_y); - regions[sp_neighbors[value][0]].size = regions[sp_neighbors[value][0]].size + regions[sp_neighbors[value][1]].size; - - Mat regionMask = grayMask.clone(); - int i = sp_neighbors[value][0]; - for (int r = 0; r < sp.size(); r++) - { - for(int c = 0; c < sp[0].size(); c++) - { - if(sp[r][c] != i) - regionMask.at<uchar>(r,c) = 0; - } - } - if(regions[i].size<minRegionSize) - regions[i].validity = false; -// cout << i << " hessian " << regionMask.channels() << endl; - //Hessian Matrix - regions[i].xx_hist = get_hess_hist_xx(regionMask, histSize, hist_range_min, hist_range_max); - regions[i].xy_hist = get_hess_hist_xy(regionMask, histSize, hist_range_min, hist_range_max); - regions[i].yy_hist = get_hess_hist_yy(regionMask, histSize, hist_range_min, hist_range_max); - -// cout << i << " orien" << endl; - //orientation Matrix - regions[i].orientation_image_hist = get_orientation_hist(regionMask, histSize, hist_range_min, hist_range_max); - -// cout << i << " diff" << endl; - //Differential Excitation Matrix - regions[i].differential_excitation_hist = get_diff_exci_hist(regionMask, histSize, hist_range_min, hist_range_max); - -// cout << i << " color" << endl; - //Color Histogram - float range[] = { hist_range_min, hist_range_max } ; - const float* histRange = { range }; - bool uniform = true; bool accumulate = false; - calcHist(&grayMask, 1, 0, Mat(), regions[i].color_hist, 1, &histSize, &histRange, uniform, accumulate ); -} - -bool checkRounds(int totals, regionProperties regions[], int numRounds) -{ - int num = 0; - for(int i = 0; i < totals; i++) - { - if(regions[i].validity == false) - { - num++; - } - } -// cout << "num = " << num << endl; - if(num > numRounds) - return true; - else - return false; -} - -vector < vector <int> > extractROIs(int total_masks, regionProperties regions[], int numRounds, float spSize, vector < vector <int> > sp, Mat img, Mat gray_mask, int minRegionSize, int histSize, float hist_range_min, float hist_range_max) -{ - vector < vector <int> > pts; - int totals = total_masks; - int value = 10; - while(checkRounds(totals, regions, numRounds) and value > 0) - { - vector < vector <int> > sp_neighbors = findNeighbors(regions, total_masks, img, sp); - vector <float> similarities; - for(int i=0; i<sp_neighbors.size(); i++) - { - - float sim = calcSimilarities(regions[sp_neighbors[i][0]],regions[sp_neighbors[i][1]], spSize); -// cout << i << " " << sp_neighbors[i][0] << " " << sp_neighbors[i][1] << " " << sim << endl; - similarities.push_back(sim); - } - - //finding closest two regions - value = min_element(similarities.begin(), similarities.end()) - similarities.begin(); -// cout << "in here" << endl; - - //merging - mergeRegions(value, regions, sp_neighbors, gray_mask, sp, minRegionSize, histSize, hist_range_min, hist_range_max); -// cout << "value = " << value << endl; - for(int i = 0; i < total_masks; i++) - { - if(regions[i].validity == true) - { - vector <int> temp; - temp.push_back(regions[i].min_x); - temp.push_back(regions[i].min_y); - temp.push_back(regions[i].max_x); - temp.push_back(regions[i].max_y); - pts.push_back(temp); - // rectangle(outputImg, Point(regions[i].min_x, regions[i].min_y), Point(regions[i].max_x, regions[i].max_y), Scalar(0, 0, 255)); - } - } - } - std::sort(pts.begin(), pts.end()); - pts.erase(std::unique(pts.begin(), pts.end()), pts.end()); - cout << "Total bounding boxes = " << pts.size() << endl; - return pts; -} - diff --git a/detectors/include/od/detectors/global2D/localization/Disjoint-set.h b/detectors/include/od/detectors/global2D/localization/Disjoint-set.h deleted file mode 100755 index 69d81566..00000000 --- a/detectors/include/od/detectors/global2D/localization/Disjoint-set.h +++ /dev/null @@ -1,77 +0,0 @@ -/* -Copyright (C) 2006 Pedro Felzenszwalb - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#pragma once - -// disjoint-set forests using union-by-rank and path compression (sort of). - -typedef struct { - int rank; - int p; - int size; -} uni_elt; - -class universe { -public: - universe(int elements); - ~universe(); - int find(int x); - void join(int x, int y); - int size(int x) const { return elts[x].size; } - int num_sets() const { return num; } - -private: - uni_elt *elts; - int num; -}; - -universe::universe(int elements) { - elts = new uni_elt[elements]; - num = elements; - for (int i = 0; i < elements; i++) { - elts[i].rank = 0; - elts[i].size = 1; - elts[i].p = i; - } -} - -universe::~universe() { - delete [] elts; -} - -int universe::find(int x) { - int y = x; - while (y != elts[y].p) - y = elts[y].p; - elts[x].p = y; - return y; -} - -void universe::join(int x, int y) { - if (elts[x].rank > elts[y].rank) { - elts[y].p = x; - elts[x].size += elts[y].size; - } else { - elts[x].p = y; - elts[y].size += elts[x].size; - if (elts[x].rank == elts[y].rank) - elts[y].rank++; - } - num--; -} - diff --git a/detectors/include/od/detectors/global2D/localization/Filter.h b/detectors/include/od/detectors/global2D/localization/Filter.h deleted file mode 100755 index be1007ee..00000000 --- a/detectors/include/od/detectors/global2D/localization/Filter.h +++ /dev/null @@ -1,97 +0,0 @@ -/* -Copyright (C) 2006 Pedro Felzenszwalb - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -/* simple filters */ - -#pragma once - -#include <vector> -#include <cmath> -#include "od/detectors/global2D/localization/Image.h" -#include "od/detectors/global2D/localization/Misc.h" -#include "od/detectors/global2D/localization/Convolve.h" -#include "od/detectors/global2D/localization/Imconv.h" - -#define WIDTH 4.0 - -/* normalize mask so it integrates to one */ -static void normalize(std::vector<float> &mask) { - int len = mask.size(); - float sum = 0; - for (int i = 1; i < len; i++) { - sum += fabs(mask[i]); - } - sum = 2*sum + fabs(mask[0]); - for (int i = 0; i < len; i++) { - mask[i] /= sum; - } -} - -/* make filters */ -#define MAKE_FILTER(name, fun) \ -static std::vector<float> make_ ## name (float sigma) { \ - sigma = std::max(sigma, 0.01F); \ - int len = (int)ceil(sigma * WIDTH) + 1; \ - std::vector<float> mask(len); \ - for (int i = 0; i < len; i++) { \ - mask[i] = fun; \ - } \ - return mask; \ -} - -MAKE_FILTER(fgauss, exp(-0.5*square(i/sigma))); - -/* convolve image with gaussian filter */ -static image<float> *smooth(image<float> *src, float sigma) { - std::vector<float> mask = make_fgauss(sigma); - normalize(mask); - - image<float> *tmp = new image<float>(src->height(), src->width(), false); - image<float> *dst = new image<float>(src->width(), src->height(), false); - convolve_even(src, tmp, mask); - convolve_even(tmp, dst, mask); - - delete tmp; - return dst; -} - -/* convolve image with gaussian filter */ -image<float> *smooth(image<uchar> *src, float sigma) { - image<float> *tmp = imageUCHARtoFLOAT(src); - image<float> *dst = smooth(tmp, sigma); - delete tmp; - return dst; -} - -/* compute laplacian */ -static image<float> *laplacian(image<float> *src) { - int width = src->width(); - int height = src->height(); - image<float> *dst = new image<float>(width, height); - - for (int y = 1; y < height-1; y++) { - for (int x = 1; x < width-1; x++) { - float d2x = imRef(src, x-1, y) + imRef(src, x+1, y) - - 2*imRef(src, x, y); - float d2y = imRef(src, x, y-1) + imRef(src, x, y+1) - - 2*imRef(src, x, y); - imRef(dst, x, y) = d2x + d2y; - } - } - return dst; -} diff --git a/detectors/include/od/detectors/global2D/localization/Image.h b/detectors/include/od/detectors/global2D/localization/Image.h deleted file mode 100755 index 891cc020..00000000 --- a/detectors/include/od/detectors/global2D/localization/Image.h +++ /dev/null @@ -1,98 +0,0 @@ -/* -Copyright (C) 2006 Pedro Felzenszwalb - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -/* a simple image class */ - -#pragma once - -#include <cstring> - -template <class T> -class image { - public: - /* create an image */ - image(const int width, const int height, const bool init = true); - - /* delete an image */ - ~image(); - - /* init an image */ - void init(const T &val); - - /* copy an image */ - image<T> *copy() const; - - /* get the width of an image. */ - int width() const { return w; } - - /* get the height of an image. */ - int height() const { return h; } - - /* image data. */ - T *data; - - /* row pointers. */ - T **access; - - private: - int w, h; -}; - -/* use imRef to access image data. */ -#define imRef(im, x, y) (im->access[y][x]) - -/* use imPtr to get pointer to image data. */ -#define imPtr(im, x, y) &(im->access[y][x]) - -template <class T> -image<T>::image(const int width, const int height, const bool init) { - w = width; - h = height; - data = new T[w * h]; // allocate space for image data - access = new T*[h]; // allocate space for row pointers - - // initialize row pointers - for (int i = 0; i < h; i++) - access[i] = data + (i * w); - - if (init) - memset(data, 0, w * h * sizeof(T)); -} - -template <class T> -image<T>::~image() { - delete [] data; - delete [] access; -} - -template <class T> -void image<T>::init(const T &val) { - T *ptr = imPtr(this, 0, 0); - T *end = imPtr(this, w-1, h-1); - while (ptr <= end) - *ptr++ = val; -} - - -template <class T> -image<T> *image<T>::copy() const { - image<T> *im = new image<T>(w, h, false); - memcpy(im->data, data, w * h * sizeof(T)); - return im; -} - diff --git a/detectors/include/od/detectors/global2D/localization/Imconv.h b/detectors/include/od/detectors/global2D/localization/Imconv.h deleted file mode 100755 index 4325b6fc..00000000 --- a/detectors/include/od/detectors/global2D/localization/Imconv.h +++ /dev/null @@ -1,175 +0,0 @@ -/* -Copyright (C) 2006 Pedro Felzenszwalb - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -/* image conversion */ - -#pragma once - -#include <climits> -#include "od/detectors/global2D/localization/Image.h" -#include "od/detectors/global2D/localization/Imutil.h" -#include "od/detectors/global2D/localization/Misc.h" - -#define RED_WEIGHT 0.299 -#define GREEN_WEIGHT 0.587 -#define BLUE_WEIGHT 0.114 - -static image<uchar> *imageRGBtoGRAY(image<rgb> *input) { - int width = input->width(); - int height = input->height(); - image<uchar> *output = new image<uchar>(width, height, false); - - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - imRef(output, x, y) = (uchar) - (imRef(input, x, y).r * RED_WEIGHT + - imRef(input, x, y).g * GREEN_WEIGHT + - imRef(input, x, y).b * BLUE_WEIGHT); - } - } - return output; -} - -static image<rgb> *imageGRAYtoRGB(image<uchar> *input) { - int width = input->width(); - int height = input->height(); - image<rgb> *output = new image<rgb>(width, height, false); - - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - imRef(output, x, y).r = imRef(input, x, y); - imRef(output, x, y).g = imRef(input, x, y); - imRef(output, x, y).b = imRef(input, x, y); - } - } - return output; -} - -static image<float> *imageUCHARtoFLOAT(image<uchar> *input) { - int width = input->width(); - int height = input->height(); - image<float> *output = new image<float>(width, height, false); - - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - imRef(output, x, y) = imRef(input, x, y); - } - } - return output; -} - -static image<float> *imageINTtoFLOAT(image<int> *input) { - int width = input->width(); - int height = input->height(); - image<float> *output = new image<float>(width, height, false); - - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - imRef(output, x, y) = imRef(input, x, y); - } - } - return output; -} - -static image<uchar> *imageFLOATtoUCHAR(image<float> *input, - float min, float max) { - int width = input->width(); - int height = input->height(); - image<uchar> *output = new image<uchar>(width, height, false); - - if (max == min) - return output; - - float scale = UCHAR_MAX / (max - min); - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - uchar val = (uchar)((imRef(input, x, y) - min) * scale); - imRef(output, x, y) = bound(val, (uchar)0, (uchar)UCHAR_MAX); - } - } - return output; -} - -static image<uchar> *imageFLOATtoUCHAR(image<float> *input) { - float min, max; - min_max(input, &min, &max); - return imageFLOATtoUCHAR(input, min, max); -} - -static image<long> *imageUCHARtoLONG(image<uchar> *input) { - int width = input->width(); - int height = input->height(); - image<long> *output = new image<long>(width, height, false); - - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - imRef(output, x, y) = imRef(input, x, y); - } - } - return output; -} - -static image<uchar> *imageLONGtoUCHAR(image<long> *input, long min, long max) { - int width = input->width(); - int height = input->height(); - image<uchar> *output = new image<uchar>(width, height, false); - - if (max == min) - return output; - - float scale = UCHAR_MAX / (float)(max - min); - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - uchar val = (uchar)((imRef(input, x, y) - min) * scale); - imRef(output, x, y) = bound(val, (uchar)0, (uchar)UCHAR_MAX); - } - } - return output; -} - -static image<uchar> *imageLONGtoUCHAR(image<long> *input) { - long min, max; - min_max(input, &min, &max); - return imageLONGtoUCHAR(input, min, max); -} - -static image<uchar> *imageSHORTtoUCHAR(image<short> *input, - short min, short max) { - int width = input->width(); - int height = input->height(); - image<uchar> *output = new image<uchar>(width, height, false); - - if (max == min) - return output; - - float scale = UCHAR_MAX / (float)(max - min); - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - uchar val = (uchar)((imRef(input, x, y) - min) * scale); - imRef(output, x, y) = bound(val, (uchar)0, (uchar)UCHAR_MAX); - } - } - return output; -} - -static image<uchar> *imageSHORTtoUCHAR(image<short> *input) { - short min, max; - min_max(input, &min, &max); - return imageSHORTtoUCHAR(input, min, max); -} - diff --git a/detectors/include/od/detectors/global2D/localization/Imutil.h b/detectors/include/od/detectors/global2D/localization/Imutil.h deleted file mode 100755 index 1e14b9eb..00000000 --- a/detectors/include/od/detectors/global2D/localization/Imutil.h +++ /dev/null @@ -1,63 +0,0 @@ -/* -Copyright (C) 2006 Pedro Felzenszwalb - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -/* some image utilities */ - -#pragma once - -#include "od/detectors/global2D/localization/Image.h" -#include "od/detectors/global2D/localization/Misc.h" - -/* compute minimum and maximum value in an image */ -template <class T> -void min_max(image<T> *im, T *ret_min, T *ret_max) { - int width = im->width(); - int height = im->height(); - - T min = imRef(im, 0, 0); - T max = imRef(im, 0, 0); - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - T val = imRef(im, x, y); - if (min > val) - min = val; - if (max < val) - max = val; - } - } - - *ret_min = min; - *ret_max = max; -} - -/* threshold image */ -template <class T> -image<uchar> *threshold(image<T> *src, int t) { - int width = src->width(); - int height = src->height(); - image<uchar> *dst = new image<uchar>(width, height); - - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - imRef(dst, x, y) = (imRef(src, x, y) >= t); - } - } - - return dst; -} - diff --git a/detectors/include/od/detectors/global2D/localization/Misc.h b/detectors/include/od/detectors/global2D/localization/Misc.h deleted file mode 100755 index 95dabe15..00000000 --- a/detectors/include/od/detectors/global2D/localization/Misc.h +++ /dev/null @@ -1,63 +0,0 @@ -/* -Copyright (C) 2006 Pedro Felzenszwalb - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -/* random stuff */ - -#pragma once - -#include <cmath> - -#ifndef M_PI -#define M_PI 3.141592653589793 -#endif - -typedef unsigned char uchar; - -typedef struct { uchar r, g, b; } rgb; - -inline bool operator==(const rgb &a, const rgb &b) { - return ((a.r == b.r) && (a.g == b.g) && (a.b == b.b)); -} - -template <class T> -inline T abs(const T &x) { return (x > 0 ? x : -x); }; - -template <class T> -inline int sign(const T &x) { return (x >= 0 ? 1 : -1); }; - -template <class T> -inline T square(const T &x) { return x*x; }; - -template <class T> -inline T bound(const T &x, const T &min, const T &max) { - return (x < min ? min : (x > max ? max : x)); -} - -template <class T> -inline bool check_bound(const T &x, const T&min, const T &max) { - return ((x < min) || (x > max)); -} - -inline int vlib_round(float x) { return (int)(x + 0.5F); } - -inline int vlib_round(double x) { return (int)(x + 0.5); } - -inline double gaussian(double val, double sigma) { - return exp(-square(val/sigma)/2)/(sqrt(2*M_PI)*sigma); -} - diff --git a/detectors/include/od/detectors/global2D/localization/Pnmfile.h b/detectors/include/od/detectors/global2D/localization/Pnmfile.h deleted file mode 100755 index f69738a2..00000000 --- a/detectors/include/od/detectors/global2D/localization/Pnmfile.h +++ /dev/null @@ -1,208 +0,0 @@ -/* -Copyright (C) 2006 Pedro Felzenszwalb - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -/* basic image I/O */ - -#pragma once - -#include <cstdlib> -#include <climits> -#include <cstring> -#include <fstream> -#include "od/detectors/global2D/localization/Image.h" -#include "od/detectors/global2D/localization/Misc.h" - -#define BUF_SIZE 256 - -class pnm_error { }; - -static void read_packed(unsigned char *data, int size, std::ifstream &f) { - unsigned char c = 0; - - int bitshift = -1; - for (int pos = 0; pos < size; pos++) { - if (bitshift == -1) { - c = f.get(); - bitshift = 7; - } - data[pos] = (c >> bitshift) & 1; - bitshift--; - } -} - -static void write_packed(unsigned char *data, int size, std::ofstream &f) { - unsigned char c = 0; - - int bitshift = 7; - for (int pos = 0; pos < size; pos++) { - c = c | (data[pos] << bitshift); - bitshift--; - if ((bitshift == -1) || (pos == size-1)) { - f.put(c); - bitshift = 7; - c = 0; - } - } -} - -/* read PNM field, skipping comments */ -static void pnm_read(std::ifstream &file, char *buf) { - char doc[BUF_SIZE]; - char c; - - file >> c; - while (c == '#') { - file.getline(doc, BUF_SIZE); - file >> c; - } - file.putback(c); - - file.width(BUF_SIZE); - file >> buf; - file.ignore(); -} - -static image<uchar> *loadPBM(const char *name) { - char buf[BUF_SIZE]; - - /* read header */ - std::ifstream file(name, std::ios::in | std::ios::binary); - pnm_read(file, buf); - if (strncmp(buf, "P4", 2)) - throw pnm_error(); - - pnm_read(file, buf); - int width = atoi(buf); - pnm_read(file, buf); - int height = atoi(buf); - - /* read data */ - image<uchar> *im = new image<uchar>(width, height); - for (int i = 0; i < height; i++) - read_packed(imPtr(im, 0, i), width, file); - - return im; -} - -static void savePBM(image<uchar> *im, const char *name) { - int width = im->width(); - int height = im->height(); - std::ofstream file(name, std::ios::out | std::ios::binary); - - file << "P4\n" << width << " " << height << "\n"; - for (int i = 0; i < height; i++) - write_packed(imPtr(im, 0, i), width, file); -} - -static image<uchar> *loadPGM(const char *name) { - char buf[BUF_SIZE]; - - /* read header */ - std::ifstream file(name, std::ios::in | std::ios::binary); - pnm_read(file, buf); - if (strncmp(buf, "P5", 2)) - throw pnm_error(); - - pnm_read(file, buf); - int width = atoi(buf); - pnm_read(file, buf); - int height = atoi(buf); - - pnm_read(file, buf); - if (atoi(buf) > UCHAR_MAX) - throw pnm_error(); - - /* read data */ - image<uchar> *im = new image<uchar>(width, height); - file.read((char *)imPtr(im, 0, 0), width * height * sizeof(uchar)); - - return im; -} - -static void savePGM(image<uchar> *im, const char *name) { - int width = im->width(); - int height = im->height(); - std::ofstream file(name, std::ios::out | std::ios::binary); - - file << "P5\n" << width << " " << height << "\n" << UCHAR_MAX << "\n"; - file.write((char *)imPtr(im, 0, 0), width * height * sizeof(uchar)); -} - -static image<rgb> *loadPPM(const char *name) { - char buf[BUF_SIZE], doc[BUF_SIZE]; - - /* read header */ - std::ifstream file(name, std::ios::in | std::ios::binary); - pnm_read(file, buf); - if (strncmp(buf, "P6", 2)) - throw pnm_error(); - - pnm_read(file, buf); - int width = atoi(buf); - pnm_read(file, buf); - int height = atoi(buf); - - pnm_read(file, buf); - if (atoi(buf) > UCHAR_MAX) - throw pnm_error(); - - /* read data */ - image<rgb> *im = new image<rgb>(width, height); - file.read((char *)imPtr(im, 0, 0), width * height * sizeof(rgb)); - - return im; -} - -static void savePPM(image<rgb> *im, const char *name) { - int width = im->width(); - int height = im->height(); - std::ofstream file(name, std::ios::out | std::ios::binary); - - file << "P6\n" << width << " " << height << "\n" << UCHAR_MAX << "\n"; - file.write((char *)imPtr(im, 0, 0), width * height * sizeof(rgb)); -} - -template <class T> -void load_image(image<T> **im, const char *name) { - char buf[BUF_SIZE]; - - /* read header */ - std::ifstream file(name, std::ios::in | std::ios::binary); - pnm_read(file, buf); - if (strncmp(buf, "VLIB", 9)) - throw pnm_error(); - - pnm_read(file, buf); - int width = atoi(buf); - pnm_read(file, buf); - int height = atoi(buf); - - /* read data */ - *im = new image<T>(width, height); - file.read((char *)imPtr((*im), 0, 0), width * height * sizeof(T)); -} - -template <class T> -void save_image(image<T> *im, const char *name) { - int width = im->width(); - int height = im->height(); - std::ofstream file(name, std::ios::out | std::ios::binary); - - file << "VLIB\n" << width << " " << height << "\n"; - file.write((char *)imPtr(im, 0, 0), width * height * sizeof(T)); -} diff --git a/detectors/include/od/detectors/global2D/localization/Segment-graph.h b/detectors/include/od/detectors/global2D/localization/Segment-graph.h deleted file mode 100755 index c79d1d3b..00000000 --- a/detectors/include/od/detectors/global2D/localization/Segment-graph.h +++ /dev/null @@ -1,81 +0,0 @@ -/* -Copyright (C) 2006 Pedro Felzenszwalb - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#pragma once - -#include <algorithm> -#include <cmath> -#include "od/detectors/global2D/localization/Disjoint-set.h" - -// threshold function -#define THRESHOLD(size, c) (c/size) - -typedef struct { - float w; - int a, b; -} edge; - -bool operator<(const edge &a, const edge &b) { - return a.w < b.w; -} - -/* - * Segment a graph - * - * Returns a disjoint-set forest representing the segmentation. - * - * num_vertices: number of vertices in graph. - * num_edges: number of edges in graph - * edges: array of edges. - * c: constant for treshold function. - */ -universe *segment_graph(int num_vertices, int num_edges, edge *edges, - float c) { - // sort edges by weight - std::sort(edges, edges + num_edges); - - // make a disjoint-set forest - universe *u = new universe(num_vertices); - - // init thresholds - float *threshold = new float[num_vertices]; - for (int i = 0; i < num_vertices; i++) - threshold[i] = THRESHOLD(1,c); - - // for each edge, in non-decreasing weight order... - for (int i = 0; i < num_edges; i++) { - edge *pedge = &edges[i]; - - // components conected by this edge - int a = u->find(pedge->a); - int b = u->find(pedge->b); - if (a != b) { - if ((pedge->w <= threshold[a]) && - (pedge->w <= threshold[b])) { - u->join(a, b); - a = u->find(a); - threshold[a] = pedge->w + THRESHOLD(u->size(a), c); - } - } - } - - // free up - delete threshold; - return u; -} - diff --git a/detectors/include/od/detectors/global2D/localization/Segment-image.h b/detectors/include/od/detectors/global2D/localization/Segment-image.h deleted file mode 100755 index 12080265..00000000 --- a/detectors/include/od/detectors/global2D/localization/Segment-image.h +++ /dev/null @@ -1,152 +0,0 @@ -/* -Copyright (C) 2006 Pedro Felzenszwalb - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#pragma once - -#include <cstdlib> -#include "od/detectors/global2D/localization/Image.h" -#include "od/detectors/global2D/localization/Misc.h" -#include "od/detectors/global2D/localization/Filter.h" -#include "od/detectors/global2D/localization/Segment-graph.h" - -// random color -rgb random_rgb(){ - rgb c; - double r; - - c.r = (uchar)random(); - c.g = (uchar)random(); - c.b = (uchar)random(); - - return c; -} - -// dissimilarity measure between pixels -static inline float diff(image<float> *r, image<float> *g, image<float> *b, - int x1, int y1, int x2, int y2) { - return sqrt(square(imRef(r, x1, y1)-imRef(r, x2, y2)) + - square(imRef(g, x1, y1)-imRef(g, x2, y2)) + - square(imRef(b, x1, y1)-imRef(b, x2, y2))); -} - -/* - * Segment an image - * - * Returns a color image representing the segmentation. - * - * im: image to segment. - * sigma: to smooth the image. - * c: constant for treshold function. - * min_size: minimum component size (enforced by post-processing stage). - * num_ccs: number of connected components in the segmentation. - */ -image<rgb> *segment_image(image<rgb> *im, float sigma, float c, int min_size, - int *num_ccs) { - int width = im->width(); - int height = im->height(); - - image<float> *r = new image<float>(width, height); - image<float> *g = new image<float>(width, height); - image<float> *b = new image<float>(width, height); - - // smooth each color channel - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - imRef(r, x, y) = imRef(im, x, y).r; - imRef(g, x, y) = imRef(im, x, y).g; - imRef(b, x, y) = imRef(im, x, y).b; - } - } - image<float> *smooth_r = smooth(r, sigma); - image<float> *smooth_g = smooth(g, sigma); - image<float> *smooth_b = smooth(b, sigma); - delete r; - delete g; - delete b; - - // build graph - edge *edges = new edge[width*height*4]; - int num = 0; - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - if (x < width-1) { - edges[num].a = y * width + x; - edges[num].b = y * width + (x+1); - edges[num].w = diff(smooth_r, smooth_g, smooth_b, x, y, x+1, y); - num++; - } - - if (y < height-1) { - edges[num].a = y * width + x; - edges[num].b = (y+1) * width + x; - edges[num].w = diff(smooth_r, smooth_g, smooth_b, x, y, x, y+1); - num++; - } - - if ((x < width-1) && (y < height-1)) { - edges[num].a = y * width + x; - edges[num].b = (y+1) * width + (x+1); - edges[num].w = diff(smooth_r, smooth_g, smooth_b, x, y, x+1, y+1); - num++; - } - - if ((x < width-1) && (y > 0)) { - edges[num].a = y * width + x; - edges[num].b = (y-1) * width + (x+1); - edges[num].w = diff(smooth_r, smooth_g, smooth_b, x, y, x+1, y-1); - num++; - } - } - } - delete smooth_r; - delete smooth_g; - delete smooth_b; - - // segment - universe *u = segment_graph(width*height, num, edges, c); - - // post process small components - for (int i = 0; i < num; i++) { - int a = u->find(edges[i].a); - int b = u->find(edges[i].b); - if ((a != b) && ((u->size(a) < min_size) || (u->size(b) < min_size))) - u->join(a, b); - } - delete [] edges; - *num_ccs = u->num_sets(); - - image<rgb> *output = new image<rgb>(width, height); - - // pick random colors for each component - rgb *colors = new rgb[width*height]; - for (int i = 0; i < width*height; i++) - colors[i] = random_rgb(); - - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - int comp = u->find(y * width + x); - imRef(output, x, y) = colors[comp]; - } - } - - delete [] colors; - delete u; - - return output; -} - diff --git a/detectors/include/od/detectors/global2D/localization/SelectiveSearchBase.h b/detectors/include/od/detectors/global2D/localization/SelectiveSearchBase.h deleted file mode 100644 index fd641102..00000000 --- a/detectors/include/od/detectors/global2D/localization/SelectiveSearchBase.h +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include "od/common/pipeline/Detector.h" -#include "od/common/pipeline/Scene.h" -#include "od/common/utils/Utils.h" -#include "od/common/utils/FeatureDetector2D.h" -#include "od/common/utils/Shared_pointers.h" -#include "od/detectors/global2D/localization/Image.h" -#include "od/detectors/global2D/localization/Misc.h" -#include "od/detectors/global2D/localization/Pnmfile.h" -#include "od/detectors/global2D/localization/Segment-image.h" - -#include <opencv2/opencv.hpp> -#include <opencv2/objdetect/objdetect.hpp> -#include <opencv2/highgui/highgui.hpp> -#include <opencv2/imgproc/imgproc.hpp> -#include <opencv2/core/core.hpp> - -#include <cmath> -#include <cstdio> -#include <vector> -#include <iostream> -#include <algorithm> -#include <stdio.h> -#include <math.h> -#include <stdlib.h> -#include <time.h> -#include <cstdlib> -#include <iomanip> -#include <fstream> -#include <ctime> -#include <cstring> -#include <sstream> -#include <string> -#include <time.h> - -namespace od -{ - namespace g2d - { - class SelectiveSearchBase: public Detector2D - { - public: - - SelectiveSearchBase(const std::string & trained_data_location = std::string("")); - - void acquireImages(const std::string & image_location, int img_width, int img_height); - cv::Mat preProcessImg(const cv::Mat & image); - std::vector<std::vector<int> > getSuperPixels(const cv::Mat & im, int & total_masks, float sigma, float k, float min_size, - const std::string & image_location); - - void init(); - shared_ptr<Detections2D> detectOmni(shared_ptr<SceneImage> scene); - shared_ptr<Detections> detect(shared_ptr<SceneImage> scene); - - private: - - cv::Mat img, cluster, outputImg, sp_preProcessed, gray_mask; - int inputImageHeight; - int inputImageWidth; - int total_masks; - std::vector<std::vector<int> > sp; - }; - } -} - - diff --git a/detectors/include/od/detectors/global2D/localization/SelectiveSearchModel.h b/detectors/include/od/detectors/global2D/localization/SelectiveSearchModel.h deleted file mode 100644 index df118527..00000000 --- a/detectors/include/od/detectors/global2D/localization/SelectiveSearchModel.h +++ /dev/null @@ -1,95 +0,0 @@ -#pragma once - -#include "od/common/pipeline/Detector.h" -#include "od/common/pipeline/Scene.h" -#include "od/common/utils/Utils.h" -#include "od/common/utils/FeatureDetector2D.h" -#include "od/common/utils/Shared_pointers.h" - -#include <opencv2/opencv.hpp> -#include <opencv2/objdetect/objdetect.hpp> -#include <opencv2/highgui/highgui.hpp> -#include <opencv2/imgproc/imgproc.hpp> -#include <opencv2/core/core.hpp> - -#include <cmath> -#include <cstdio> -#include <vector> -#include <iostream> -#include <algorithm> -#include <stdio.h> -#include <math.h> -#include <stdlib.h> -#include <time.h> -#include <cstdlib> -#include <iomanip> -#include <fstream> -#include <ctime> -#include <cstring> -#include <sstream> -#include <string> -#include <time.h> - -namespace od -{ - namespace g2d - { - class SelectiveSearchModel: public Detector2D - { - public: - - SelectiveSearchModel(const std::string & trained_data_location = std::string("")); - - void setLabel(int l); - void setBoundaries(int ix, int iy, int ax, int ay); - - void init(); - shared_ptr<Detections2D> detectOmni(shared_ptr<SceneImage> scene); - shared_ptr<Detections> detect(shared_ptr<SceneImage> scene); - shared_ptr<Detections> detectOmni(shared_ptr<Scene> scene); - - int min_x; - int max_x; - int min_y; - int max_y; - bool validity; - int size; - - int label; - - cv::Mat xx_hist, xy_hist, yy_hist, orientation_image_hist, differential_excitation_hist, color_hist; - std::vector<int> neighbors; - - }; - - cv::Mat get_hess_hist_xx(const cv::Mat & regionMask, int histSize, float hist_range_min, float hist_range_max); - - cv::Mat get_hess_hist_xy(const cv::Mat & regionMask, int histSize, float hist_range_min, float hist_range_max); - - cv::Mat get_hess_hist_yy(const cv::Mat & regionMask, int histSize, float hist_range_min, float hist_range_max); - - cv::Mat get_orientation_hist(const cv::Mat & regionMask, int histSize, float hist_range_min, float hist_range_max); - - cv::Mat get_diff_exci_hist(const cv::Mat & regionMask, int histSize, float hist_range_min, float hist_range_max); - - void refineRegions(const std::vector<std::vector<int> > & sp, int total_masks, SelectiveSearchModel regions[], int min_height, int min_width); - - void createModel(const std::vector<std::vector<int> > & sp, int total_masks, const cv::Mat & grayMask, SelectiveSearchModel regions[], - int histSize, float hist_range_min, float hist_range_max); - - bool checkNeighbors(const SelectiveSearchModel & a, const SelectiveSearchModel & b); - - std::vector<std::vector<int> > findNeighbors(SelectiveSearchModel regions[], int total_masks, cv::Mat & regionMask, std::vector<std::vector<int> > & sp); - float calcSimilarities(const SelectiveSearchModel & a, const SelectiveSearchModel & b, float spSize); - - void mergeRegions(int value, SelectiveSearchModel regions[], std::vector<std::vector<int> > & sp_neighbors, cv::Mat & grayMask, - const std::vector<std::vector<int> > & sp, int minRegionSize, int histSize, float hist_range_min, float hist_range_max); - - bool checkRounds(int totals, SelectiveSearchModel regions[], int numRounds); - std::vector<std::vector<int> > extractROIs(int total_masks, SelectiveSearchModel regions[], int numRounds, float spSize, - const std::vector<std::vector<int> > & sp, const cv::Mat & img, const cv::Mat & gray_mask, - int minRegionSize, int histSize, float hist_range_min, float hist_range_max); - } -} - - diff --git a/detectors/include/od/detectors/global2D/training/ActivationWindow.h b/detectors/include/od/detectors/global2D/training/ActivationWindow.h deleted file mode 100644 index c5284b9d..00000000 --- a/detectors/include/od/detectors/global2D/training/ActivationWindow.h +++ /dev/null @@ -1,458 +0,0 @@ -#include "od/detectors/global2D/training/Network.h" - -void NetworkCreator::showWindow_activationLayerType(Glib::ustring data) -{ - - remove(); - set_title("Activation Layer"); - set_border_width(10); - add(m_sw_activationLayerType); - m_grid_activationLayerType.set_column_spacing (10); - m_grid_activationLayerType.set_row_spacing (50); - - //level 0 - if(data == "") - title_activationLayerType.set_text("Set the Properties of Activation Layer type: AbsVal"); - else - title_activationLayerType.set_text("Set the Properties of Activation Layer type: " + data); - title_activationLayerType.set_line_wrap(); - title_activationLayerType.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_activationLayerType.attach(title_activationLayerType,0,0,2,1); - title_activationLayerType.show(); - - button_setActivationParameters.show(); - button_addMoreLayer.show(); - - //level next - if(data == "" or data == "AbsVal" or data == "PReLU" or data == "Sigmoid" or data == "TanH") - { -/* m_grid_activationLayerType.remove(label_activationLayerBottom); - m_grid_activationLayerType.remove(label_activationLayerName); - m_grid_activationLayerType.remove(label_activationLayerTop); - m_grid_activationLayerType.remove(label_activationLayerScale); - m_grid_activationLayerType.remove(label_activationLayerShift); - m_grid_activationLayerType.remove(label_activationLayerBase); - m_grid_activationLayerType.remove(label_activationLayerNegativeSlope); - m_grid_activationLayerType.remove(text_activationLayerBottom); - m_grid_activationLayerType.remove(text_activationLayerName); - m_grid_activationLayerType.remove(text_activationLayerTop); - m_grid_activationLayerType.remove(text_activationLayerScale); - m_grid_activationLayerType.remove(text_activationLayerShift); - m_grid_activationLayerType.remove(text_activationLayerBase); - m_grid_activationLayerType.remove(text_activationLayerNegativeSlope); -// m_grid_activationLayerType.remove(button_setActivationParameters); -// m_grid_activationLayerType.remove(button_addMoreLayer); -*/ - label_activationLayerBottom.hide(); - label_activationLayerName.hide(); - label_activationLayerTop.hide(); - label_activationLayerScale.hide(); - label_activationLayerShift.hide(); - label_activationLayerBase.hide(); - label_activationLayerNegativeSlope.hide(); - text_activationLayerBottom.hide(); - text_activationLayerName.hide(); - text_activationLayerTop.hide(); - text_activationLayerScale.hide(); - text_activationLayerShift.hide(); - text_activationLayerBase.hide(); - text_activationLayerNegativeSlope.hide(); - - label_activationLayerBottom.set_text("Bottom Layer Name: "); - label_activationLayerBottom.set_line_wrap(); - label_activationLayerBottom.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_activationLayerType.attach(label_activationLayerBottom,0,1,2,1); - label_activationLayerBottom.show(); - - text_activationLayerBottom.set_max_length(100); - text_activationLayerBottom.set_text(""); - text_activationLayerBottom.select_region(0, text_activationLayerBottom.get_text_length()); -// m_grid_activationLayerType.attach(text_activationLayerBottom,2,1,1,1); - text_activationLayerBottom.show(); - - label_activationLayerTop.set_text("Top Layer Name: "); - label_activationLayerTop.set_line_wrap(); - label_activationLayerTop.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_activationLayerType.attach(label_activationLayerTop,0,2,2,1); - label_activationLayerTop.show(); - - text_activationLayerTop.set_max_length(100); - text_activationLayerTop.set_text(""); - text_activationLayerTop.select_region(0, text_activationLayerTop.get_text_length()); -// m_grid_activationLayerType.attach(text_activationLayerTop,2,2,1,1); - text_activationLayerTop.show(); - - label_activationLayerName.set_text("Current Layer Name: "); - label_activationLayerName.set_line_wrap(); - label_activationLayerName.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_activationLayerType.attach(label_activationLayerName,0,3,2,1); - label_activationLayerName.show(); - - text_activationLayerName.set_max_length(100); - text_activationLayerName.set_text(""); - text_activationLayerName.select_region(0, text_activationLayerName.get_text_length()); -// m_grid_activationLayerType.attach(text_activationLayerName,2,3,1,1); - text_activationLayerName.show(); - - } - else if(data == "ReLU") - { -/* m_grid_activationLayerType.remove(label_activationLayerBottom); - m_grid_activationLayerType.remove(label_activationLayerName); - m_grid_activationLayerType.remove(label_activationLayerTop); - m_grid_activationLayerType.remove(label_activationLayerScale); - m_grid_activationLayerType.remove(label_activationLayerShift); - m_grid_activationLayerType.remove(label_activationLayerBase); - m_grid_activationLayerType.remove(label_activationLayerNegativeSlope); - m_grid_activationLayerType.remove(text_activationLayerBottom); - m_grid_activationLayerType.remove(text_activationLayerName); - m_grid_activationLayerType.remove(text_activationLayerTop); - m_grid_activationLayerType.remove(text_activationLayerScale); - m_grid_activationLayerType.remove(text_activationLayerShift); - m_grid_activationLayerType.remove(text_activationLayerBase); - m_grid_activationLayerType.remove(text_activationLayerNegativeSlope); -// m_grid_activationLayerType.remove(button_setActivationParameters); -// m_grid_activationLayerType.remove(button_addMoreLayer); -*/ - label_activationLayerBottom.hide(); - label_activationLayerName.hide(); - label_activationLayerTop.hide(); - label_activationLayerScale.hide(); - label_activationLayerShift.hide(); - label_activationLayerBase.hide(); - label_activationLayerNegativeSlope.hide(); - text_activationLayerBottom.hide(); - text_activationLayerName.hide(); - text_activationLayerTop.hide(); - text_activationLayerScale.hide(); - text_activationLayerShift.hide(); - text_activationLayerBase.hide(); - text_activationLayerNegativeSlope.hide(); - - label_activationLayerBottom.set_text("Bottom Layer Name: "); - label_activationLayerBottom.set_line_wrap(); - label_activationLayerBottom.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_activationLayerType.attach(label_activationLayerBottom,0,1,2,1); - label_activationLayerBottom.show(); - - text_activationLayerBottom.set_max_length(100); - text_activationLayerBottom.set_text(""); - text_activationLayerBottom.select_region(0, text_activationLayerBottom.get_text_length()); -// m_grid_activationLayerType.attach(text_activationLayerBottom,2,1,1,1); - text_activationLayerBottom.show(); - - label_activationLayerTop.set_text("Top Layer Name: "); - label_activationLayerTop.set_line_wrap(); - label_activationLayerTop.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_activationLayerType.attach(label_activationLayerTop,0,2,2,1); - label_activationLayerTop.show(); - - text_activationLayerTop.set_max_length(100); - text_activationLayerTop.set_text(""); - text_activationLayerTop.select_region(0, text_activationLayerTop.get_text_length()); -// m_grid_activationLayerType.attach(text_activationLayerTop,2,2,1,1); - text_activationLayerTop.show(); - - label_activationLayerName.set_text("Current Layer Name: "); - label_activationLayerName.set_line_wrap(); - label_activationLayerName.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_activationLayerType.attach(label_activationLayerName,0,3,2,1); - label_activationLayerName.show(); - - text_activationLayerName.set_max_length(100); - text_activationLayerName.set_text(""); - text_activationLayerName.select_region(0, text_activationLayerName.get_text_length()); -// m_grid_activationLayerType.attach(text_activationLayerName,2,3,1,1); - text_activationLayerName.show(); - - label_activationLayerNegativeSlope.set_text("Relu Param Negative Slope: "); - label_activationLayerNegativeSlope.set_line_wrap(); - label_activationLayerNegativeSlope.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_activationLayerType.attach(label_activationLayerNegativeSlope,0,4,2,1); - label_activationLayerNegativeSlope.show(); - - text_activationLayerNegativeSlope.set_max_length(100); - text_activationLayerNegativeSlope.set_text("0"); - text_activationLayerNegativeSlope.select_region(0, text_activationLayerNegativeSlope.get_text_length()); -// m_grid_activationLayerType.attach(text_activationLayerNegativeSlope,2,4,1,1); - text_activationLayerNegativeSlope.show(); -/* - button_setActivationParameters.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "setActivationParameters")); - m_grid_activationLayerType.attach(button_setActivationParameters,0,5,2,1); - button_setActivationParameters.show(); - - button_addMoreLayer.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "addMoreLayer")); - m_grid_activationLayerType.attach(button_addMoreLayer,2,5,1,1); - button_addMoreLayer.show(); -*/ } - else if(data == "Exp" or data == "Log") - { -/* m_grid_activationLayerType.remove(label_activationLayerBottom); - m_grid_activationLayerType.remove(label_activationLayerName); - m_grid_activationLayerType.remove(label_activationLayerTop); - m_grid_activationLayerType.remove(label_activationLayerScale); - m_grid_activationLayerType.remove(label_activationLayerShift); - m_grid_activationLayerType.remove(label_activationLayerBase); - m_grid_activationLayerType.remove(label_activationLayerNegativeSlope); - m_grid_activationLayerType.remove(text_activationLayerBottom); - m_grid_activationLayerType.remove(text_activationLayerName); - m_grid_activationLayerType.remove(text_activationLayerTop); - m_grid_activationLayerType.remove(text_activationLayerScale); - m_grid_activationLayerType.remove(text_activationLayerShift); - m_grid_activationLayerType.remove(text_activationLayerBase); - m_grid_activationLayerType.remove(text_activationLayerNegativeSlope); -// m_grid_activationLayerType.remove(button_setActivationParameters); -// m_grid_activationLayerType.remove(button_addMoreLayer); -*/ - label_activationLayerBottom.hide(); - label_activationLayerName.hide(); - label_activationLayerTop.hide(); - label_activationLayerScale.hide(); - label_activationLayerShift.hide(); - label_activationLayerBase.hide(); - label_activationLayerNegativeSlope.hide(); - text_activationLayerBottom.hide(); - text_activationLayerName.hide(); - text_activationLayerTop.hide(); - text_activationLayerScale.hide(); - text_activationLayerShift.hide(); - text_activationLayerBase.hide(); - text_activationLayerNegativeSlope.hide(); - - label_activationLayerBottom.set_text("Bottom Layer Name: "); - label_activationLayerBottom.set_line_wrap(); - label_activationLayerBottom.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_activationLayerType.attach(label_activationLayerBottom,0,1,2,1); - label_activationLayerBottom.show(); - - text_activationLayerBottom.set_max_length(100); - text_activationLayerBottom.set_text(""); - text_activationLayerBottom.select_region(0, text_activationLayerBottom.get_text_length()); -// m_grid_activationLayerType.attach(text_activationLayerBottom,2,1,1,1); - text_activationLayerBottom.show(); - - label_activationLayerTop.set_text("Top Layer Name: "); - label_activationLayerTop.set_line_wrap(); - label_activationLayerTop.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_activationLayerType.attach(label_activationLayerTop,0,2,2,1); - label_activationLayerTop.show(); - - text_activationLayerTop.set_max_length(100); - text_activationLayerTop.set_text(""); - text_activationLayerTop.select_region(0, text_activationLayerTop.get_text_length()); -// m_grid_activationLayerType.attach(text_activationLayerTop,2,2,1,1); - text_activationLayerTop.show(); - - label_activationLayerName.set_text("Current Layer Name: "); - label_activationLayerName.set_line_wrap(); - label_activationLayerName.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_activationLayerType.attach(label_activationLayerName,0,3,2,1); - label_activationLayerName.show(); - - text_activationLayerName.set_max_length(100); - text_activationLayerName.set_text(""); - text_activationLayerName.select_region(0, text_activationLayerName.get_text_length()); -// m_grid_activationLayerType.attach(text_activationLayerName,2,3,1,1); - text_activationLayerName.show(); - - label_activationLayerScale.set_text("Layer Parameter Scale: "); - label_activationLayerScale.set_line_wrap(); - label_activationLayerScale.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_activationLayerType.attach(label_activationLayerScale,0,4,2,1); - label_activationLayerScale.show(); - - text_activationLayerScale.set_max_length(100); - text_activationLayerScale.set_text("1"); - text_activationLayerScale.select_region(0, text_activationLayerScale.get_text_length()); -// m_grid_activationLayerType.attach(text_activationLayerScale,2,4,1,1); - text_activationLayerScale.show(); - - label_activationLayerShift.set_text("Layer Parameter Shift: "); - label_activationLayerShift.set_line_wrap(); - label_activationLayerShift.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_activationLayerType.attach(label_activationLayerShift,0,5,2,1); - label_activationLayerShift.show(); - - text_activationLayerShift.set_max_length(100); - text_activationLayerShift.set_text("0"); - text_activationLayerShift.select_region(0, text_activationLayerShift.get_text_length()); -// m_grid_activationLayerType.attach(text_activationLayerShift,2,5,1,1); - text_activationLayerShift.show(); - - label_activationLayerBase.set_text("Layer Parameter Base: "); - label_activationLayerBase.set_line_wrap(); - label_activationLayerBase.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_activationLayerType.attach(label_activationLayerBase,0,6,2,1); - label_activationLayerBase.show(); - - text_activationLayerBase.set_max_length(100); - text_activationLayerBase.set_text("-1"); - text_activationLayerBase.select_region(0, text_activationLayerBase.get_text_length()); -// m_grid_activationLayerType.attach(text_activationLayerBase,2,6,1,1); - text_activationLayerBase.show(); -/* - button_setActivationParameters.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "setActivationParameters")); - m_grid_activationLayerType.attach(button_setActivationParameters,0,7,2,1); - button_setActivationParameters.show(); - - button_addMoreLayer.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "addMoreLayer")); - m_grid_activationLayerType.attach(button_addMoreLayer,2,7,1,1); - button_addMoreLayer.show(); -*/ } - else if(data == "Power") - { -/* m_grid_activationLayerType.remove(label_activationLayerBottom); - m_grid_activationLayerType.remove(label_activationLayerName); - m_grid_activationLayerType.remove(label_activationLayerTop); - m_grid_activationLayerType.remove(label_activationLayerScale); - m_grid_activationLayerType.remove(label_activationLayerShift); - m_grid_activationLayerType.remove(label_activationLayerBase); - m_grid_activationLayerType.remove(label_activationLayerNegativeSlope); - m_grid_activationLayerType.remove(text_activationLayerBottom); - m_grid_activationLayerType.remove(text_activationLayerName); - m_grid_activationLayerType.remove(text_activationLayerTop); - m_grid_activationLayerType.remove(text_activationLayerScale); - m_grid_activationLayerType.remove(text_activationLayerShift); - m_grid_activationLayerType.remove(text_activationLayerBase); - m_grid_activationLayerType.remove(text_activationLayerNegativeSlope); -// m_grid_activationLayerType.remove(button_setActivationParameters); -// m_grid_activationLayerType.remove(button_addMoreLayer); -*/ - label_activationLayerBottom.hide(); - label_activationLayerName.hide(); - label_activationLayerTop.hide(); - label_activationLayerScale.hide(); - label_activationLayerShift.hide(); - label_activationLayerBase.hide(); - label_activationLayerNegativeSlope.hide(); - text_activationLayerBottom.hide(); - text_activationLayerName.hide(); - text_activationLayerTop.hide(); - text_activationLayerScale.hide(); - text_activationLayerShift.hide(); - text_activationLayerBase.hide(); - text_activationLayerNegativeSlope.hide(); - - label_activationLayerBottom.set_text("Bottom Layer Name: "); - label_activationLayerBottom.set_line_wrap(); - label_activationLayerBottom.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_activationLayerType.attach(label_activationLayerBottom,0,1,2,1); - label_activationLayerBottom.show(); - - text_activationLayerBottom.set_max_length(100); - text_activationLayerBottom.set_text(""); - text_activationLayerBottom.select_region(0, text_activationLayerBottom.get_text_length()); -// m_grid_activationLayerType.attach(text_activationLayerBottom,2,1,1,1); - text_activationLayerBottom.show(); - - label_activationLayerTop.set_text("Top Layer Name: "); - label_activationLayerTop.set_line_wrap(); - label_activationLayerTop.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_activationLayerType.attach(label_activationLayerTop,0,2,2,1); - label_activationLayerTop.show(); - - text_activationLayerTop.set_max_length(100); - text_activationLayerTop.set_text(""); - text_activationLayerTop.select_region(0, text_activationLayerTop.get_text_length()); -// m_grid_activationLayerType.attach(text_activationLayerTop,2,2,1,1); - text_activationLayerTop.show(); - - label_activationLayerName.set_text("Current Layer Name: "); - label_activationLayerName.set_line_wrap(); - label_activationLayerName.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_activationLayerType.attach(label_activationLayerName,0,3,2,1); - label_activationLayerName.show(); - - text_activationLayerName.set_max_length(100); - text_activationLayerName.set_text(""); - text_activationLayerName.select_region(0, text_activationLayerName.get_text_length()); -// m_grid_activationLayerType.attach(text_activationLayerName,2,3,1,1); - text_activationLayerName.show(); - - label_activationLayerScale.set_text("Layer Parameter Scale: "); - label_activationLayerScale.set_line_wrap(); - label_activationLayerScale.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_activationLayerType.attach(label_activationLayerScale,0,4,2,1); - label_activationLayerScale.show(); - - text_activationLayerScale.set_max_length(100); - text_activationLayerScale.set_text("1"); - text_activationLayerScale.select_region(0, text_activationLayerScale.get_text_length()); -// m_grid_activationLayerType.attach(text_activationLayerScale,2,4,1,1); - text_activationLayerScale.show(); - - label_activationLayerShift.set_text("Layer Parameter Shift: "); - label_activationLayerShift.set_line_wrap(); - label_activationLayerShift.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_activationLayerType.attach(label_activationLayerShift,0,5,2,1); - label_activationLayerShift.show(); - - text_activationLayerShift.set_max_length(100); - text_activationLayerShift.set_text("0"); - text_activationLayerShift.select_region(0, text_activationLayerShift.get_text_length()); -// m_grid_activationLayerType.attach(text_activationLayerShift,2,5,1,1); - text_activationLayerShift.show(); - - label_activationLayerBase.set_text("Layer Parameter Power: "); - label_activationLayerBase.set_line_wrap(); - label_activationLayerBase.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_activationLayerType.attach(label_activationLayerBase,0,6,2,1); - label_activationLayerBase.show(); - - text_activationLayerBase.set_max_length(100); - text_activationLayerBase.set_text("1"); - text_activationLayerBase.select_region(0, text_activationLayerBase.get_text_length()); -// m_grid_activationLayerType.attach(text_activationLayerBase,2,6,1,1); - text_activationLayerBase.show(); -/* - button_setActivationParameters.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "setActivationParameters")); - m_grid_activationLayerType.attach(button_setActivationParameters,0,7,2,1); - button_setActivationParameters.show(); - - button_addMoreLayer.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "addMoreLayer")); - m_grid_activationLayerType.attach(button_addMoreLayer,2,7,1,1); - button_addMoreLayer.show(); -*/ } -/* else - { - m_grid_activationLayerType.remove(label_activationLayerBottom); - m_grid_activationLayerType.remove(label_activationLayerName); - m_grid_activationLayerType.remove(label_activationLayerTop); - m_grid_activationLayerType.remove(label_activationLayerScale); - m_grid_activationLayerType.remove(label_activationLayerShift); - m_grid_activationLayerType.remove(label_activationLayerBase); - m_grid_activationLayerType.remove(label_activationLayerNegativeSlope); - m_grid_activationLayerType.remove(text_activationLayerBottom); - m_grid_activationLayerType.remove(text_activationLayerName); - m_grid_activationLayerType.remove(text_activationLayerTop); - m_grid_activationLayerType.remove(text_activationLayerScale); - m_grid_activationLayerType.remove(text_activationLayerShift); - m_grid_activationLayerType.remove(text_activationLayerBase); - m_grid_activationLayerType.remove(text_activationLayerNegativeSlope); - m_grid_activationLayerType.remove(button_setActivationParameters); - m_grid_activationLayerType.remove(button_addMoreLayer); - - - button_setActivationParameters.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "setActivationParameters")); - m_grid_activationLayerType.attach(button_setActivationParameters,0,1,7,1); - button_setActivationParameters.show(); - - button_addMoreLayer.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "addMoreLayer")); - m_grid_activationLayerType.attach(button_addMoreLayer,2,1,7,1); - button_addMoreLayer.show(); - } -*/ -// m_sw_activationLayerType.add(m_grid_activationLayerType); - m_sw_activationLayerType.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); - m_grid_activationLayerType.show(); -// show_all_children(); - m_sw_activationLayerType.show(); -} diff --git a/detectors/include/od/detectors/global2D/training/ConvTrainer.h b/detectors/include/od/detectors/global2D/training/ConvTrainer.h deleted file mode 100644 index 02894858..00000000 --- a/detectors/include/od/detectors/global2D/training/ConvTrainer.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once -#include "od/common/pipeline/Trainer.h" -#include "od/common/utils/Utils.h" -#include "od/detectors/global2D/training/Solver.h" -#include "od/detectors/global2D/training/Network.h" -#include <opencv2/opencv.hpp> - -#include <cstdlib> -#include <vector> -#include <fstream> -#include <string> -#include <iostream> -#include <stdio.h> -#include <caffe/caffe.hpp> -#include <caffe/util/io.hpp> -#include <caffe/blob.hpp> -#include <caffe/solver.hpp> -#include <caffe/sgd_solvers.hpp> - -namespace od -{ - namespace g2d - { - class ConvTrainer : public Trainer - { - public: - ConvTrainer(const std::string & training_input_location_ = std::string(""), - const std::string & trained_data_location_ = std::string("")); - int train(); - void init(){} - void setSolverLocation(const std::string & location); - void setSolverProperties(int argc, char *argv[]); - void startTraining(); - void createCustomNetwork(int argc, char *argv[]); - - private: - std::string solverLocation; - }; - - } -} - diff --git a/detectors/include/od/detectors/global2D/training/CriticalWindow.h b/detectors/include/od/detectors/global2D/training/CriticalWindow.h deleted file mode 100644 index 6e713e6c..00000000 --- a/detectors/include/od/detectors/global2D/training/CriticalWindow.h +++ /dev/null @@ -1,1240 +0,0 @@ -#include "od/detectors/global2D/training/Network.h" - -void NetworkCreator::showWindow_criticalLayerType(Glib::ustring data) -{ - - remove(); - set_title("Critical Layer"); - set_border_width(10); - add(m_sw_criticalLayerType); - m_grid_criticalLayerType.set_column_spacing (10); - m_grid_criticalLayerType.set_row_spacing (50); - - //level 0 - if(data == "") - title_criticalLayerType.set_text("Set the Properties of Critical Layer type: Accuracy"); - else - title_criticalLayerType.set_text("Set the Properties of Critical Layer type: " + data); - title_criticalLayerType.set_line_wrap(); - title_criticalLayerType.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(title_criticalLayerType,0,0,2,1); - title_criticalLayerType.show(); - - button_setCriticalParameters.show(); - button_addMoreLayer2.show(); - - if(data == "" or data == "Accuracy" or data == "Softmax") - { - label_criticalLayerBottom1.hide(); - text_criticalLayerBottom1.hide(); - label_criticalLayerBottom2.hide(); - text_criticalLayerBottom2.hide(); - label_criticalLayerTop.hide(); - text_criticalLayerTop.hide(); - label_criticalLayerName.hide(); - text_criticalLayerName.hide(); - label_criticalLayerFilterLr.hide(); - text_criticalLayerFilterLr.hide(); - label_criticalLayerFilterDm.hide(); - text_criticalLayerFilterDm.hide(); - label_criticalLayerBiasLr.hide(); - text_criticalLayerBiasLr.hide(); - label_criticalLayerBiasDm.hide(); - text_criticalLayerBiasDm.hide(); - label_criticalLayerNumOutput.hide(); - text_criticalLayerNumOutput.hide(); - label_criticalLayerKernelW.hide(); - text_criticalLayerKernelW.hide(); - label_criticalLayerKernelH.hide(); - text_criticalLayerKernelH.hide(); - label_criticalLayerStrideW.hide(); - text_criticalLayerStrideW.hide(); - label_criticalLayerStrideH.hide(); - text_criticalLayerStrideH.hide(); - label_criticalLayerPadW.hide(); - text_criticalLayerPadW.hide(); - label_criticalLayerPadH.hide(); - text_criticalLayerPadH.hide(); - label_criticalLayerWeightFiller.hide(); - rbutton_criticalLayerWeightFillerConstant.hide(); - rbutton_criticalLayerWeightFillerUniform.hide(); - rbutton_criticalLayerWeightGaussian.hide(); - rbutton_criticalLayerWeightFillerPositiveUnitBall.hide(); - rbutton_criticalLayerWeightFillerXavier.hide(); - rbutton_criticalLayerWeightFillerMSRA.hide(); - rbutton_criticalLayerWeightFillerBilinear.hide(); - label_criticalLayerWeightFillerConstantValue.hide(); - text_criticalLayerWeightFillerConstantValue.hide(); - label_criticalLayerWeightFillerUniformMin.hide(); - label_criticalLayerWeightFillerUniformMax.hide(); - text_criticalLayerWeightFillerUniformMin.hide(); - text_criticalLayerWeightFillerUniformMax.hide(); - label_criticalLayerWeightFillerGaussianMean.hide(); - text_criticalLayerWeightFillerGaussianMean.hide(); - label_criticalLayerWeightFillerGaussianStd.hide(); - text_criticalLayerWeightFillerGaussianStd.hide(); - label_criticalLayerWeightFillerXavierVariance.hide(); - rbutton_criticalLayerWeightFillerXavierIn.hide(); - rbutton_criticalLayerWeightFillerXavierOut.hide(); - rbutton_criticalLayerWeightFillerXavierAvg.hide(); - label_criticalLayerWeightFillerMSRAVariance.hide(); - rbutton_criticalLayerWeightFillerMSRAIn.hide(); - rbutton_criticalLayerWeightFillerMSRAOut.hide(); - rbutton_criticalLayerWeightFillerMSRAAvg.hide(); - label_criticalLayerDropoutRatio.hide(); - text_criticalLayerDropoutRatio.hide(); - label_criticalLayerPool.hide(); - label_criticalLayerBias.hide(); - text_criticalLayerBias.hide(); - rbutton_criticalLayerPoolMax.hide(); - rbutton_criticalLayerPoolAve.hide(); - - - label_criticalLayerBottom1.set_text("Bottom1 Layer Name: "); - label_criticalLayerBottom1.set_line_wrap(); - label_criticalLayerBottom1.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerBottom1,0,1,2,1); - label_criticalLayerBottom1.show(); - - text_criticalLayerBottom1.set_max_length(100); - text_criticalLayerBottom1.set_text(""); - text_criticalLayerBottom1.select_region(0, text_criticalLayerBottom1.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerBottom1,2,1,1,1); - text_criticalLayerBottom1.show(); - - label_criticalLayerBottom2.set_text("Bottom2 Layer Name: "); - label_criticalLayerBottom2.set_line_wrap(); - label_criticalLayerBottom2.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerBottom2,0,1,2,1); - label_criticalLayerBottom2.show(); - - text_criticalLayerBottom2.set_max_length(100); - text_criticalLayerBottom2.set_text(""); - text_criticalLayerBottom2.select_region(0, text_criticalLayerBottom2.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerBottom2,2,1,1,1); - text_criticalLayerBottom2.show(); - - label_criticalLayerTop.set_text("Top Layer Name: "); - label_criticalLayerTop.set_line_wrap(); - label_criticalLayerTop.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerTop,0,3,2,1); - label_criticalLayerTop.show(); - - text_criticalLayerTop.set_max_length(100); - text_criticalLayerTop.set_text(""); - text_criticalLayerTop.select_region(0, text_criticalLayerTop.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerTop,2,3,1,1); - text_criticalLayerTop.show(); - - label_criticalLayerName.set_text("Current Layer Name: "); - label_criticalLayerName.set_line_wrap(); - label_criticalLayerName.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerName,0,4,2,1); - label_criticalLayerName.show(); - - text_criticalLayerName.set_max_length(100); - text_criticalLayerName.set_text(""); - text_criticalLayerName.select_region(0, text_criticalLayerName.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerName,2,4,1,1); - text_criticalLayerName.show(); - } - else if( data == "Concat") - { - label_criticalLayerBottom1.hide(); - text_criticalLayerBottom1.hide(); - label_criticalLayerBottom2.hide(); - text_criticalLayerBottom2.hide(); - label_criticalLayerTop.hide(); - text_criticalLayerTop.hide(); - label_criticalLayerName.hide(); - text_criticalLayerName.hide(); - label_criticalLayerFilterLr.hide(); - text_criticalLayerFilterLr.hide(); - label_criticalLayerFilterDm.hide(); - text_criticalLayerFilterDm.hide(); - label_criticalLayerBiasLr.hide(); - text_criticalLayerBiasLr.hide(); - label_criticalLayerBiasDm.hide(); - text_criticalLayerBiasDm.hide(); - label_criticalLayerNumOutput.hide(); - text_criticalLayerNumOutput.hide(); - label_criticalLayerKernelW.hide(); - text_criticalLayerKernelW.hide(); - label_criticalLayerKernelH.hide(); - text_criticalLayerKernelH.hide(); - label_criticalLayerStrideW.hide(); - text_criticalLayerStrideW.hide(); - label_criticalLayerStrideH.hide(); - text_criticalLayerStrideH.hide(); - label_criticalLayerPadW.hide(); - text_criticalLayerPadW.hide(); - label_criticalLayerPadH.hide(); - text_criticalLayerPadH.hide(); - label_criticalLayerWeightFiller.hide(); - rbutton_criticalLayerWeightFillerConstant.hide(); - rbutton_criticalLayerWeightFillerUniform.hide(); - rbutton_criticalLayerWeightGaussian.hide(); - rbutton_criticalLayerWeightFillerPositiveUnitBall.hide(); - rbutton_criticalLayerWeightFillerXavier.hide(); - rbutton_criticalLayerWeightFillerMSRA.hide(); - rbutton_criticalLayerWeightFillerBilinear.hide(); - label_criticalLayerWeightFillerConstantValue.hide(); - text_criticalLayerWeightFillerConstantValue.hide(); - label_criticalLayerWeightFillerUniformMin.hide(); - label_criticalLayerWeightFillerUniformMax.hide(); - text_criticalLayerWeightFillerUniformMin.hide(); - text_criticalLayerWeightFillerUniformMax.hide(); - label_criticalLayerWeightFillerGaussianMean.hide(); - text_criticalLayerWeightFillerGaussianMean.hide(); - label_criticalLayerWeightFillerGaussianStd.hide(); - text_criticalLayerWeightFillerGaussianStd.hide(); - label_criticalLayerWeightFillerXavierVariance.hide(); - rbutton_criticalLayerWeightFillerXavierIn.hide(); - rbutton_criticalLayerWeightFillerXavierOut.hide(); - rbutton_criticalLayerWeightFillerXavierAvg.hide(); - label_criticalLayerWeightFillerMSRAVariance.hide(); - rbutton_criticalLayerWeightFillerMSRAIn.hide(); - rbutton_criticalLayerWeightFillerMSRAOut.hide(); - rbutton_criticalLayerWeightFillerMSRAAvg.hide();\ - label_criticalLayerDropoutRatio.hide(); - text_criticalLayerDropoutRatio.hide(); - label_criticalLayerPool.hide(); - label_criticalLayerBias.hide(); - text_criticalLayerBias.hide(); - rbutton_criticalLayerPoolMax.hide(); - rbutton_criticalLayerPoolAve.hide(); - - - label_criticalLayerBottom1.set_text("Facility will be added soon"); - label_criticalLayerBottom1.set_line_wrap(); - label_criticalLayerBottom1.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerBottom1,0,1,2,1); - label_criticalLayerBottom1.show(); - } - else if( data == "Convolution" or data == "Deconvolution") - { - label_criticalLayerBottom1.hide(); - text_criticalLayerBottom1.hide(); - label_criticalLayerBottom2.hide(); - text_criticalLayerBottom2.hide(); - label_criticalLayerTop.hide(); - text_criticalLayerTop.hide(); - label_criticalLayerName.hide(); - text_criticalLayerName.hide(); - label_criticalLayerFilterLr.hide(); - text_criticalLayerFilterLr.hide(); - label_criticalLayerFilterDm.hide(); - text_criticalLayerFilterDm.hide(); - label_criticalLayerBiasLr.hide(); - text_criticalLayerBiasLr.hide(); - label_criticalLayerBiasDm.hide(); - text_criticalLayerBiasDm.hide(); - label_criticalLayerNumOutput.hide(); - text_criticalLayerNumOutput.hide(); - label_criticalLayerKernelW.hide(); - text_criticalLayerKernelW.hide(); - label_criticalLayerKernelH.hide(); - text_criticalLayerKernelH.hide(); - label_criticalLayerStrideW.hide(); - text_criticalLayerStrideW.hide(); - label_criticalLayerStrideH.hide(); - text_criticalLayerStrideH.hide(); - label_criticalLayerPadW.hide(); - text_criticalLayerPadW.hide(); - label_criticalLayerPadH.hide(); - text_criticalLayerPadH.hide(); - label_criticalLayerWeightFiller.hide(); - rbutton_criticalLayerWeightFillerConstant.hide(); - rbutton_criticalLayerWeightFillerUniform.hide(); - rbutton_criticalLayerWeightGaussian.hide(); - rbutton_criticalLayerWeightFillerPositiveUnitBall.hide(); - rbutton_criticalLayerWeightFillerXavier.hide(); - rbutton_criticalLayerWeightFillerMSRA.hide(); - rbutton_criticalLayerWeightFillerBilinear.hide(); - label_criticalLayerWeightFillerConstantValue.hide(); - text_criticalLayerWeightFillerConstantValue.hide(); - label_criticalLayerWeightFillerUniformMin.hide(); - label_criticalLayerWeightFillerUniformMax.hide(); - text_criticalLayerWeightFillerUniformMin.hide(); - text_criticalLayerWeightFillerUniformMax.hide(); - label_criticalLayerWeightFillerGaussianMean.hide(); - text_criticalLayerWeightFillerGaussianMean.hide(); - label_criticalLayerWeightFillerGaussianStd.hide(); - text_criticalLayerWeightFillerGaussianStd.hide(); - label_criticalLayerWeightFillerXavierVariance.hide(); - rbutton_criticalLayerWeightFillerXavierIn.hide(); - rbutton_criticalLayerWeightFillerXavierOut.hide(); - rbutton_criticalLayerWeightFillerXavierAvg.hide(); - label_criticalLayerWeightFillerMSRAVariance.hide(); - rbutton_criticalLayerWeightFillerMSRAIn.hide(); - rbutton_criticalLayerWeightFillerMSRAOut.hide(); - rbutton_criticalLayerWeightFillerMSRAAvg.hide(); - label_criticalLayerDropoutRatio.hide(); - text_criticalLayerDropoutRatio.hide(); - label_criticalLayerPool.hide(); - label_criticalLayerBias.hide(); - text_criticalLayerBias.hide(); - rbutton_criticalLayerPoolMax.hide(); - rbutton_criticalLayerPoolAve.hide(); - - label_criticalLayerBottom1.set_text("Bottom1 Layer Name: "); - label_criticalLayerBottom1.set_line_wrap(); - label_criticalLayerBottom1.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerBottom1,0,1,2,1); - label_criticalLayerBottom1.show(); - - text_criticalLayerBottom1.set_max_length(100); - text_criticalLayerBottom1.set_text(""); - text_criticalLayerBottom1.select_region(0, text_criticalLayerBottom1.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerBottom1,2,1,1,1); - text_criticalLayerBottom1.show(); - - label_criticalLayerTop.set_text("Top Layer Name: "); - label_criticalLayerTop.set_line_wrap(); - label_criticalLayerTop.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerTop,0,3,2,1); - label_criticalLayerTop.show(); - - text_criticalLayerTop.set_max_length(100); - text_criticalLayerTop.set_text(""); - text_criticalLayerTop.select_region(0, text_criticalLayerTop.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerTop,2,3,1,1); - text_criticalLayerTop.show(); - - label_criticalLayerName.set_text("Current Layer Name: "); - label_criticalLayerName.set_line_wrap(); - label_criticalLayerName.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerName,0,4,2,1); - label_criticalLayerName.show(); - - text_criticalLayerName.set_max_length(100); - text_criticalLayerName.set_text(""); - text_criticalLayerName.select_region(0, text_criticalLayerName.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerName,2,4,1,1); - text_criticalLayerName.show(); - - label_criticalLayerFilterLr.set_text("Set filter lr_mult:\n(Leave unchanged if not needed) "); - label_criticalLayerFilterLr.set_line_wrap(); - label_criticalLayerFilterLr.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerFilterLr,0,5,2,1); - label_criticalLayerFilterLr.show(); - - text_criticalLayerFilterLr.set_max_length(100); - text_criticalLayerFilterLr.set_text("1"); - text_criticalLayerFilterLr.select_region(0, text_criticalLayerFilterLr.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerFilterLr,2,5,1,1); - text_criticalLayerFilterLr.show(); - - label_criticalLayerFilterDm.set_text("Set filter decay_mult:\n(Leave unchanged if not needed) "); - label_criticalLayerFilterDm.set_line_wrap(); - label_criticalLayerFilterDm.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerFilterDm,0,6,2,1); - label_criticalLayerFilterDm.show(); - - text_criticalLayerFilterDm.set_max_length(100); - text_criticalLayerFilterDm.set_text("1"); - text_criticalLayerFilterDm.select_region(0, text_criticalLayerFilterDm.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerFilterDm,2,6,1,1); - text_criticalLayerFilterDm.show(); - - label_criticalLayerBiasLr.set_text("Set bias lr_mult:\n(Leave unchanged if not needed) "); - label_criticalLayerBiasLr.set_line_wrap(); - label_criticalLayerBiasLr.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerBiasLr,0,7,2,1); - label_criticalLayerBiasLr.show(); - - text_criticalLayerBiasLr.set_max_length(100); - text_criticalLayerBiasLr.set_text("2"); - text_criticalLayerBiasLr.select_region(0, text_criticalLayerBiasLr.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerBiasLr,2,7,1,1); - text_criticalLayerBiasLr.show(); - - label_criticalLayerBiasDm.set_text("Set bias decay_mult:\n(Leave unchanged if not needed) "); - label_criticalLayerBiasDm.set_line_wrap(); - label_criticalLayerBiasDm.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerBiasDm,0,8,2,1); - label_criticalLayerBiasDm.show(); - - text_criticalLayerBiasDm.set_max_length(100); - text_criticalLayerBiasDm.set_text("0"); - text_criticalLayerBiasDm.select_region(0, text_criticalLayerBiasDm.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerBiasDm,2,8,1,1); - text_criticalLayerBiasDm.show(); - - label_criticalLayerNumOutput.set_text("Set param num_output: "); - label_criticalLayerNumOutput.set_line_wrap(); - label_criticalLayerNumOutput.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerNumOutput,0,9,2,1); - label_criticalLayerNumOutput.show(); - - text_criticalLayerNumOutput.set_max_length(100); - text_criticalLayerNumOutput.set_text("64"); - text_criticalLayerNumOutput.select_region(0, text_criticalLayerNumOutput.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerNumOutput,2,9,1,1); - text_criticalLayerNumOutput.show(); - - label_criticalLayerKernelW.set_text("Set param kernel_w: "); - label_criticalLayerKernelW.set_line_wrap(); - label_criticalLayerKernelW.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerKernelW,0,10,2,1); - label_criticalLayerKernelW.show(); - - text_criticalLayerKernelW.set_max_length(100); - text_criticalLayerKernelW.set_text("3"); - text_criticalLayerKernelW.select_region(0, text_criticalLayerKernelW.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerKernelW,2,10,1,1); - text_criticalLayerKernelW.show(); - - label_criticalLayerKernelH.set_text("Set param kernel_h: "); - label_criticalLayerKernelH.set_line_wrap(); - label_criticalLayerKernelH.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerKernelH,0,11,2,1); - label_criticalLayerKernelH.show(); - - text_criticalLayerKernelH.set_max_length(100); - text_criticalLayerKernelH.set_text("3"); - text_criticalLayerKernelH.select_region(0, text_criticalLayerKernelH.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerKernelH,2,11,1,1); - text_criticalLayerKernelH.show(); - - label_criticalLayerStrideW.set_text("Set param stride_w: "); - label_criticalLayerStrideW.set_line_wrap(); - label_criticalLayerStrideW.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerStrideW,0,12,2,1); - label_criticalLayerStrideW.show(); - - text_criticalLayerStrideW.set_max_length(100); - text_criticalLayerStrideW.set_text("1"); - text_criticalLayerStrideW.select_region(0, text_criticalLayerStrideW.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerStrideW,2,12,1,1); - text_criticalLayerStrideW.show(); - - label_criticalLayerStrideH.set_text("Set param stride_h: "); - label_criticalLayerStrideH.set_line_wrap(); - label_criticalLayerStrideH.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerStrideH,0,13,2,1); - label_criticalLayerStrideH.show(); - - text_criticalLayerStrideH.set_max_length(100); - text_criticalLayerStrideH.set_text("1"); - text_criticalLayerStrideH.select_region(0, text_criticalLayerStrideH.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerStrideH,2,13,1,1); - text_criticalLayerStrideH.show(); - - label_criticalLayerPadW.set_text("Set param pad_w: "); - label_criticalLayerPadW.set_line_wrap(); - label_criticalLayerPadW.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerPadW,0,14,2,1); - label_criticalLayerPadW.show(); - - text_criticalLayerPadW.set_max_length(100); - text_criticalLayerPadW.set_text("1"); - text_criticalLayerPadW.select_region(0, text_criticalLayerPadW.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerPadW,2,14,1,1); - text_criticalLayerPadW.show(); - - label_criticalLayerPadH.set_text("Set param pad_h: "); - label_criticalLayerPadH.set_line_wrap(); - label_criticalLayerPadH.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerPadH,0,14,2,1); - label_criticalLayerPadH.show(); - - text_criticalLayerPadH.set_max_length(100); - text_criticalLayerPadH.set_text("1"); - text_criticalLayerPadH.select_region(0, text_criticalLayerPadH.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerPadH,2,14,1,1); - text_criticalLayerPadH.show(); - - label_criticalLayerWeightFiller.set_text("Set Weight Filler : "); - label_criticalLayerWeightFiller.set_line_wrap(); - label_criticalLayerWeightFiller.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerWeightFiller,0,16,2,1); - label_criticalLayerWeightFiller.show(); - -// Gtk::RadioButton::Group group1 = rbutton_criticalLayerWeightFillerConstant.get_group(); -// rbutton_criticalLayerWeightFillerUniform.set_group(group1); -// rbutton_criticalLayerWeightGaussian.set_group(group1); -// rbutton_criticalLayerWeightFillerPositiveUnitBall.set_group(group1); -// rbutton_criticalLayerWeightFillerXavier.set_group(group1); -// rbutton_criticalLayerWeightFillerMSRA.set_group(group1); -// rbutton_criticalLayerWeightFillerBilinear.set_group(group1); -// rbutton_criticalLayerWeightFillerConstant.set_active(); -// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerConstant,2,17,1,1); - rbutton_criticalLayerWeightFillerConstant.show(); -// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerUniform,2,18,1,1); - rbutton_criticalLayerWeightFillerUniform.show(); -// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightGaussian,2,19,1,1); - rbutton_criticalLayerWeightGaussian.show(); -// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerPositiveUnitBall,2,20,1,1); - rbutton_criticalLayerWeightFillerPositiveUnitBall.show(); -// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavier,2,21,1,1); - rbutton_criticalLayerWeightFillerXavier.show(); -// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerMSRA,2,22,1,1); - rbutton_criticalLayerWeightFillerMSRA.show(); -// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerBilinear,2,23,1,1); - rbutton_criticalLayerWeightFillerBilinear.show(); - - label_criticalLayerWeightFillerConstantValue.set_text("value: "); - label_criticalLayerWeightFillerConstantValue.set_line_wrap(); - label_criticalLayerWeightFillerConstantValue.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerConstantValue,4,17,1,1); - label_criticalLayerWeightFillerConstantValue.show(); - - text_criticalLayerWeightFillerConstantValue.set_max_length(100); - text_criticalLayerWeightFillerConstantValue.set_text("0.5"); - text_criticalLayerWeightFillerConstantValue.select_region(0, text_criticalLayerWeightFillerConstantValue.get_text_length()); -// m_grid_criticalLayerWeightFillerConstantValue.attach(text_criticalLayerWeightFillerConstantValue,5,15,1,1); - text_criticalLayerWeightFillerConstantValue.show(); - - label_criticalLayerWeightFillerUniformMin.set_text("min: "); - label_criticalLayerWeightFillerUniformMin.set_line_wrap(); - label_criticalLayerWeightFillerUniformMin.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerUniformMin,4,18,1,1); - label_criticalLayerWeightFillerUniformMin.show(); - - text_criticalLayerWeightFillerUniformMin.set_max_length(100); - text_criticalLayerWeightFillerUniformMin.set_text("0"); - text_criticalLayerWeightFillerUniformMin.select_region(0, text_criticalLayerWeightFillerUniformMin.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerWeightFillerUniformMin,5,18,1,1); - text_criticalLayerWeightFillerUniformMin.show(); - - label_criticalLayerWeightFillerUniformMax.set_text("max: "); - label_criticalLayerWeightFillerUniformMax.set_line_wrap(); - label_criticalLayerWeightFillerUniformMax.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerUniformMax,4,19,1,1); - label_criticalLayerWeightFillerUniformMax.show(); - - text_criticalLayerWeightFillerUniformMax.set_max_length(100); - text_criticalLayerWeightFillerUniformMax.set_text("1"); - text_criticalLayerWeightFillerUniformMax.select_region(0, text_criticalLayerWeightFillerUniformMax.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerWeightFillerUniformMax,5,19,1,1); - text_criticalLayerWeightFillerUniformMax.show(); - - label_criticalLayerWeightFillerGaussianMean.set_text("mean: "); - label_criticalLayerWeightFillerGaussianMean.set_line_wrap(); - label_criticalLayerWeightFillerGaussianMean.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerGaussianMean,4,19,1,1); - label_criticalLayerWeightFillerGaussianMean.show(); - - text_criticalLayerWeightFillerGaussianMean.set_max_length(100); - text_criticalLayerWeightFillerGaussianMean.set_text("0"); - text_criticalLayerWeightFillerGaussianMean.select_region(0, text_criticalLayerWeightFillerGaussianMean.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerWeightFillerGaussianMean,5,19,1,1); - text_criticalLayerWeightFillerGaussianMean.show(); - - label_criticalLayerWeightFillerGaussianStd.set_text("std: "); - label_criticalLayerWeightFillerGaussianStd.set_line_wrap(); - label_criticalLayerWeightFillerGaussianStd.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerGaussianStd,6,19,1,1); - label_criticalLayerWeightFillerGaussianStd.show(); - - text_criticalLayerWeightFillerGaussianStd.set_max_length(100); - text_criticalLayerWeightFillerGaussianStd.set_text("0.1"); - text_criticalLayerWeightFillerGaussianStd.select_region(0, text_criticalLayerWeightFillerGaussianStd.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerWeightFillerGaussianStd,7,19,1,1); - text_criticalLayerWeightFillerGaussianStd.show(); - - label_criticalLayerWeightFillerXavierVariance.set_text("variance_norm: "); - label_criticalLayerWeightFillerXavierVariance.set_line_wrap(); - label_criticalLayerWeightFillerXavierVariance.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerXavierVariance,4,20,1,1); - label_criticalLayerWeightFillerXavierVariance.show(); - -// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavierIn,5,20,1,1); - rbutton_criticalLayerWeightFillerXavierIn.show(); -// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavierOut,6,20,1,1); - rbutton_criticalLayerWeightFillerXavierOut.show(); -// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavierAvg,7,20,1,1); - rbutton_criticalLayerWeightFillerXavierAvg.show(); - - label_criticalLayerWeightFillerMSRAVariance.set_text("variance_norm: "); - label_criticalLayerWeightFillerMSRAVariance.set_line_wrap(); - label_criticalLayerWeightFillerMSRAVariance.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerMSRAVariance,4,20,1,1); - label_criticalLayerWeightFillerMSRAVariance.show(); - -// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavierIn,5,20,1,1); - rbutton_criticalLayerWeightFillerMSRAIn.show(); -// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavierOut,6,20,1,1); - rbutton_criticalLayerWeightFillerMSRAOut.show(); -// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavierAvg,7,20,1,1); - rbutton_criticalLayerWeightFillerMSRAAvg.show(); - - label_criticalLayerBias.set_text("Set bias value: "); - label_criticalLayerBias.set_line_wrap(); - label_criticalLayerBias.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerBias,0,24,2,1); - label_criticalLayerBias.show(); - - text_criticalLayerBias.set_max_length(100); - text_criticalLayerBias.set_text("0.1"); - text_criticalLayerBias.select_region(0, text_criticalLayerBias.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerBias,2,24,1,1); - text_criticalLayerBias.show(); - } - else if(data == "InnerProduct") - { - label_criticalLayerBottom1.hide(); - text_criticalLayerBottom1.hide(); - label_criticalLayerBottom2.hide(); - text_criticalLayerBottom2.hide(); - label_criticalLayerTop.hide(); - text_criticalLayerTop.hide(); - label_criticalLayerName.hide(); - text_criticalLayerName.hide(); - label_criticalLayerFilterLr.hide(); - text_criticalLayerFilterLr.hide(); - label_criticalLayerFilterDm.hide(); - text_criticalLayerFilterDm.hide(); - label_criticalLayerBiasLr.hide(); - text_criticalLayerBiasLr.hide(); - label_criticalLayerBiasDm.hide(); - text_criticalLayerBiasDm.hide(); - label_criticalLayerNumOutput.hide(); - text_criticalLayerNumOutput.hide(); - label_criticalLayerKernelW.hide(); - text_criticalLayerKernelW.hide(); - label_criticalLayerKernelH.hide(); - text_criticalLayerKernelH.hide(); - label_criticalLayerStrideW.hide(); - text_criticalLayerStrideW.hide(); - label_criticalLayerStrideH.hide(); - text_criticalLayerStrideH.hide(); - label_criticalLayerPadW.hide(); - text_criticalLayerPadW.hide(); - label_criticalLayerPadH.hide(); - text_criticalLayerPadH.hide(); - label_criticalLayerWeightFiller.hide(); - rbutton_criticalLayerWeightFillerConstant.hide(); - rbutton_criticalLayerWeightFillerUniform.hide(); - rbutton_criticalLayerWeightGaussian.hide(); - rbutton_criticalLayerWeightFillerPositiveUnitBall.hide(); - rbutton_criticalLayerWeightFillerXavier.hide(); - rbutton_criticalLayerWeightFillerMSRA.hide(); - rbutton_criticalLayerWeightFillerBilinear.hide(); - label_criticalLayerWeightFillerConstantValue.hide(); - text_criticalLayerWeightFillerConstantValue.hide(); - label_criticalLayerWeightFillerUniformMin.hide(); - label_criticalLayerWeightFillerUniformMax.hide(); - text_criticalLayerWeightFillerUniformMin.hide(); - text_criticalLayerWeightFillerUniformMax.hide(); - label_criticalLayerWeightFillerGaussianMean.hide(); - text_criticalLayerWeightFillerGaussianMean.hide(); - label_criticalLayerWeightFillerGaussianStd.hide(); - text_criticalLayerWeightFillerGaussianStd.hide(); - label_criticalLayerWeightFillerXavierVariance.hide(); - rbutton_criticalLayerWeightFillerXavierIn.hide(); - rbutton_criticalLayerWeightFillerXavierOut.hide(); - rbutton_criticalLayerWeightFillerXavierAvg.hide(); - label_criticalLayerWeightFillerMSRAVariance.hide(); - rbutton_criticalLayerWeightFillerMSRAIn.hide(); - rbutton_criticalLayerWeightFillerMSRAOut.hide(); - rbutton_criticalLayerWeightFillerMSRAAvg.hide(); - label_criticalLayerDropoutRatio.hide(); - text_criticalLayerDropoutRatio.hide(); - label_criticalLayerPool.hide(); - label_criticalLayerBias.hide(); - text_criticalLayerBias.hide(); - rbutton_criticalLayerPoolMax.hide(); - rbutton_criticalLayerPoolAve.hide(); - - label_criticalLayerBottom1.set_text("Bottom1 Layer Name: "); - label_criticalLayerBottom1.set_line_wrap(); - label_criticalLayerBottom1.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerBottom1,0,1,2,1); - label_criticalLayerBottom1.show(); - - text_criticalLayerBottom1.set_max_length(100); - text_criticalLayerBottom1.set_text(""); - text_criticalLayerBottom1.select_region(0, text_criticalLayerBottom1.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerBottom1,2,1,1,1); - text_criticalLayerBottom1.show(); - - label_criticalLayerTop.set_text("Top Layer Name: "); - label_criticalLayerTop.set_line_wrap(); - label_criticalLayerTop.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerTop,0,3,2,1); - label_criticalLayerTop.show(); - - text_criticalLayerTop.set_max_length(100); - text_criticalLayerTop.set_text(""); - text_criticalLayerTop.select_region(0, text_criticalLayerTop.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerTop,2,3,1,1); - text_criticalLayerTop.show(); - - label_criticalLayerName.set_text("Current Layer Name: "); - label_criticalLayerName.set_line_wrap(); - label_criticalLayerName.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerName,0,4,2,1); - label_criticalLayerName.show(); - - text_criticalLayerName.set_max_length(100); - text_criticalLayerName.set_text(""); - text_criticalLayerName.select_region(0, text_criticalLayerName.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerName,2,4,1,1); - text_criticalLayerName.show(); - - label_criticalLayerFilterLr.set_text("Set filter lr_mult:\n(Leave unchanged if not needed) "); - label_criticalLayerFilterLr.set_line_wrap(); - label_criticalLayerFilterLr.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerFilterLr,0,5,2,1); - label_criticalLayerFilterLr.show(); - - text_criticalLayerFilterLr.set_max_length(100); - text_criticalLayerFilterLr.set_text("1"); - text_criticalLayerFilterLr.select_region(0, text_criticalLayerFilterLr.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerFilterLr,2,5,1,1); - text_criticalLayerFilterLr.show(); - - label_criticalLayerFilterDm.set_text("Set filter decay_mult:\n(Leave unchanged if not needed) "); - label_criticalLayerFilterDm.set_line_wrap(); - label_criticalLayerFilterDm.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerFilterDm,0,6,2,1); - label_criticalLayerFilterDm.show(); - - text_criticalLayerFilterDm.set_max_length(100); - text_criticalLayerFilterDm.set_text("1"); - text_criticalLayerFilterDm.select_region(0, text_criticalLayerFilterDm.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerFilterDm,2,6,1,1); - text_criticalLayerFilterDm.show(); - - label_criticalLayerBiasLr.set_text("Set bias lr_mult:\n(Leave unchanged if not needed) "); - label_criticalLayerBiasLr.set_line_wrap(); - label_criticalLayerBiasLr.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerBiasLr,0,7,2,1); - label_criticalLayerBiasLr.show(); - - text_criticalLayerBiasLr.set_max_length(100); - text_criticalLayerBiasLr.set_text("2"); - text_criticalLayerBiasLr.select_region(0, text_criticalLayerBiasLr.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerBiasLr,2,7,1,1); - text_criticalLayerBiasLr.show(); - - label_criticalLayerBiasDm.set_text("Set bias decay_mult:\n(Leave unchanged if not needed) "); - label_criticalLayerBiasDm.set_line_wrap(); - label_criticalLayerBiasDm.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerBiasDm,0,8,2,1); - label_criticalLayerBiasDm.show(); - - text_criticalLayerBiasDm.set_max_length(100); - text_criticalLayerBiasDm.set_text("0"); - text_criticalLayerBiasDm.select_region(0, text_criticalLayerBiasDm.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerBiasDm,2,8,1,1); - text_criticalLayerBiasDm.show(); - - label_criticalLayerNumOutput.set_text("Set param num_output: "); - label_criticalLayerNumOutput.set_line_wrap(); - label_criticalLayerNumOutput.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerNumOutput,0,9,2,1); - label_criticalLayerNumOutput.show(); - - text_criticalLayerNumOutput.set_max_length(100); - text_criticalLayerNumOutput.set_text("64"); - text_criticalLayerNumOutput.select_region(0, text_criticalLayerNumOutput.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerNumOutput,2,9,1,1); - text_criticalLayerNumOutput.show(); - - - label_criticalLayerWeightFiller.set_text("Set Weight Filler : "); - label_criticalLayerWeightFiller.set_line_wrap(); - label_criticalLayerWeightFiller.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerWeightFiller,0,16,2,1); - label_criticalLayerWeightFiller.show(); - -// Gtk::RadioButton::Group group1 = rbutton_criticalLayerWeightFillerConstant.get_group(); -// rbutton_criticalLayerWeightFillerUniform.set_group(group1); -// rbutton_criticalLayerWeightGaussian.set_group(group1); -// rbutton_criticalLayerWeightFillerPositiveUnitBall.set_group(group1); -// rbutton_criticalLayerWeightFillerXavier.set_group(group1); -// rbutton_criticalLayerWeightFillerMSRA.set_group(group1); -// rbutton_criticalLayerWeightFillerBilinear.set_group(group1); -// rbutton_criticalLayerWeightFillerConstant.set_active(); -// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerConstant,2,17,1,1); - rbutton_criticalLayerWeightFillerConstant.show(); -// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerUniform,2,18,1,1); - rbutton_criticalLayerWeightFillerUniform.show(); -// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightGaussian,2,19,1,1); - rbutton_criticalLayerWeightGaussian.show(); -// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerPositiveUnitBall,2,20,1,1); - rbutton_criticalLayerWeightFillerPositiveUnitBall.show(); -// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavier,2,21,1,1); - rbutton_criticalLayerWeightFillerXavier.show(); -// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerMSRA,2,22,1,1); - rbutton_criticalLayerWeightFillerMSRA.show(); -// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerBilinear,2,23,1,1); - rbutton_criticalLayerWeightFillerBilinear.show(); - - label_criticalLayerWeightFillerConstantValue.set_text("value: "); - label_criticalLayerWeightFillerConstantValue.set_line_wrap(); - label_criticalLayerWeightFillerConstantValue.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerConstantValue,4,17,1,1); - label_criticalLayerWeightFillerConstantValue.show(); - - text_criticalLayerWeightFillerConstantValue.set_max_length(100); - text_criticalLayerWeightFillerConstantValue.set_text("0.5"); - text_criticalLayerWeightFillerConstantValue.select_region(0, text_criticalLayerWeightFillerConstantValue.get_text_length()); -// m_grid_criticalLayerWeightFillerConstantValue.attach(text_criticalLayerWeightFillerConstantValue,5,15,1,1); - text_criticalLayerWeightFillerConstantValue.show(); - - label_criticalLayerWeightFillerUniformMin.set_text("min: "); - label_criticalLayerWeightFillerUniformMin.set_line_wrap(); - label_criticalLayerWeightFillerUniformMin.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerUniformMin,4,18,1,1); - label_criticalLayerWeightFillerUniformMin.show(); - - text_criticalLayerWeightFillerUniformMin.set_max_length(100); - text_criticalLayerWeightFillerUniformMin.set_text("0"); - text_criticalLayerWeightFillerUniformMin.select_region(0, text_criticalLayerWeightFillerUniformMin.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerWeightFillerUniformMin,5,18,1,1); - text_criticalLayerWeightFillerUniformMin.show(); - - label_criticalLayerWeightFillerUniformMax.set_text("max: "); - label_criticalLayerWeightFillerUniformMax.set_line_wrap(); - label_criticalLayerWeightFillerUniformMax.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerUniformMax,4,19,1,1); - label_criticalLayerWeightFillerUniformMax.show(); - - text_criticalLayerWeightFillerUniformMax.set_max_length(100); - text_criticalLayerWeightFillerUniformMax.set_text("1"); - text_criticalLayerWeightFillerUniformMax.select_region(0, text_criticalLayerWeightFillerUniformMax.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerWeightFillerUniformMax,5,19,1,1); - text_criticalLayerWeightFillerUniformMax.show(); - - label_criticalLayerWeightFillerGaussianMean.set_text("mean: "); - label_criticalLayerWeightFillerGaussianMean.set_line_wrap(); - label_criticalLayerWeightFillerGaussianMean.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerGaussianMean,4,19,1,1); - label_criticalLayerWeightFillerGaussianMean.show(); - - text_criticalLayerWeightFillerGaussianMean.set_max_length(100); - text_criticalLayerWeightFillerGaussianMean.set_text("0"); - text_criticalLayerWeightFillerGaussianMean.select_region(0, text_criticalLayerWeightFillerGaussianMean.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerWeightFillerGaussianMean,5,19,1,1); - text_criticalLayerWeightFillerGaussianMean.show(); - - label_criticalLayerWeightFillerGaussianStd.set_text("std: "); - label_criticalLayerWeightFillerGaussianStd.set_line_wrap(); - label_criticalLayerWeightFillerGaussianStd.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerGaussianStd,6,19,1,1); - label_criticalLayerWeightFillerGaussianStd.show(); - - text_criticalLayerWeightFillerGaussianStd.set_max_length(100); - text_criticalLayerWeightFillerGaussianStd.set_text("0.1"); - text_criticalLayerWeightFillerGaussianStd.select_region(0, text_criticalLayerWeightFillerGaussianStd.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerWeightFillerGaussianStd,7,19,1,1); - text_criticalLayerWeightFillerGaussianStd.show(); - - label_criticalLayerWeightFillerXavierVariance.set_text("variance_norm: "); - label_criticalLayerWeightFillerXavierVariance.set_line_wrap(); - label_criticalLayerWeightFillerXavierVariance.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerXavierVariance,4,20,1,1); - label_criticalLayerWeightFillerXavierVariance.show(); - -// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavierIn,5,20,1,1); - rbutton_criticalLayerWeightFillerXavierIn.show(); -// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavierOut,6,20,1,1); - rbutton_criticalLayerWeightFillerXavierOut.show(); -// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavierAvg,7,20,1,1); - rbutton_criticalLayerWeightFillerXavierAvg.show(); - - label_criticalLayerWeightFillerMSRAVariance.set_text("variance_norm: "); - label_criticalLayerWeightFillerMSRAVariance.set_line_wrap(); - label_criticalLayerWeightFillerMSRAVariance.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerMSRAVariance,4,20,1,1); - label_criticalLayerWeightFillerMSRAVariance.show(); - -// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavierIn,5,20,1,1); - rbutton_criticalLayerWeightFillerMSRAIn.show(); -// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavierOut,6,20,1,1); - rbutton_criticalLayerWeightFillerMSRAOut.show(); -// m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavierAvg,7,20,1,1); - rbutton_criticalLayerWeightFillerMSRAAvg.show(); - - label_criticalLayerBias.set_text("Set bias value: "); - label_criticalLayerBias.set_line_wrap(); - label_criticalLayerBias.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerBias,0,24,2,1); - label_criticalLayerBias.show(); - - text_criticalLayerBias.set_max_length(100); - text_criticalLayerBias.set_text("0.1"); - text_criticalLayerBias.select_region(0, text_criticalLayerBias.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerBias,2,24,1,1); - text_criticalLayerBias.show(); - } - else if( data == "Dropout") - { - label_criticalLayerBottom1.hide(); - text_criticalLayerBottom1.hide(); - label_criticalLayerBottom2.hide(); - text_criticalLayerBottom2.hide(); - label_criticalLayerTop.hide(); - text_criticalLayerTop.hide(); - label_criticalLayerName.hide(); - text_criticalLayerName.hide(); - label_criticalLayerFilterLr.hide(); - text_criticalLayerFilterLr.hide(); - label_criticalLayerFilterDm.hide(); - text_criticalLayerFilterDm.hide(); - label_criticalLayerBiasLr.hide(); - text_criticalLayerBiasLr.hide(); - label_criticalLayerBiasDm.hide(); - text_criticalLayerBiasDm.hide(); - label_criticalLayerNumOutput.hide(); - text_criticalLayerNumOutput.hide(); - label_criticalLayerKernelW.hide(); - text_criticalLayerKernelW.hide(); - label_criticalLayerKernelH.hide(); - text_criticalLayerKernelH.hide(); - label_criticalLayerStrideW.hide(); - text_criticalLayerStrideW.hide(); - label_criticalLayerStrideH.hide(); - text_criticalLayerStrideH.hide(); - label_criticalLayerPadW.hide(); - text_criticalLayerPadW.hide(); - label_criticalLayerPadH.hide(); - text_criticalLayerPadH.hide(); - label_criticalLayerWeightFiller.hide(); - rbutton_criticalLayerWeightFillerConstant.hide(); - rbutton_criticalLayerWeightFillerUniform.hide(); - rbutton_criticalLayerWeightGaussian.hide(); - rbutton_criticalLayerWeightFillerPositiveUnitBall.hide(); - rbutton_criticalLayerWeightFillerXavier.hide(); - rbutton_criticalLayerWeightFillerMSRA.hide(); - rbutton_criticalLayerWeightFillerBilinear.hide(); - label_criticalLayerWeightFillerConstantValue.hide(); - text_criticalLayerWeightFillerConstantValue.hide(); - label_criticalLayerWeightFillerUniformMin.hide(); - label_criticalLayerWeightFillerUniformMax.hide(); - text_criticalLayerWeightFillerUniformMin.hide(); - text_criticalLayerWeightFillerUniformMax.hide(); - label_criticalLayerWeightFillerGaussianMean.hide(); - text_criticalLayerWeightFillerGaussianMean.hide(); - label_criticalLayerWeightFillerGaussianStd.hide(); - text_criticalLayerWeightFillerGaussianStd.hide(); - label_criticalLayerWeightFillerXavierVariance.hide(); - rbutton_criticalLayerWeightFillerXavierIn.hide(); - rbutton_criticalLayerWeightFillerXavierOut.hide(); - rbutton_criticalLayerWeightFillerXavierAvg.hide(); - label_criticalLayerWeightFillerMSRAVariance.hide(); - rbutton_criticalLayerWeightFillerMSRAIn.hide(); - rbutton_criticalLayerWeightFillerMSRAOut.hide(); - rbutton_criticalLayerWeightFillerMSRAAvg.hide(); - label_criticalLayerDropoutRatio.hide(); - text_criticalLayerDropoutRatio.hide(); - label_criticalLayerPool.hide(); - label_criticalLayerBias.hide(); - text_criticalLayerBias.hide(); - rbutton_criticalLayerPoolMax.hide(); - rbutton_criticalLayerPoolAve.hide(); - - label_criticalLayerBottom1.set_text("Bottom1 Layer Name: "); - label_criticalLayerBottom1.set_line_wrap(); - label_criticalLayerBottom1.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerBottom1,0,1,2,1); - label_criticalLayerBottom1.show(); - - text_criticalLayerBottom1.set_max_length(100); - text_criticalLayerBottom1.set_text(""); - text_criticalLayerBottom1.select_region(0, text_criticalLayerBottom1.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerBottom1,2,1,1,1); - text_criticalLayerBottom1.show(); - - label_criticalLayerTop.set_text("Top Layer Name: "); - label_criticalLayerTop.set_line_wrap(); - label_criticalLayerTop.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerTop,0,3,2,1); - label_criticalLayerTop.show(); - - text_criticalLayerTop.set_max_length(100); - text_criticalLayerTop.set_text(""); - text_criticalLayerTop.select_region(0, text_criticalLayerTop.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerTop,2,3,1,1); - text_criticalLayerTop.show(); - - label_criticalLayerName.set_text("Current Layer Name: "); - label_criticalLayerName.set_line_wrap(); - label_criticalLayerName.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerName,0,4,2,1); - label_criticalLayerName.show(); - - text_criticalLayerName.set_max_length(100); - text_criticalLayerName.set_text(""); - text_criticalLayerName.select_region(0, text_criticalLayerName.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerName,2,4,1,1); - text_criticalLayerName.show(); - - label_criticalLayerDropoutRatio.set_text("Set Dropout Ratio: "); - label_criticalLayerDropoutRatio.set_line_wrap(); - label_criticalLayerDropoutRatio.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerDropoutRatio,0,22,2,1); - label_criticalLayerDropoutRatio.show(); - - text_criticalLayerDropoutRatio.set_max_length(100); - text_criticalLayerDropoutRatio.set_text("0.5"); - text_criticalLayerDropoutRatio.select_region(0, text_criticalLayerDropoutRatio.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerDropoutRatio,2,22,1,1); - text_criticalLayerDropoutRatio.show(); - } - else if(data == "Pooling") - { - label_criticalLayerBottom1.hide(); - text_criticalLayerBottom1.hide(); - label_criticalLayerBottom2.hide(); - text_criticalLayerBottom2.hide(); - label_criticalLayerTop.hide(); - text_criticalLayerTop.hide(); - label_criticalLayerName.hide(); - text_criticalLayerName.hide(); - label_criticalLayerFilterLr.hide(); - text_criticalLayerFilterLr.hide(); - label_criticalLayerFilterDm.hide(); - text_criticalLayerFilterDm.hide(); - label_criticalLayerBiasLr.hide(); - text_criticalLayerBiasLr.hide(); - label_criticalLayerBiasDm.hide(); - text_criticalLayerBiasDm.hide(); - label_criticalLayerNumOutput.hide(); - text_criticalLayerNumOutput.hide(); - label_criticalLayerKernelW.hide(); - text_criticalLayerKernelW.hide(); - label_criticalLayerKernelH.hide(); - text_criticalLayerKernelH.hide(); - label_criticalLayerStrideW.hide(); - text_criticalLayerStrideW.hide(); - label_criticalLayerStrideH.hide(); - text_criticalLayerStrideH.hide(); - label_criticalLayerPadW.hide(); - text_criticalLayerPadW.hide(); - label_criticalLayerPadH.hide(); - text_criticalLayerPadH.hide(); - label_criticalLayerWeightFiller.hide(); - rbutton_criticalLayerWeightFillerConstant.hide(); - rbutton_criticalLayerWeightFillerUniform.hide(); - rbutton_criticalLayerWeightGaussian.hide(); - rbutton_criticalLayerWeightFillerPositiveUnitBall.hide(); - rbutton_criticalLayerWeightFillerXavier.hide(); - rbutton_criticalLayerWeightFillerMSRA.hide(); - rbutton_criticalLayerWeightFillerBilinear.hide(); - label_criticalLayerWeightFillerConstantValue.hide(); - text_criticalLayerWeightFillerConstantValue.hide(); - label_criticalLayerWeightFillerUniformMin.hide(); - label_criticalLayerWeightFillerUniformMax.hide(); - text_criticalLayerWeightFillerUniformMin.hide(); - text_criticalLayerWeightFillerUniformMax.hide(); - label_criticalLayerWeightFillerGaussianMean.hide(); - text_criticalLayerWeightFillerGaussianMean.hide(); - label_criticalLayerWeightFillerGaussianStd.hide(); - text_criticalLayerWeightFillerGaussianStd.hide(); - label_criticalLayerWeightFillerXavierVariance.hide(); - rbutton_criticalLayerWeightFillerXavierIn.hide(); - rbutton_criticalLayerWeightFillerXavierOut.hide(); - rbutton_criticalLayerWeightFillerXavierAvg.hide(); - label_criticalLayerWeightFillerMSRAVariance.hide(); - rbutton_criticalLayerWeightFillerMSRAIn.hide(); - rbutton_criticalLayerWeightFillerMSRAOut.hide(); - rbutton_criticalLayerWeightFillerMSRAAvg.hide(); - label_criticalLayerDropoutRatio.hide(); - text_criticalLayerDropoutRatio.hide(); - label_criticalLayerPool.hide(); - label_criticalLayerBias.hide(); - text_criticalLayerBias.hide(); - rbutton_criticalLayerPoolMax.hide(); - rbutton_criticalLayerPoolAve.hide(); - - - label_criticalLayerBottom1.set_text("Bottom1 Layer Name: "); - label_criticalLayerBottom1.set_line_wrap(); - label_criticalLayerBottom1.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerBottom1,0,1,2,1); - label_criticalLayerBottom1.show(); - - text_criticalLayerBottom1.set_max_length(100); - text_criticalLayerBottom1.set_text(""); - text_criticalLayerBottom1.select_region(0, text_criticalLayerBottom1.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerBottom1,2,1,1,1); - text_criticalLayerBottom1.show(); - - label_criticalLayerTop.set_text("Top Layer Name: "); - label_criticalLayerTop.set_line_wrap(); - label_criticalLayerTop.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerTop,0,3,2,1); - label_criticalLayerTop.show(); - - text_criticalLayerTop.set_max_length(100); - text_criticalLayerTop.set_text(""); - text_criticalLayerTop.select_region(0, text_criticalLayerTop.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerTop,2,3,1,1); - text_criticalLayerTop.show(); - - label_criticalLayerName.set_text("Current Layer Name: "); - label_criticalLayerName.set_line_wrap(); - label_criticalLayerName.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerName,0,4,2,1); - label_criticalLayerName.show(); - - text_criticalLayerName.set_max_length(100); - text_criticalLayerName.set_text(""); - text_criticalLayerName.select_region(0, text_criticalLayerName.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerName,2,4,1,1); - text_criticalLayerName.show(); - - label_criticalLayerKernelW.set_text("Set param kernel_w: "); - label_criticalLayerKernelW.set_line_wrap(); - label_criticalLayerKernelW.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerKernelW,0,10,2,1); - label_criticalLayerKernelW.show(); - - text_criticalLayerKernelW.set_max_length(100); - text_criticalLayerKernelW.set_text("3"); - text_criticalLayerKernelW.select_region(0, text_criticalLayerKernelW.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerKernelW,2,10,1,1); - text_criticalLayerKernelW.show(); - - label_criticalLayerKernelH.set_text("Set param kernel_h: "); - label_criticalLayerKernelH.set_line_wrap(); - label_criticalLayerKernelH.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerKernelH,0,11,2,1); - label_criticalLayerKernelH.show(); - - text_criticalLayerKernelH.set_max_length(100); - text_criticalLayerKernelH.set_text("3"); - text_criticalLayerKernelH.select_region(0, text_criticalLayerKernelH.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerKernelH,2,11,1,1); - text_criticalLayerKernelH.show(); - - label_criticalLayerStrideW.set_text("Set param stride_w: "); - label_criticalLayerStrideW.set_line_wrap(); - label_criticalLayerStrideW.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerStrideW,0,12,2,1); - label_criticalLayerStrideW.show(); - - text_criticalLayerStrideW.set_max_length(100); - text_criticalLayerStrideW.set_text("1"); - text_criticalLayerStrideW.select_region(0, text_criticalLayerStrideW.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerStrideW,2,12,1,1); - text_criticalLayerStrideW.show(); - - label_criticalLayerStrideH.set_text("Set param stride_h: "); - label_criticalLayerStrideH.set_line_wrap(); - label_criticalLayerStrideH.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerStrideH,0,13,2,1); - label_criticalLayerStrideH.show(); - - text_criticalLayerStrideH.set_max_length(100); - text_criticalLayerStrideH.set_text("1"); - text_criticalLayerStrideH.select_region(0, text_criticalLayerStrideH.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerStrideH,2,13,1,1); - text_criticalLayerStrideH.show(); - - label_criticalLayerPadW.set_text("Set param pad_w: "); - label_criticalLayerPadW.set_line_wrap(); - label_criticalLayerPadW.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerPadW,0,14,2,1); - label_criticalLayerPadW.show(); - - text_criticalLayerPadW.set_max_length(100); - text_criticalLayerPadW.set_text("1"); - text_criticalLayerPadW.select_region(0, text_criticalLayerPadW.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerPadW,2,14,1,1); - text_criticalLayerPadW.show(); - - label_criticalLayerPadH.set_text("Set param pad_h: "); - label_criticalLayerPadH.set_line_wrap(); - label_criticalLayerPadH.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerPadH,0,14,2,1); - label_criticalLayerPadH.show(); - - text_criticalLayerPadH.set_max_length(100); - text_criticalLayerPadH.set_text("1"); - text_criticalLayerPadH.select_region(0, text_criticalLayerPadH.get_text_length()); -// m_grid_criticalLayerType.attach(text_criticalLayerPadH,2,14,1,1); - text_criticalLayerPadH.show(); - - label_criticalLayerPool.set_text("Set Pool Type: "); - label_criticalLayerPool.set_line_wrap(); - label_criticalLayerPool.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_criticalLayerType.attach(label_criticalLayerPool,0,23,2,1); - label_criticalLayerPool.show(); - - Gtk::RadioButton::Group group4 = rbutton_criticalLayerPoolMax.get_group(); - rbutton_criticalLayerPoolAve.set_group(group4); - rbutton_criticalLayerPoolMax.set_active(); -// m_grid_criticalLayerType.attach(rbutton_criticalLayerPoolMax,2,23,1,1); - rbutton_criticalLayerPoolMax.show(); -// m_grid_criticalLayerType.attach(rbutton_criticalLayerPoolAve,3,23,1,1); - rbutton_criticalLayerPoolAve.show(); - - } - else - { - label_criticalLayerBottom1.hide(); - text_criticalLayerBottom1.hide(); - label_criticalLayerBottom2.hide(); - text_criticalLayerBottom2.hide(); - label_criticalLayerTop.hide(); - text_criticalLayerTop.hide(); - label_criticalLayerName.hide(); - text_criticalLayerName.hide(); - label_criticalLayerFilterLr.hide(); - text_criticalLayerFilterLr.hide(); - label_criticalLayerFilterDm.hide(); - text_criticalLayerFilterDm.hide(); - label_criticalLayerBiasLr.hide(); - text_criticalLayerBiasLr.hide(); - label_criticalLayerBiasDm.hide(); - text_criticalLayerBiasDm.hide(); - label_criticalLayerNumOutput.hide(); - text_criticalLayerNumOutput.hide(); - label_criticalLayerKernelW.hide(); - text_criticalLayerKernelW.hide(); - label_criticalLayerKernelH.hide(); - text_criticalLayerKernelH.hide(); - label_criticalLayerStrideW.hide(); - text_criticalLayerStrideW.hide(); - label_criticalLayerStrideH.hide(); - text_criticalLayerStrideH.hide(); - label_criticalLayerPadW.hide(); - text_criticalLayerPadW.hide(); - label_criticalLayerPadH.hide(); - text_criticalLayerPadH.hide(); - label_criticalLayerWeightFiller.hide(); - rbutton_criticalLayerWeightFillerConstant.hide(); - rbutton_criticalLayerWeightFillerUniform.hide(); - rbutton_criticalLayerWeightGaussian.hide(); - rbutton_criticalLayerWeightFillerPositiveUnitBall.hide(); - rbutton_criticalLayerWeightFillerXavier.hide(); - rbutton_criticalLayerWeightFillerMSRA.hide(); - rbutton_criticalLayerWeightFillerBilinear.hide(); - label_criticalLayerWeightFillerConstantValue.hide(); - text_criticalLayerWeightFillerConstantValue.hide(); - label_criticalLayerWeightFillerUniformMin.hide(); - label_criticalLayerWeightFillerUniformMax.hide(); - text_criticalLayerWeightFillerUniformMin.hide(); - text_criticalLayerWeightFillerUniformMax.hide(); - label_criticalLayerWeightFillerGaussianMean.hide(); - text_criticalLayerWeightFillerGaussianMean.hide(); - label_criticalLayerWeightFillerGaussianStd.hide(); - text_criticalLayerWeightFillerGaussianStd.hide(); - label_criticalLayerWeightFillerXavierVariance.hide(); - rbutton_criticalLayerWeightFillerXavierIn.hide(); - rbutton_criticalLayerWeightFillerXavierOut.hide(); - rbutton_criticalLayerWeightFillerXavierAvg.hide(); - label_criticalLayerWeightFillerMSRAVariance.hide(); - rbutton_criticalLayerWeightFillerMSRAIn.hide(); - rbutton_criticalLayerWeightFillerMSRAOut.hide(); - rbutton_criticalLayerWeightFillerMSRAAvg.hide(); - label_criticalLayerDropoutRatio.hide(); - text_criticalLayerDropoutRatio.hide(); - label_criticalLayerPool.hide(); - label_criticalLayerBias.hide(); - text_criticalLayerBias.hide(); - rbutton_criticalLayerPoolMax.hide(); - rbutton_criticalLayerPoolAve.hide(); - } - m_sw_criticalLayerType.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); - m_grid_criticalLayerType.show(); -// show_all_children(); - m_sw_criticalLayerType.show(); - -} diff --git a/detectors/include/od/detectors/global2D/training/DisplayWindow.h b/detectors/include/od/detectors/global2D/training/DisplayWindow.h deleted file mode 100644 index 3566ccd2..00000000 --- a/detectors/include/od/detectors/global2D/training/DisplayWindow.h +++ /dev/null @@ -1,14 +0,0 @@ -#include "od/detectors/global2D/training/Network.h" - -void NetworkCreator::showWindow_displayWindow() -{ - remove(); - set_title("Display Entire Network"); - set_border_width(10); - add(box_fullCnnLayerMatter); - buffer_fullCnnLayerMatter->set_text(fullCnnLayerMatter); - textView_fullCnnLayerMatter.set_buffer(buffer_fullCnnLayerMatter); - show_all_children(); -} - - diff --git a/detectors/include/od/detectors/global2D/training/ExtraWindow.h b/detectors/include/od/detectors/global2D/training/ExtraWindow.h deleted file mode 100644 index 17c0e38e..00000000 --- a/detectors/include/od/detectors/global2D/training/ExtraWindow.h +++ /dev/null @@ -1,24 +0,0 @@ -#include "od/detectors/global2D/training/Network.h" - -void NetworkCreator::showWindow_extraLayerType(Glib::ustring data) -{ - remove(); - set_title("Extra Layer"); - set_border_width(10); - add(m_sw_extraLayerType); - m_grid_extraLayerType.set_column_spacing (10); - m_grid_extraLayerType.set_row_spacing (50); - - title_extraLayerType.set_text("Will be updated soon"); - title_extraLayerType.set_line_wrap(); - title_extraLayerType.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_extraLayerType.attach(title_extraLayerType,0,0,2,1); - title_extraLayerType.show(); - - button_addMoreLayer5.show(); - - m_sw_extraLayerType.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); - m_grid_extraLayerType.show(); -// show_all_children(); - m_sw_extraLayerType.show(); -} diff --git a/detectors/include/od/detectors/global2D/training/LossWindow.h b/detectors/include/od/detectors/global2D/training/LossWindow.h deleted file mode 100644 index 51aaf9ee..00000000 --- a/detectors/include/od/detectors/global2D/training/LossWindow.h +++ /dev/null @@ -1,127 +0,0 @@ -#include "od/detectors/global2D/training/Network.h" - -void NetworkCreator::showWindow_lossLayerType(Glib::ustring data) -{ - remove(); - set_title("Loss Layer"); - set_border_width(10); - add(m_sw_lossLayerType); - m_grid_lossLayerType.set_column_spacing (10); - m_grid_lossLayerType.set_row_spacing (50); - - //level 0 - if(data == "" or data == "SoftmaxWithLoss") - title_lossLayerType.set_text("Set the Properties of Loss Layer type: SoftmaxWithLoss"); - else - title_lossLayerType.set_text("Will be updated soon"); - title_lossLayerType.set_line_wrap(); - title_lossLayerType.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_lossLayerType.attach(title_lossLayerType,0,0,2,1); - title_lossLayerType.show(); - - - button_addMoreLayer4.show(); - - if(data == "" or data == "SoftmaxWithLoss") - { - label_lossLayerBottom1.hide(); - text_lossLayerBottom1.hide(); - label_lossLayerBottom2.hide(); - text_lossLayerBottom2.hide(); - label_lossLayerTop.hide(); - text_lossLayerTop.hide(); - label_lossLayerName.hide(); - text_lossLayerName.hide(); - label_lossLayerNormalize.hide(); - text_lossLayerNormalize.hide(); - button_setLossParameters.hide(); - label_lossLayerNormalize.hide(); - - label_lossLayerBottom1.set_text("Bottom1 Layer Name: "); - label_lossLayerBottom1.set_line_wrap(); - label_lossLayerBottom1.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_lossLayerType.attach(label_lossLayerBottom1,0,1,2,1); - label_lossLayerBottom1.show(); - - text_lossLayerBottom1.set_max_length(100); - text_lossLayerBottom1.set_text(""); - text_lossLayerBottom1.select_region(0, text_lossLayerBottom1.get_text_length()); -// m_grid_lossLayerType.attach(text_lossLayerBottom1,2,1,1,1); - text_lossLayerBottom1.show(); - - label_lossLayerBottom2.set_text("Bottom2 Layer Name: "); - label_lossLayerBottom2.set_line_wrap(); - label_lossLayerBottom2.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_lossLayerType.attach(label_lossLayerBottom2,0,2,2,1); - label_lossLayerBottom2.show(); - - text_lossLayerBottom2.set_max_length(100); - text_lossLayerBottom2.set_text(""); - text_lossLayerBottom2.select_region(0, text_lossLayerBottom2.get_text_length()); -// m_grid_lossLayerType.attach(text_lossLayerBottom2,2,2,1,1); - text_lossLayerBottom2.show(); - - label_lossLayerTop.set_text("Top Layer Name: "); - label_lossLayerTop.set_line_wrap(); - label_lossLayerTop.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_lossLayerType.attach(label_lossLayerTop,0,3,2,1); - label_lossLayerTop.show(); - - text_lossLayerTop.set_max_length(100); - text_lossLayerTop.set_text(""); - text_lossLayerTop.select_region(0, text_lossLayerTop.get_text_length()); -// m_grid_lossLayerType.attach(text_lossLayerTop,2,3,1,1); - text_lossLayerTop.show(); - - label_lossLayerName.set_text("Current Layer Name: "); - label_lossLayerName.set_line_wrap(); - label_lossLayerName.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_lossLayerType.attach(label_lossLayerName,0,4,2,1); - label_lossLayerName.show(); - - text_lossLayerName.set_max_length(100); - text_lossLayerName.set_text(""); - text_lossLayerName.select_region(0, text_lossLayerName.get_text_length()); -// m_grid_lossLayerType.attach(text_lossLayerName,2,4,1,1); - text_lossLayerName.show(); - - label_lossLayerNormalize.set_text("Normalize: \n(bool value)"); - label_lossLayerNormalize.set_line_wrap(); - label_lossLayerNormalize.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_lossLayerType.attach(label_lossLayerNormalize,0,5,2,1); - label_lossLayerNormalize.show(); - - text_lossLayerNormalize.set_max_length(100); - text_lossLayerNormalize.set_text(""); - text_lossLayerNormalize.select_region(0, text_lossLayerNormalize.get_text_length()); -// m_grid_lossLayerType.attach(text_lossLayerNormalize,2,5,1,1); - text_lossLayerNormalize.show(); - - label_lossLayerNormalize.set_text("Normalization: \n(select type)"); - label_lossLayerNormalize.set_line_wrap(); - label_lossLayerNormalize.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_lossLayerType.attach(label_lossLayerNormalize,0,6,2,1); - label_lossLayerNormalize.show(); - - button_setLossParameters.show(); - } - else - { - label_lossLayerBottom1.hide(); - text_lossLayerBottom1.hide(); - label_lossLayerBottom2.hide(); - text_lossLayerBottom2.hide(); - label_lossLayerTop.hide(); - text_lossLayerTop.hide(); - label_lossLayerName.hide(); - text_lossLayerName.hide(); - label_lossLayerNormalize.hide(); - text_lossLayerNormalize.hide(); - button_setLossParameters.hide(); - } - - m_sw_lossLayerType.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); - m_grid_lossLayerType.show(); -// show_all_children(); - m_sw_lossLayerType.show(); -} diff --git a/detectors/include/od/detectors/global2D/training/MainWindow.h b/detectors/include/od/detectors/global2D/training/MainWindow.h deleted file mode 100644 index 8d9813fe..00000000 --- a/detectors/include/od/detectors/global2D/training/MainWindow.h +++ /dev/null @@ -1,146 +0,0 @@ -#include "od/detectors/global2D/training/Network.h" - -void NetworkCreator::showWindow_main() -{ - remove(); - set_title("Network Creator"); - set_border_width(10); - add(m_sw1); - m_grid1.set_column_spacing (10); - m_grid1.set_row_spacing (50); -// m_sw1.add(m_grid1); - m_sw1.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); -// m_grid1.show(); - show_all_children(); - m_sw1.show(); -} - - -void NetworkCreator::on_cell_data_extra(const Gtk::TreeModel::const_iterator& iter) -{ - // level 1 - auto row_activationLayerType = *iter; - const Glib::ustring extra_activationLayerType = row_activationLayerType[column_activationLayerType.m_col_extra]; - if(extra_activationLayerType.empty()) - cell_activationLayerType.property_text() = "(none)"; - else - cell_activationLayerType.property_text() = "-" + extra_activationLayerType + "-"; - cell_activationLayerType.property_foreground() = "green"; - - // level 2 - auto row_criticalLayerType = *iter; - const Glib::ustring extra_criticalLayerType = row_criticalLayerType[column_criticalLayerType.m_col_extra]; - if(extra_criticalLayerType.empty()) - cell_criticalLayerType.property_text() = "(none)"; - else - cell_criticalLayerType.property_text() = "-" + extra_criticalLayerType + "-"; - cell_criticalLayerType.property_foreground() = "red"; - - // level 3 - auto row_normalizationLayerType = *iter; - const Glib::ustring extra_normalizationLayerType = row_normalizationLayerType[column_normalizationLayerType.m_col_extra]; - if(extra_normalizationLayerType.empty()) - cell_normalizationLayerType.property_text() = "(none)"; - else - cell_normalizationLayerType.property_text() = "-" + extra_normalizationLayerType + "-"; - cell_normalizationLayerType.property_foreground() = "blue"; - - // level 4 - auto row_lossLayerType = *iter; - const Glib::ustring extra_lossLayerType = row_lossLayerType[column_lossLayerType.m_col_extra]; - if(extra_lossLayerType.empty()) - cell_lossLayerType.property_text() = "(none)"; - else - cell_lossLayerType.property_text() = "-" + extra_lossLayerType + "-"; - cell_lossLayerType.property_foreground() = "brown"; - - // level 5 - auto row_extraLayerType = *iter; - const Glib::ustring extra_extraLayerType = row_extraLayerType[column_extraLayerType.m_col_extra]; - if(extra_extraLayerType.empty()) - cell_extraLayerType.property_text() = "(none)"; - else - cell_extraLayerType.property_text() = "-" + extra_extraLayerType + "-"; - cell_extraLayerType.property_foreground() = "purple"; - -} - -void NetworkCreator::on_combo_changed() -{ - Gtk::TreeModel::iterator iter_activationLayerType = combo_activationLayerType.get_active(); - Gtk::TreeModel::iterator iter_criticalLayerType = combo_criticalLayerType.get_active(); - Gtk::TreeModel::iterator iter_normalizationLayerType = combo_normalizationLayerType.get_active(); - Gtk::TreeModel::iterator iter_lossLayerType = combo_lossLayerType.get_active(); - Gtk::TreeModel::iterator iter_extraLayerType = combo_extraLayerType.get_active(); - - - // level 1 - if(iter_activationLayerType) - { - Gtk::TreeModel::Row row_activationLayerType = *iter_activationLayerType; - if(row_activationLayerType) - { - int id_activationLayerType = row_activationLayerType[column_activationLayerType.m_col_id]; - Glib::ustring name_activationLayerType = row_activationLayerType[column_activationLayerType.m_col_name]; -// std::cout << " ID=" << id_activationLayerType << ", name=" << name_activationLayerType << std::endl; - activationLayerTypeData = name_activationLayerType; - } - - } - - //level 2 - if(iter_criticalLayerType) - { - Gtk::TreeModel::Row row_criticalLayerType = *iter_criticalLayerType; - if(row_criticalLayerType) - { - int id_criticalLayerType = row_criticalLayerType[column_criticalLayerType.m_col_id]; - Glib::ustring name_criticalLayerType = row_criticalLayerType[column_criticalLayerType.m_col_name]; -// std::cout << " ID=" << id_criticalLayerType << ", name=" << name_criticalLayerType << std::endl; - criticalLayerTypeData = name_criticalLayerType; - } - } - - // level 3 - if(iter_normalizationLayerType) - { - Gtk::TreeModel::Row row_normalizationLayerType = *iter_normalizationLayerType; - if(row_normalizationLayerType) - { - int id_normalizationLayerType = row_normalizationLayerType[column_normalizationLayerType.m_col_id]; - Glib::ustring name_normalizationLayerType = row_normalizationLayerType[column_normalizationLayerType.m_col_name]; -// std::cout << " ID=" << id_normalizationLayerType << ", name=" << name_normalizationLayerType << std::endl; - normalizationLayerTypeData = name_normalizationLayerType; - } - } - - // level 4 - if(iter_lossLayerType) - { - Gtk::TreeModel::Row row_lossLayerType = *iter_lossLayerType; - if(row_lossLayerType) - { - int id_lossLayerType = row_lossLayerType[column_lossLayerType.m_col_id]; - Glib::ustring name_lossLayerType = row_lossLayerType[column_lossLayerType.m_col_name]; -// std::cout << " ID=" << id_lossLayerType << ", name=" << name_lossLayerType << std::endl; - lossLayerTypeData = name_lossLayerType; - } - } - - // level 5 - if(iter_extraLayerType) - { - Gtk::TreeModel::Row row_extraLayerType = *iter_extraLayerType; - if(row_extraLayerType) - { - int id_extraLayerType = row_extraLayerType[column_extraLayerType.m_col_id]; - Glib::ustring name_extraLayerType = row_extraLayerType[column_extraLayerType.m_col_name]; -// std::cout << " ID=" << id_extraLayerType << ", name=" << name_extraLayerType << std::endl; - extraLayerTypeData = name_extraLayerType; - } - } - - else - std::cout << "invalid iter" << std::endl; -} - diff --git a/detectors/include/od/detectors/global2D/training/Network.h b/detectors/include/od/detectors/global2D/training/Network.h deleted file mode 100644 index 571bae80..00000000 --- a/detectors/include/od/detectors/global2D/training/Network.h +++ /dev/null @@ -1,239 +0,0 @@ -#pragma once - -#include <gtkmm/grid.h> -#include <gtkmm/entry.h> -#include <gtkmm/button.h> -#include <gtkmm/radiobutton.h> -#include <gtkmm/messagedialog.h> -#include <gtkmm/window.h> -#include <gtkmm/scrolledwindow.h> -#include <gtkmm/application.h> -#include <gtkmm/comboboxtext.h> -#include <gtkmm/liststore.h> -#include <gtkmm/textview.h> -#include <iostream> -#include <fstream> -#include <sstream> -#include <vector> - -class NetworkCreator : public Gtk::Window -{ - public: - NetworkCreator(); - virtual ~NetworkCreator(); - - - protected: - void on_button_clicked(Glib::ustring data); - void on_combo_changed(); - void on_cell_data_extra(const Gtk::TreeModel::const_iterator& iter); - void showWindow_main(); - void showWindow_activationLayerType(Glib::ustring data); - void showWindow_displayWindow(); - void showWindow_criticalLayerType(Glib::ustring data); - void showWindow_normalizationLayerType(Glib::ustring data); - void showWindow_lossLayerType(Glib::ustring data); - void showWindow_extraLayerType(Glib::ustring data); - - // Child widgets: - Gtk::Grid m_grid1, - m_grid_activationLayerType, - m_grid_criticalLayerType, - m_grid_normalizationLayerType, - m_grid_lossLayerType, - m_grid_extraLayerType; - Gtk::ScrolledWindow m_sw1, - m_sw_activationLayerType, - m_sw_criticalLayerType, - m_sw_fullCnnLayerMatter, - m_sw_normalizationLayerType, - m_sw_lossLayerType, - m_sw_extraLayerType; - Gtk::Button button_networkFileName, - button_activationLayerType, - button_addMoreLayer, - button_setActivationParameters, - button_displayCnnLayers, - button_editMore, - button_deleteLayerAtEnd, - button_criticalLayerType, - button_setCriticalParameters, - button_addMoreLayer2, - button_normalizationLayerType, - button_lossLayerType, - button_extraLayerType, - button_addMoreLayer3, - button_setNormalizationParameters, - button_addMoreLayer4, - button_setLossParameters, - button_addMoreLayer5, - button_saveFile; - Gtk::Entry text_networkFileName, - text_activationLayerTop, - text_activationLayerBottom, - text_activationLayerName, - text_activationLayerType, - text_activationLayerScale, - text_activationLayerShift, - text_activationLayerBase, - text_activationLayerNegativeSlope, - text_criticalLayerTop, - text_criticalLayerBottom1, - text_criticalLayerBottom2, - text_criticalLayerName, - text_criticalLayerFilterLr, - text_criticalLayerFilterDm, - text_criticalLayerBiasLr, - text_criticalLayerBiasDm, - text_criticalLayerNumOutput, - text_criticalLayerKernelW, - text_criticalLayerKernelH, - text_criticalLayerStrideW, - text_criticalLayerStrideH, - text_criticalLayerPadW, - text_criticalLayerPadH, - text_criticalLayerWeightFillerConstantValue, - text_criticalLayerWeightFillerUniformMin, - text_criticalLayerWeightFillerUniformMax, - text_criticalLayerWeightFillerGaussianMean, - text_criticalLayerWeightFillerGaussianStd, - text_criticalLayerDropoutRatio, - text_criticalLayerBias, - text_normalizationLayerTop, - text_normalizationLayerBottom, - text_normalizationLayerName, - text_normalizationLayerlocalSize, - text_normalizationLayerAlpha, - text_normalizationLayerBeta, - text_normalizationLayerK, - text_normalizationLayerAcrossChannel, - text_normalizationLayerNormalizeVariance, - text_normalizationLayerEps, - text_lossLayerTop, - text_lossLayerBottom1, - text_lossLayerBottom2, - text_lossLayerName, - text_lossLayerNormalize; - Gtk::Label label_networkFileName, - label_activationLayerType, - label_criticalLayerType, - label_normalizationLayerType, - label_lossLayerType, - label_extraLayerType, - title_activationLayerType, - label_activationLayerTop, - label_activationLayerBottom, - label_activationLayerName, - label_activationLayerScale, - label_activationLayerShift, - label_activationLayerBase, - label_activationLayerNegativeSlope, - title_criticalLayerType, - label_criticalLayerTop, - label_criticalLayerBottom1, - label_criticalLayerBottom2, - label_criticalLayerName, - label_criticalLayerFilterLr, - label_criticalLayerFilterDm, - label_criticalLayerBiasLr, - label_criticalLayerBiasDm, - label_criticalLayerNumOutput, - label_criticalLayerKernelW, - label_criticalLayerKernelH, - label_criticalLayerStrideW, - label_criticalLayerStrideH, - label_criticalLayerPadW, - label_criticalLayerPadH, - label_criticalLayerWeightFiller, - label_criticalLayerWeightFillerConstantValue, - label_criticalLayerWeightFillerUniformMin, - label_criticalLayerWeightFillerUniformMax, - label_criticalLayerWeightFillerGaussianMean, - label_criticalLayerWeightFillerGaussianStd, - label_criticalLayerWeightFillerXavierVariance, - label_criticalLayerWeightFillerMSRAVariance, - label_criticalLayerDropoutRatio, - label_criticalLayerPool, - label_criticalLayerBias, - title_normalizationLayerType, - label_normalizationLayerTop, - label_normalizationLayerBottom, - label_normalizationLayerName, - label_normalizationLayerlocalSize, - label_normalizationLayerAlpha, - label_normalizationLayerBeta, - label_normalizationLayerK, - label_normalizationLayerNormRegion, - label_normalizationLayerAcrossChannel, - label_normalizationLayerNormalizeVariance, - label_normalizationLayerEps, - title_lossLayerType, - title_extraLayerType, - label_lossLayerTop, - label_lossLayerBottom1, - label_lossLayerBottom2, - label_lossLayerName, - label_lossLayerNormalize, - label_lossLayerNormalization; - Gtk::ComboBox combo_activationLayerType, - combo_criticalLayerType, - combo_normalizationLayerType, - combo_lossLayerType, - combo_extraLayerType; - Gtk::TextView textView_fullCnnLayerMatter; - Glib::RefPtr<Gtk::TextBuffer> buffer_fullCnnLayerMatter; - Gtk::Box box_fullCnnLayerMatter; - Gtk::ButtonBox buttonBox_fullCnnLayerMatter; - Gtk::RadioButton rbutton_criticalLayerWeightFillerConstant, rbutton_criticalLayerWeightFillerUniform, - rbutton_criticalLayerWeightGaussian, rbutton_criticalLayerWeightFillerPositiveUnitBall, - rbutton_criticalLayerWeightFillerXavier, rbutton_criticalLayerWeightFillerMSRA, - rbutton_criticalLayerWeightFillerBilinear, - rbutton_criticalLayerWeightFillerXavierIn, rbutton_criticalLayerWeightFillerXavierOut, - rbutton_criticalLayerWeightFillerXavierAvg, - rbutton_criticalLayerWeightFillerMSRAIn, rbutton_criticalLayerWeightFillerMSRAOut, - rbutton_criticalLayerWeightFillerMSRAAvg, - rbutton_criticalLayerPoolMax, rbutton_criticalLayerPoolAve, - rbutton_normalizationLayerLRNWithin, rbutton_normalizationLayerLRNAcross; - - - //Tree model columns: - class ModelColumns : public Gtk::TreeModel::ColumnRecord - { - public: - ModelColumns(){ add(m_col_id); add(m_col_name); add(m_col_extra);} - Gtk::TreeModelColumn<int> m_col_id; - Gtk::TreeModelColumn<Glib::ustring> m_col_name; - Gtk::TreeModelColumn<Glib::ustring> m_col_extra; - }; - - ModelColumns column_activationLayerType, - column_criticalLayerType, - column_normalizationLayerType, - column_lossLayerType, - column_extraLayerType; - Gtk::CellRendererText cell_activationLayerType, - cell_criticalLayerType, - cell_normalizationLayerType, - cell_lossLayerType, - cell_extraLayerType; - Glib::RefPtr<Gtk::ListStore> ref_activationLayerType, - ref_criticalLayerType, - ref_normalizationLayerType, - ref_lossLayerType, - ref_extraLayerType; - - - private: - Glib::ustring networkFileName; - Glib::ustring activationLayerTypeData, activationLayerTypeMatter; - Glib::ustring criticalLayerTypeData, criticalLayerTypeMatter; - Glib::ustring normalizationLayerTypeData, normalizationLayerTypeMatter; - Glib::ustring lossLayerTypeData, lossLayerTypeMatter; - Glib::ustring extraLayerTypeData, extraLayerTypeMatter; - int numLayers; - Glib::ustring fullCnnLayerMatter; - std::vector<Glib::ustring> fullCnnLayers; - -}; - - diff --git a/detectors/include/od/detectors/global2D/training/Node.h b/detectors/include/od/detectors/global2D/training/Node.h deleted file mode 100644 index cd19ee88..00000000 --- a/detectors/include/od/detectors/global2D/training/Node.h +++ /dev/null @@ -1,86 +0,0 @@ -#include "od/detectors/global2D/training/Network.h" -#include <iostream> - -struct Node { - Glib::ustring data; - Node* nextLayer; -}; - - -void initializeLayer(struct Node *headLayer, Glib::ustring data) -{ - headLayer->data = data; - headLayer->nextLayer = NULL; -} - -void appendLayer(struct Node *headLayer, Glib::ustring data) -{ - Node *newLayer = new Node; - newLayer->data = data; - newLayer->nextLayer = NULL; - - Node *currentLayer = headLayer; - while(currentLayer) - { - if(currentLayer->nextLayer == NULL) - { - currentLayer->nextLayer = newLayer; - return; - } - currentLayer = currentLayer->nextLayer; - } -} - - -bool deleteLayer(struct Node **headLayer, Node *deleteLayer) -{ - Node *currentLayer = *headLayer; - if(deleteLayer == *headLayer) - { - *headLayer = currentLayer->nextLayer; - delete deleteLayer; - return true; - } - - while(currentLayer) - { - if(currentLayer->nextLayer == deleteLayer) - { - currentLayer->nextLayer = deleteLayer->nextLayer; - delete deleteLayer; - return true; - } - currentLayer = currentLayer->nextLayer; - } - return false; -} - - -struct Node *searchLayer(struct Node *headLayer, Glib::ustring data) - { - Node *currentLayer = headLayer; - while(currentLayer) - { - if(currentLayer->data == data) - { - return currentLayer; - } - currentLayer = currentLayer->nextLayer; - } - std::cout << "No Layer " << data << " in the CNN." << std::endl; -} - -Glib::ustring displayCNN(struct Node *headLayer) -{ - Node *cnn = headLayer; - Glib::ustring fullCnnLayerMatter = ""; - while(cnn) - { -// std::cout << cnn->data << std::endl; - fullCnnLayerMatter += cnn->data; - cnn = cnn->nextLayer; - } -// std::cout << std::endl; -// std::cout << std::endl; - return fullCnnLayerMatter; -} diff --git a/detectors/include/od/detectors/global2D/training/NormalizationWindow.h b/detectors/include/od/detectors/global2D/training/NormalizationWindow.h deleted file mode 100644 index f48e1ad5..00000000 --- a/detectors/include/od/detectors/global2D/training/NormalizationWindow.h +++ /dev/null @@ -1,345 +0,0 @@ -#include "od/detectors/global2D/training/Network.h" - -void NetworkCreator::showWindow_normalizationLayerType(Glib::ustring data) -{ - remove(); - set_title("Normalization Layer"); - set_border_width(10); - add(m_sw_normalizationLayerType); - m_grid_normalizationLayerType.set_column_spacing (10); - m_grid_normalizationLayerType.set_row_spacing (50); - - //level 0 - if(data == "") - title_normalizationLayerType.set_text("Set the Properties of Normalization Layer type: BatchNorm"); - else - title_normalizationLayerType.set_text("Set the Properties of Normalization Layer type: " + data); - title_normalizationLayerType.set_line_wrap(); - title_normalizationLayerType.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_normalizationLayerType.attach(title_normalizationLayerType,0,0,2,1); - title_normalizationLayerType.show(); - - button_setNormalizationParameters.show(); - button_addMoreLayer3.show(); - - if(data == "" or data == "BatchNorm") - { - label_normalizationLayerTop.hide(); - label_normalizationLayerBottom.hide(); - label_normalizationLayerName.hide(); - text_normalizationLayerTop.hide(); - text_normalizationLayerBottom.hide(); - text_normalizationLayerName.hide(); - label_normalizationLayerlocalSize.hide(); - text_normalizationLayerlocalSize.hide(); - label_normalizationLayerAlpha.hide(); - text_normalizationLayerAlpha.hide(); - label_normalizationLayerBeta.hide(); - text_normalizationLayerBeta.hide(); - label_normalizationLayerK.hide(); - text_normalizationLayerK.hide(); - label_normalizationLayerNormRegion.hide(); - rbutton_normalizationLayerLRNWithin.hide(); - rbutton_normalizationLayerLRNAcross.hide(); - label_normalizationLayerAcrossChannel.hide(); - text_normalizationLayerAcrossChannel.hide(); - label_normalizationLayerNormalizeVariance.hide(); - text_normalizationLayerNormalizeVariance.hide(); - label_normalizationLayerEps.hide(); - text_normalizationLayerEps.hide(); - - - label_normalizationLayerBottom.set_text("Bottom Layer Name: "); - label_normalizationLayerBottom.set_line_wrap(); - label_normalizationLayerBottom.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_normalizationLayerType.attach(label_normalizationLayerBottom,0,1,2,1); - label_normalizationLayerBottom.show(); - - text_normalizationLayerBottom.set_max_length(100); - text_normalizationLayerBottom.set_text(""); - text_normalizationLayerBottom.select_region(0, text_normalizationLayerBottom.get_text_length()); -// m_grid_normalizationLayerType.attach(text_normalizationLayerBottom1,2,1,1,1); - text_normalizationLayerBottom.show(); - - label_normalizationLayerTop.set_text("Top Layer Name: "); - label_normalizationLayerTop.set_line_wrap(); - label_normalizationLayerTop.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_normalizationLayerType.attach(label_normalizationLayerTop,0,3,2,1); - label_normalizationLayerTop.show(); - - text_normalizationLayerTop.set_max_length(100); - text_normalizationLayerTop.set_text(""); - text_normalizationLayerTop.select_region(0, text_normalizationLayerTop.get_text_length()); -// m_grid_normalizationLayerType.attach(text_normalizationLayerTop,2,3,1,1); - text_normalizationLayerTop.show(); - - label_normalizationLayerName.set_text("Current Layer Name: "); - label_normalizationLayerName.set_line_wrap(); - label_normalizationLayerName.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_normalizationLayerType.attach(label_normalizationLayerName,0,4,2,1); - label_normalizationLayerName.show(); - - text_normalizationLayerName.set_max_length(100); - text_normalizationLayerName.set_text(""); - text_normalizationLayerName.select_region(0, text_normalizationLayerName.get_text_length()); -// m_grid_normalizationLayerType.attach(text_normalizationLayerName,2,4,1,1); - text_normalizationLayerName.show(); - } - else if(data == "LRN") - { - label_normalizationLayerTop.hide(); - label_normalizationLayerBottom.hide(); - label_normalizationLayerName.hide(); - text_normalizationLayerTop.hide(); - text_normalizationLayerBottom.hide(); - text_normalizationLayerName.hide(); - label_normalizationLayerlocalSize.hide(); - text_normalizationLayerlocalSize.hide(); - label_normalizationLayerAlpha.hide(); - text_normalizationLayerAlpha.hide(); - label_normalizationLayerBeta.hide(); - text_normalizationLayerBeta.hide(); - label_normalizationLayerK.hide(); - text_normalizationLayerK.hide(); - label_normalizationLayerNormRegion.hide(); - rbutton_normalizationLayerLRNWithin.hide(); - rbutton_normalizationLayerLRNAcross.hide(); - label_normalizationLayerAcrossChannel.hide(); - text_normalizationLayerAcrossChannel.hide(); - label_normalizationLayerNormalizeVariance.hide(); - text_normalizationLayerNormalizeVariance.hide(); - label_normalizationLayerEps.hide(); - text_normalizationLayerEps.hide(); - - - label_normalizationLayerBottom.set_text("Bottom Layer Name: "); - label_normalizationLayerBottom.set_line_wrap(); - label_normalizationLayerBottom.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_normalizationLayerType.attach(label_normalizationLayerBottom,0,1,2,1); - label_normalizationLayerBottom.show(); - - text_normalizationLayerBottom.set_max_length(100); - text_normalizationLayerBottom.set_text(""); - text_normalizationLayerBottom.select_region(0, text_normalizationLayerBottom.get_text_length()); -// m_grid_normalizationLayerType.attach(text_normalizationLayerBottom1,2,1,1,1); - text_normalizationLayerBottom.show(); - - label_normalizationLayerTop.set_text("Top Layer Name: "); - label_normalizationLayerTop.set_line_wrap(); - label_normalizationLayerTop.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_normalizationLayerType.attach(label_normalizationLayerTop,0,3,2,1); - label_normalizationLayerTop.show(); - - text_normalizationLayerTop.set_max_length(100); - text_normalizationLayerTop.set_text(""); - text_normalizationLayerTop.select_region(0, text_normalizationLayerTop.get_text_length()); -// m_grid_normalizationLayerType.attach(text_normalizationLayerTop,2,3,1,1); - text_normalizationLayerTop.show(); - - label_normalizationLayerName.set_text("Current Layer Name: "); - label_normalizationLayerName.set_line_wrap(); - label_normalizationLayerName.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_normalizationLayerType.attach(label_normalizationLayerName,0,4,2,1); - label_normalizationLayerName.show(); - - text_normalizationLayerName.set_max_length(100); - text_normalizationLayerName.set_text(""); - text_normalizationLayerName.select_region(0, text_normalizationLayerName.get_text_length()); -// m_grid_normalizationLayerType.attach(text_normalizationLayerName,2,4,1,1); - text_normalizationLayerName.show(); - - label_normalizationLayerlocalSize.set_text("Inner Parameter - local_size: "); - label_normalizationLayerlocalSize.set_line_wrap(); - label_normalizationLayerlocalSize.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_normalizationLayerType.attach(label_normalizationLayerlocalSize,0,5,2,1); - label_normalizationLayerlocalSize.show(); - - text_normalizationLayerlocalSize.set_max_length(100); - text_normalizationLayerlocalSize.set_text("5"); - text_normalizationLayerlocalSize.select_region(0, text_normalizationLayerlocalSize.get_text_length()); -// m_grid_normalizationLayerType.attach(text_normalizationLayerlocalSize,2,5,1,1); - text_normalizationLayerlocalSize.show(); - - label_normalizationLayerAlpha.set_text("Inner Parameter - alpha: "); - label_normalizationLayerAlpha.set_line_wrap(); - label_normalizationLayerAlpha.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_normalizationLayerType.attach(label_normalizationLayerAlpha,0,6,2,1); - label_normalizationLayerAlpha.show(); - - text_normalizationLayerAlpha.set_max_length(100); - text_normalizationLayerAlpha.set_text("0.0001"); - text_normalizationLayerAlpha.select_region(0, text_normalizationLayerAlpha.get_text_length()); -// m_grid_normalizationLayerType.attach(text_normalizationLayerAlpha,2,6,1,1); - text_normalizationLayerAlpha.show(); - - label_normalizationLayerBeta.set_text("Inner Parameter - beta: "); - label_normalizationLayerBeta.set_line_wrap(); - label_normalizationLayerBeta.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_normalizationLayerType.attach(label_normalizationLayerBeta,0,7,2,1); - label_normalizationLayerBeta.show(); - - text_normalizationLayerBeta.set_max_length(100); - text_normalizationLayerBeta.set_text("0.0001"); - text_normalizationLayerBeta.select_region(0, text_normalizationLayerBeta.get_text_length()); -// m_grid_normalizationLayerType.attach(text_normalizationLayerBeta,2,7,1,1); - text_normalizationLayerBeta.show(); - - label_normalizationLayerK.set_text("Inner Parameter - k: "); - label_normalizationLayerK.set_line_wrap(); - label_normalizationLayerK.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_normalizationLayerType.attach(label_normalizationLayerK,0,8,2,1); - label_normalizationLayerK.show(); - - text_normalizationLayerK.set_max_length(100); - text_normalizationLayerK.set_text("1"); - text_normalizationLayerK.select_region(0, text_normalizationLayerK.get_text_length()); -// m_grid_normalizationLayerType.attach(text_normalizationLayerK,2,8,1,1); - text_normalizationLayerK.show(); - - label_normalizationLayerNormRegion.set_text("Inner Parameter - norm_region: "); - label_normalizationLayerNormRegion.set_line_wrap(); - label_normalizationLayerNormRegion.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_normalizationLayerType.attach(label_normalizationLayerNormRegion,0,9,2,1); - label_normalizationLayerNormRegion.show(); - - Gtk::RadioButton::Group group5 = rbutton_normalizationLayerLRNWithin.get_group(); - rbutton_normalizationLayerLRNAcross.set_group(group5); - rbutton_normalizationLayerLRNWithin.set_active(); -// m_grid_normalizationLayerType.attach(rbutton_normalizationLayerLRNWithin,2,9,1,1); - rbutton_normalizationLayerLRNWithin.show(); -// m_grid_normalizationLayerType.attach(rbutton_normalizationLayerLRNAcross,3,9,1,1); - rbutton_normalizationLayerLRNAcross.show(); - - } - else if(data == "MVN") - { - label_normalizationLayerTop.hide(); - label_normalizationLayerBottom.hide(); - label_normalizationLayerName.hide(); - text_normalizationLayerTop.hide(); - text_normalizationLayerBottom.hide(); - text_normalizationLayerName.hide(); - label_normalizationLayerlocalSize.hide(); - text_normalizationLayerlocalSize.hide(); - label_normalizationLayerAlpha.hide(); - text_normalizationLayerAlpha.hide(); - label_normalizationLayerBeta.hide(); - text_normalizationLayerBeta.hide(); - label_normalizationLayerK.hide(); - text_normalizationLayerK.hide(); - label_normalizationLayerNormRegion.hide(); - rbutton_normalizationLayerLRNWithin.hide(); - rbutton_normalizationLayerLRNAcross.hide(); - label_normalizationLayerAcrossChannel.hide(); - text_normalizationLayerAcrossChannel.hide(); - label_normalizationLayerNormalizeVariance.hide(); - text_normalizationLayerNormalizeVariance.hide(); - label_normalizationLayerEps.hide(); - text_normalizationLayerEps.hide(); - - - label_normalizationLayerBottom.set_text("Bottom Layer Name: "); - label_normalizationLayerBottom.set_line_wrap(); - label_normalizationLayerBottom.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_normalizationLayerType.attach(label_normalizationLayerBottom,0,1,2,1); - label_normalizationLayerBottom.show(); - - text_normalizationLayerBottom.set_max_length(100); - text_normalizationLayerBottom.set_text(""); - text_normalizationLayerBottom.select_region(0, text_normalizationLayerBottom.get_text_length()); -// m_grid_normalizationLayerType.attach(text_normalizationLayerBottom1,2,1,1,1); - text_normalizationLayerBottom.show(); - - label_normalizationLayerTop.set_text("Top Layer Name: "); - label_normalizationLayerTop.set_line_wrap(); - label_normalizationLayerTop.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_normalizationLayerType.attach(label_normalizationLayerTop,0,3,2,1); - label_normalizationLayerTop.show(); - - text_normalizationLayerTop.set_max_length(100); - text_normalizationLayerTop.set_text(""); - text_normalizationLayerTop.select_region(0, text_normalizationLayerTop.get_text_length()); -// m_grid_normalizationLayerType.attach(text_normalizationLayerTop,2,3,1,1); - text_normalizationLayerTop.show(); - - label_normalizationLayerName.set_text("Current Layer Name: "); - label_normalizationLayerName.set_line_wrap(); - label_normalizationLayerName.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_normalizationLayerType.attach(label_normalizationLayerName,0,4,2,1); - label_normalizationLayerName.show(); - - text_normalizationLayerName.set_max_length(100); - text_normalizationLayerName.set_text(""); - text_normalizationLayerName.select_region(0, text_normalizationLayerName.get_text_length()); -// m_grid_normalizationLayerType.attach(text_normalizationLayerName,2,4,1,1); - text_normalizationLayerName.show(); - - label_normalizationLayerAcrossChannel.set_text("Inner Parameter - across_channels: \n(boolean- 0 or 1)"); - label_normalizationLayerAcrossChannel.set_line_wrap(); - label_normalizationLayerAcrossChannel.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_normalizationLayerType.attach(label_normalizationLayerAcrossChannel,0,10,2,1); - label_normalizationLayerAcrossChannel.show(); - - text_normalizationLayerAcrossChannel.set_max_length(100); - text_normalizationLayerAcrossChannel.set_text("0"); - text_normalizationLayerAcrossChannel.select_region(0, text_normalizationLayerAcrossChannel.get_text_length()); -// m_grid_normalizationLayerType.attach(text_normalizationLayerAcrossChannel,2,10,1,1); - text_normalizationLayerAcrossChannel.show(); - - label_normalizationLayerNormalizeVariance.set_text("Inner Parameter - normalize_variance: \n(boolean- 0 or 1)"); - label_normalizationLayerNormalizeVariance.set_line_wrap(); - label_normalizationLayerNormalizeVariance.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_normalizationLayerType.attach(label_normalizationLayerNormalizeVariance,0,11,2,1); - label_normalizationLayerNormalizeVariance.show(); - - text_normalizationLayerNormalizeVariance.set_max_length(100); - text_normalizationLayerNormalizeVariance.set_text("0"); - text_normalizationLayerNormalizeVariance.select_region(0, text_normalizationLayerNormalizeVariance.get_text_length()); -// m_grid_normalizationLayerType.attach(text_normalizationLayerNormalizeVariance,2,11,1,1); - text_normalizationLayerNormalizeVariance.show(); - - label_normalizationLayerEps.set_text("Inner Parameter - eps: "); - label_normalizationLayerEps.set_line_wrap(); - label_normalizationLayerEps.set_justify(Gtk::JUSTIFY_FILL); -// m_grid_normalizationLayerType.attach(label_normalizationLayerEps,0,12,2,1); - label_normalizationLayerEps.show(); - - text_normalizationLayerEps.set_max_length(100); - text_normalizationLayerEps.set_text("100"); - text_normalizationLayerEps.select_region(0, text_normalizationLayerEps.get_text_length()); -// m_grid_normalizationLayerType.attach(text_normalizationLayerEps,2,12,1,1); - text_normalizationLayerEps.show(); - } - else - { - - label_normalizationLayerTop.hide(); - label_normalizationLayerBottom.hide(); - label_normalizationLayerName.hide(); - text_normalizationLayerTop.hide(); - text_normalizationLayerBottom.hide(); - text_normalizationLayerName.hide(); - label_normalizationLayerlocalSize.hide(); - text_normalizationLayerlocalSize.hide(); - label_normalizationLayerAlpha.hide(); - text_normalizationLayerAlpha.hide(); - label_normalizationLayerBeta.hide(); - text_normalizationLayerBeta.hide(); - label_normalizationLayerK.hide(); - text_normalizationLayerK.hide(); - label_normalizationLayerNormRegion.hide(); - rbutton_normalizationLayerLRNWithin.hide(); - rbutton_normalizationLayerLRNAcross.hide(); - label_normalizationLayerAcrossChannel.hide(); - text_normalizationLayerAcrossChannel.hide(); - label_normalizationLayerNormalizeVariance.hide(); - text_normalizationLayerNormalizeVariance.hide(); - label_normalizationLayerEps.hide(); - text_normalizationLayerEps.hide(); - } - - m_sw_normalizationLayerType.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); - m_grid_normalizationLayerType.show(); -// show_all_children(); - m_sw_normalizationLayerType.show(); -} diff --git a/detectors/include/od/detectors/global2D/training/Solver.h b/detectors/include/od/detectors/global2D/training/Solver.h deleted file mode 100644 index d628a143..00000000 --- a/detectors/include/od/detectors/global2D/training/Solver.h +++ /dev/null @@ -1,131 +0,0 @@ -#pragma once - -#include <gtkmm/grid.h> -#include <gtkmm/entry.h> -#include <gtkmm/button.h> -#include <gtkmm/radiobutton.h> -#include <gtkmm/messagedialog.h> -#include <gtkmm/window.h> -#include <gtkmm/scrolledwindow.h> - -class SolverProperties : public Gtk::Window -{ - public: - SolverProperties(); - virtual ~SolverProperties(); - Glib::ustring solverFileName; - - protected: - void on_button_clicked(Glib::ustring data); - - // Child widgets: - Gtk::Grid m_grid1; - Gtk::ScrolledWindow m_sw1; - Gtk::Button button_solverFileName, - button_trainNetworkFileName, - button_testNetworkFileName, - button_testIter, - button_testInterval, - button_averageLoss, - button_randomSample, - button_display, - button_snapshot, - button_debugInfo, - button_testComputeLoss, - button_snapshotPrefix, - button_maxIter, - button_type, - button_learningRatePolicy, - button_baseLearningRate, - button_gamma, - button_power, - button_stepSize, - button_stepSizeValue, - button_weightDecay, - button_momentum, - button_saveFile; - Gtk::Entry text_solverFileName, - text_trainNetworkFileName, - text_testNetworkFileName, - text_testIter, - text_testInterval, - text_averageLoss, - text_randomSample, - text_display, - text_snapshot, - text_snapshotPrefix, - text_maxIter, - text_learningRatePolicy, - text_baseLearningRate, - text_gamma, - text_power, - text_stepSize, - text_stepSizeValue, - text_weightDecay, - text_momentum; - Gtk::Label label_solverFileName, - label_trainNetworkFileType, - label_trainNetworkFileName, - label_enableTestNet, - label_testNetworkFileName, - label_enableValidationParameters, - label_testIter, - label_testInterval, - label_enableAverageLoss, - label_averageLoss, - label_enableRandomSample, - label_randomSample, - label_display, - label_enableDebugInfo, - label_snapshot, - label_enableTestComputeLoss, - label_snapshotPrefix, - label_maxIter, - label_type, - label_learningRatePolicy, - label_baseLearningRate, - label_gamma, - label_power, - label_stepSize, - label_stepSizeValue, - label_weightDecay, - label_momentum; - Gtk::RadioButton rbutton_trainNetworkFileType_net, rbutton_trainNetworkFileType_tt, - rbutton_enableTestNet_no, rbutton_enableTestNet_yes, - rbutton_enableValidationParameters_no, rbutton_enableValidationParameters_yes, - rbutton_enableAverageLoss_no, rbutton_enableAverageLoss_yes, - rbutton_enableRandomSample_no, rbutton_enableRandomSample_yes, - rbutton_enableDebugInfo_no, rbutton_enableDebugInfo_yes, - rbutton_enableTestComputeLoss_no, rbutton_enableTestComputeLoss_yes, - rbutton_typeSGD_yes, rbutton_typeAdadelta_yes, rbutton_typeAdagrad_yes, rbutton_typeAdam_yes, - rbutton_typeRMSProp_yes, rbutton_typeNesterov_yes, - rbutton_learningRatePolicyFixed_yes, rbutton_learningRatePolicyExp_yes, - rbutton_learningRatePolicyStep_yes, rbutton_learningRatePolicyInv_yes, - rbutton_learningRatePolicyMultistep_yes, rbutton_learningRatePolicyPoly_yes, - rbutton_learningRatePolicySigmoid_yes; - - private: - - Glib::ustring trainNetworkFileName; - Glib::ustring testNetworkFileName; - Glib::ustring testIter; - Glib::ustring testInterval; - Glib::ustring averageLoss; - Glib::ustring randomSample; - Glib::ustring display; - Glib::ustring debugInfo; - Glib::ustring snapshot; - Glib::ustring testComputeLoss; - Glib::ustring snapshotPrefix; - Glib::ustring maxIter; - Glib::ustring type; - Glib::ustring learningRatePolicy; - Glib::ustring baseLearningRate; - Glib::ustring gamma; - Glib::ustring power; - Glib::ustring stepSize; - Glib::ustring stepSizeValue; - Glib::ustring weightDecay; - Glib::ustring momentum; -}; - diff --git a/detectors/src/global2D/CMakeLists.txt b/detectors/src/global2D/CMakeLists.txt index 2bb038dd..bb667138 100644 --- a/detectors/src/global2D/CMakeLists.txt +++ b/detectors/src/global2D/CMakeLists.txt @@ -16,6 +16,7 @@ if(BUILD_GLOBAL_2D_DETECTION) "detection/CascadeDetectorImpl.cpp" ) +if(0) if(WITH_CAFFE AND Caffe_FOUND) set(SOURCES ${SOURCES} "detection/ConvClassification.cpp" @@ -37,6 +38,7 @@ if(BUILD_GLOBAL_2D_DETECTION) endif() endif() +endif(0) if(WITH_SVMLIGHT) set(SOURCES ${SOURCES} "training/HOGTrainer.cpp" "detection/HOGDetector.cpp") diff --git a/detectors/src/global2D/detection/ConvClassification.cpp b/detectors/src/global2D/detection/ConvClassification.cpp deleted file mode 100644 index 93068de3..00000000 --- a/detectors/src/global2D/detection/ConvClassification.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include "od/detectors/global2D/detection/ConvClassification.h" - -namespace od -{ - namespace g2d - { - - ConvClassification::ConvClassification(const std::string & trained_data_location): - Detector2D(trained_data_location){} - - void ConvClassification::setWeightModelFileLocation(const std::string & location) - { - weightModelFileLoaction = location; - } - - void ConvClassification::setNetworkModelFileLocation(const std::string & location) - { - networkFileLocation = location; - } - - void ConvClassification::setImageFileLocation(const std::string & location) - { - imageFileLocation = location; - } - - std::string ConvClassification::getWeightModelFileLocation() - { - std::cout << "Weight Model File Location = " << weightModelFileLoaction << std::endl; - return weightModelFileLoaction; - } - - std::string ConvClassification::getNetworkModelFileLocation() - { - std::cout << "Network Model File Location = " << networkFileLocation << std::endl; - return networkFileLocation; - } - - std::string ConvClassification::getImageFileLocation() - { - std::cout << "Image File Location = " << imageFileLocation << std::endl; - return imageFileLocation; - } - - void ConvClassification::setTestBlob(int numChannels, int imgHeight, int imgWidth) - { - if (!ReadImageToDatum(imageFileLocation, numChannels, imgHeight, imgWidth, &strucBlob)) - { - std::cout << "Image File Not Found" << std::endl; - exit(-1); - } - caffe::Blob<float>* dataBlob = new caffe::Blob<float>(1, strucBlob.channels(), strucBlob.height(), strucBlob.width()); - - protoBlob.set_num(1); - protoBlob.set_channels(strucBlob.channels()); - protoBlob.set_height(strucBlob.height()); - protoBlob.set_width(strucBlob.width()); - const int data_size = strucBlob.channels() * strucBlob.height() * strucBlob.width(); - int sizeStrucBlob = std::max<int>(strucBlob.data().size(), strucBlob.float_data_size()); - for(int i = 0; i < sizeStrucBlob; ++i) - { - protoBlob.add_data(0.); - } - const std::string & data = strucBlob.data(); - if(data.size() != 0) - { - for (int i = 0; i < sizeStrucBlob; ++i) - { - protoBlob.set_data(i, protoBlob.data(i) + (uint8_t)data[i]); - } - } - - dataBlob->FromProto(protoBlob); - inputBlob.push_back(dataBlob); - - } - - void ConvClassification::classify() - { -#if(WITH_GPU) - caffe::Caffe::SetDevice(0); - caffe::Caffe::set_mode(caffe::Caffe::GPU); -#else - caffe::Caffe::set_mode(caffe::Caffe::CPU); -#endif - caffe::Net<float> net(networkFileLocation, caffe::TEST); - net.CopyTrainedLayersFrom(weightModelFileLoaction); - - float type = 0.0; - const std::vector<caffe::Blob<float>*>& result = net.Forward(inputBlob, &type); - float max = 0; - float max_i = 0; - for(int i = 0; i < 10; ++i) - { - float value = result[0]->cpu_data()[i]; - if (max < value) - { - max = value; - max_i = i; - } - } - std::cout << std::endl << std::endl << "****** OUTPUT *******" << std::endl; - std::cout << "classified image is digit " << max_i << std::endl << std::endl; - } - - void ConvClassification::init() - { - } - - shared_ptr<Detections2D> ConvClassification::detectOmni(shared_ptr<SceneImage> scene) - { - return make_shared<Detections2D>(); - } - - shared_ptr<Detections> ConvClassification::detect(shared_ptr<SceneImage> scene) - { - return make_shared<Detections>(); - } - - shared_ptr<Detections> ConvClassification::detectOmni(shared_ptr<Scene> scene) - { - std::cout << "not implemented, use with shared_ptr<SceneImage>" <<std::endl; - return nullptr; - } - - - - } -} diff --git a/detectors/src/global2D/localization/SelectiveSearchBase.cpp b/detectors/src/global2D/localization/SelectiveSearchBase.cpp deleted file mode 100644 index e8bc35b4..00000000 --- a/detectors/src/global2D/localization/SelectiveSearchBase.cpp +++ /dev/null @@ -1,145 +0,0 @@ -#include "od/detectors/global2D/localization/SelectiveSearchBase.h" - -namespace od -{ - namespace g2d - { - - SelectiveSearchBase::SelectiveSearchBase(const std::string & trained_data_location): - Detector2D(trained_data_location){} - - void SelectiveSearchBase::acquireImages(const std::string & imageLocation, int imgWidth, int imgHeight) - { - inputImageHeight = imgHeight; - inputImageWidth = imgWidth; - img = cv::imread(imageLocation, CV_LOAD_IMAGE_COLOR); - cv::Size size(imgWidth, imgHeight); - cv::resize(img, img, size); - cluster = cv::imread(imageLocation, CV_LOAD_IMAGE_COLOR); - cv::resize(cluster, cluster, size); - outputImg = cv::imread(imageLocation, CV_LOAD_IMAGE_COLOR); - cv::resize(outputImg, outputImg, size); - } - - - cv::Mat SelectiveSearchBase::preProcessImg(const cv::Mat & image) - { - std::vector<cv::Mat> channels; - cv::Mat img_hist_equalized; - cv::cvtColor(image, img_hist_equalized, CV_BGR2YCrCb); - cv::split(img_hist_equalized,channels); - cv::equalizeHist(channels[0], channels[0]); - cv::merge(channels,img_hist_equalized); - cv::cvtColor(img_hist_equalized, img_hist_equalized, CV_YCrCb2BGR); - return img_hist_equalized; - } - - std::vector<std::vector<int> > SelectiveSearchBase::getSuperPixels(const cv::Mat & im, int & totalMasks, float Sigma, float K, - float Min_size, const std::string & imageLocation) - { - std::string img_location(imageLocation + std::string("img.ppm")); - imwrite(img_location, im); - float sigma = Sigma; - float k = K; - int min_size = Min_size; - image<rgb> *input = loadPPM(img_location.c_str()); - int num_ccs; - image<rgb> *seg = segment_image(input, sigma, k, min_size, &num_ccs); - image<uchar> *gr = imageRGBtoGRAY(seg); - int num = imRef(seg,0,0).r; - - std::ofstream myfile; - myfile.open("segmented.txt"); - for(int R = 0; R < seg->height(); R++) - { - for(int C = 0; C < seg->width(); C++) - { - int numR, numG, numB, numGr; - numR = imRef(seg,C,R).r; - numG = imRef(seg,C,R).g; - numB = imRef(seg,C,R).b; - numGr = imRef(gr,C,R); - myfile << numR << " " << numG << " " << numB << " " << numGr << std::endl; - } - } - std::string output_location("../images/img.ppm"); - savePPM(seg, output_location.c_str()); - myfile.close(); - - int H = 0; - int W = 0; - num = im.rows*im.cols; - std::vector<std::vector<int> > mask; - mask.resize(im.rows, std::vector<int>(im.cols, 0)); - int maskValue = 0; - int v = 0; - - std::ifstream infile("segmented.txt"); - int R,G,B,GR; - - std::vector<std::vector<int> > mem; - mem.resize(num, std::vector<int>(3, 0)); - std::vector<int> val; - val.resize(num, 0); - int insertion_mem_index = 1; - std::vector<int> query = {R, G, B}; - - for(int i = 0; i < num; i++) - { - infile >> R >> G >> B >> GR; - if(i==0) - { - mem[i][0] = R; - mem[i][1] = G; - mem[i][2] = B; - val[i] = maskValue; - mask[H][W] = val[0]; - } - else - { - auto pos = find(mem.begin(), mem.end(), query); - if(pos != mem.end()) - { - mask[H][W] = val[pos-mem.begin()]; - } - else - { - mem[insertion_mem_index][0] = R; - mem[insertion_mem_index][1] = G; - mem[insertion_mem_index][2] = B; - maskValue++; - insertion_mem_index++; - val[insertion_mem_index] = maskValue; - mask[H][W] = maskValue; - } - } - W = W + 1; - if(W == im.cols) - { - H = H + 1; - W = 0; - } - } - - totalMasks = maskValue; - return mask; - } - - - void SelectiveSearchBase::init() - { - } - - shared_ptr<Detections2D> SelectiveSearchBase::detectOmni(shared_ptr<SceneImage> scene) - { - return make_shared<Detections2D>(); - } - - shared_ptr<Detections> SelectiveSearchBase::detect(shared_ptr<SceneImage> scene) - { - return make_shared<Detections>(); - } - - } -} - diff --git a/detectors/src/global2D/localization/SelectiveSearchModel.cpp b/detectors/src/global2D/localization/SelectiveSearchModel.cpp deleted file mode 100644 index 3e392f72..00000000 --- a/detectors/src/global2D/localization/SelectiveSearchModel.cpp +++ /dev/null @@ -1,604 +0,0 @@ -#include "od/detectors/global2D/localization/SelectiveSearchModel.h" - -namespace od -{ - namespace g2d - { - SelectiveSearchModel::SelectiveSearchModel(const std::string & trained_data_location): - Detector2D(trained_data_location_){} - - void SelectiveSearchModel::setLabel(int l) - { - label = l; - } - - void SelectiveSearchModel::init() - { - } - - shared_ptr<Detections2D> SelectiveSearchModel::detectOmni(shared_ptr<SceneImage> scene) - { - return make_shared<Detections2D>(); - } - - shared_ptr<Detections> SelectiveSearchModel::detect(shared_ptr<SceneImage> scene) - { - return make_shared<Detections>(); - } - - shared_ptr<Detections> SelectiveSearchModel::detectOmni(shared_ptr<Scene> scene) - { - std::cout << "not implemented, use with shared_ptr<SceneImage>" <<std::endl; - return nullptr; - } - - cv::Mat get_hess_hist_xx(const cv::Mat & regionMask, int histSize, float hist_range_min, float hist_range_max) - { - cv::Mat kernel_filter_1(7,7, CV_32FC1,1); - kernel_filter_1 = (cv::Mat_<double>(7,7) << 1.57130243e-04, 7.17839338e-04, 0, -1.76805171e-03, 0, 7.17839338e-04, 1.57130243e-04, - 1.91423823e-03, 8.74507340e-03, 0, -2.15392793e-02, 0, 8.74507340e-03, 1.91423823e-03, - 8.57902057e-03, 3.91926999e-02, 0, -9.65323526e-02, 0, 3.91926999e-02, 8.57902057e-03, - 1.41444137e-02, 6.46178379e-02, 0, -1.59154943e-01, 0, 6.46178379e-02, 1.41444137e-02, - 8.57902057e-03, 3.91926999e-02, 0, -9.65323526e-02, 0, 3.91926999e-02, 8.57902057e-03, - 1.91423823e-03, 8.74507340e-03, 0, -2.15392793e-02, 0, 8.74507340e-03, 1.91423823e-03, - 1.57130243e-04, 7.17839338e-04, 0, -1.76805171e-03, 0, 7.17839338e-04, 1.57130243e-04 - ); - cv::Mat image_filtered_xx; - cv::filter2D(regionMask, image_filtered_xx, -1, kernel_filter_1, cv::Point(-1,-1), 0, cv::BORDER_DEFAULT ); - cv::Mat xx_hist; - float range[] = { hist_range_min, hist_range_max } ; - const float * histRange = { range }; - bool uniform = true; - bool accumulate = false; - cv::calcHist(&image_filtered_xx, 1, 0, cv::Mat(), xx_hist, 1, &histSize, &histRange, uniform, accumulate ); - return xx_hist; - } - - cv::Mat get_hess_hist_xy(const cv::Mat & regionMask, int histSize, float hist_range_min, float hist_range_max) - { - cv::Mat kernel_filter_2(7,7, CV_32FC1,1); - kernel_filter_2 = (cv::Mat_<double>(7,7) << 0.00017677, 0.00143568, 0.00321713, 0, -0.00321713, -0.00143568, -0.00017677, - 0.00143568, 0.0116601, 0.02612847, 0, -0.02612847, -0.0116601, -0.00143568, - 0.00321713, 0.02612847, 0.05854983, 0, -0.05854983, -0.02612847, -0.00321713, - 0, 0, 0, 0, 0, 0, 0, - -0.00321713, -0.02612847, -0.05854983, 0, 0.05854983, 0.02612847, 0.00321713, - -0.00143568, -0.0116601, -0.02612847, 0, 0.02612847, 0.0116601, 0.00143568, - -0.00017677, -0.00143568, -0.00321713, 0, 0.00321713, 0.00143568, 0.00017677 - ); - cv::Mat image_filtered_xy; - cv::filter2D(regionMask, image_filtered_xy, -1, kernel_filter_2, cv::Point(-1,-1), 0, cv::BORDER_DEFAULT ); - cv::Mat xy_hist; - float range[] = { hist_range_min, hist_range_max } ; - const float * histRange = { range }; - bool uniform = true; bool accumulate = false; - cv::calcHist( &image_filtered_xy, 1, 0, cv::Mat(), xy_hist, 1, &histSize, &histRange, uniform, accumulate ); - return xy_hist; - } - - - - cv::Mat get_hess_hist_yy(const cv::Mat & regionMask, int histSize, float hist_range_min, float hist_range_max) - { - cv::Mat kernel_filter_3(7,7, CV_32FC1,1); - kernel_filter_3 = (cv::Mat_<double>(7,7) << - 1.57130243e-04, 1.91423823e-03, 8.57902057e-03, 1.41444137e-02, 8.57902057e-03, 1.91423823e-03, 1.57130243e-04, - 7.17839338e-04, 8.74507340e-03, 3.91926999e-02, 6.46178379e-02, 3.91926999e-02, 8.74507340e-03, 7.17839338e-04, - 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, - -1.76805171e-03, -2.15392793e-02, -9.65323526e-02, -1.59154943e-01, -9.65323526e-02, -2.15392793e-02, -1.76805171e-03, - 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, - 7.17839338e-04, 8.74507340e-03, 3.91926999e-02, 6.46178379e-02, 3.91926999e-02, 8.74507340e-03, 7.17839338e-04, - 1.57130243e-04, 1.91423823e-03, 8.57902057e-03, 1.41444137e-02, 8.57902057e-03, 1.91423823e-03, 1.57130243e-04 - ); - cv::Mat image_filtered_yy; - cv::filter2D(regionMask, image_filtered_yy, -1, kernel_filter_3, cv::Point(-1,-1), 0, cv::BORDER_DEFAULT ); - cv::Mat yy_hist; - float range[] = { hist_range_min, hist_range_max } ; - const float * histRange = { range }; - bool uniform = true; - bool accumulate = false; - - cv::calcHist(&image_filtered_yy, 1, 0, cv::Mat(), yy_hist, 1, &histSize, &histRange, uniform, accumulate ); - return yy_hist; - } - - cv::Mat get_orientation_hist(const cv::Mat & regionMask, int histSize, float hist_range_min, float hist_range_max) - { - cv::Mat kernel_filter_1(3,3, CV_32FC1,1); - kernel_filter_1 = (cv::Mat_<double>(3,3) << 1, 1, 1, 1, -8, 1, 1, 1, 1); - cv::Mat kernel_filter_2(3,3, CV_32FC1,1); - kernel_filter_2 = (cv::Mat_<double>(3,3) << 0,0,0,0,1,0,0,0,0); - cv::Mat kernel_filter_3(3,3, CV_32FC1,1); - kernel_filter_3 = (cv::Mat_<double>(3,3) << 1,2,1,0,0,0,-1,-2,-1); - cv::Mat kernel_filter_4(3,3, CV_32FC1,1); - kernel_filter_4 = (cv::Mat_<double>(3,3) << 1,0,-1,2,0,-2,1,0,-1); - cv::Mat image_filtered_v1; - cv::Mat image_filtered_v2; - cv::Mat image_filtered_v3; - cv::Mat image_filtered_v4; - int temp30; - //filtering - cv::filter2D(regionMask, image_filtered_v1, -1, kernel_filter_1, cv::Point(-1,-1), 0, cv::BORDER_DEFAULT ); - cv::filter2D(regionMask, image_filtered_v2, -1, kernel_filter_2, cv::Point(-1,-1), 0, cv::BORDER_DEFAULT ); - cv::filter2D(regionMask, image_filtered_v3, -1, kernel_filter_3, cv::Point(-1,-1), 0, cv::BORDER_DEFAULT ); - cv::filter2D(regionMask, image_filtered_v4, -1, kernel_filter_4, cv::Point(-1,-1), 0, cv::BORDER_DEFAULT ); - - //Orientation New - float temp_5; - float temp_6; - float temp_7_theta; - float temp_8_theta_dash; - int temp_9_theta_dash_quantized; - float quantized_1[12] = {0,1,2,3,4,5,6,7,8,9,10,11}; - int quantized_count_1 = 0; - float orientation_image_matrix[regionMask.rows][regionMask.cols]; - cv::Mat orientation_image = regionMask.clone(); - for(int r = 0; r < regionMask.rows; r++) - { - for(int c = 0; c < regionMask.cols; c++) - { - orientation_image_matrix[r][c] = 0; - } - } - for(int r = 0; r < regionMask.rows; r++) - { - for(int c = 0; c < regionMask.cols; c++) - { - temp_5 = image_filtered_v3.at<schar>(r,c); - temp_6 = image_filtered_v4.at<schar>(r,c); - if(temp_6 != 0 && temp_5 != 0) - { - temp_7_theta = atan(temp_5/temp_6); - } - else if(temp_6 == 0 && temp_5 > 0) - { - temp_7_theta = M_PI/2; - } - else if(temp_6 == 0 && temp_5 < 0) - { - temp_7_theta = -M_PI/2; - } - else if(temp_6 == 0 && temp_5 == 0) - { - temp_7_theta = 0; - } - else if(temp_6 != 0 && temp_5 == 0) - { - temp_7_theta = 0; - } - - if(temp_5 >= 0 && temp_6 >= 0) - { - temp_8_theta_dash = temp_7_theta; - } - else if(temp_5 < 0 && temp_6 >= 0) - { - temp_8_theta_dash = temp_7_theta + M_PI; - } - else if(temp_5 < 0 && temp_6 < 0) - { - temp_8_theta_dash = temp_7_theta + M_PI; - } - else if(temp_5 >= 0 && temp_6 < 0) - { - temp_8_theta_dash = temp_7_theta + 2*M_PI; - } - temp_9_theta_dash_quantized = floor((temp_8_theta_dash*11)/(2*M_PI)); - orientation_image_matrix[r][c] = temp_9_theta_dash_quantized; - orientation_image.at<uchar>(r,c) = temp_9_theta_dash_quantized; - } - } - /* for(int r = 0; r < img.rows; r++) - { - for(int c = 0; c < img.cols; c++) - { - cout << orientation_image_matrix[r][c] << " "; - } - cout << endl; - } - */ - float range[] = { hist_range_min, hist_range_max } ; - const float * histRange = { range }; - bool uniform = true; bool accumulate = false; - cv::Mat orientation_image_hist; - - /// Compute the histograms: - cv::calcHist( &orientation_image, 1, 0, cv::Mat(), orientation_image_hist, 1, &histSize, &histRange, uniform, accumulate ); - return orientation_image_hist; - } - - cv::Mat get_diff_exci_hist(const cv::Mat & regionMask, int histSize, float hist_range_min, float hist_range_max) - { - cv::Mat kernel_filter_1(3,3, CV_32FC1,1); - kernel_filter_1 = (cv::Mat_<double>(3,3) << 1, 1, 1, 1, -8, 1, 1, 1, 1); - cv::Mat kernel_filter_2(3,3, CV_32FC1,1); - kernel_filter_2 = (cv::Mat_<double>(3,3) << 0,0,0,0,1,0,0,0,0); - cv::Mat kernel_filter_3(3,3, CV_32FC1,1); - kernel_filter_3 = (cv::Mat_<double>(3,3) << 1,2,1,0,0,0,-1,-2,-1); - cv::Mat kernel_filter_4(3,3, CV_32FC1,1); - kernel_filter_4 = (cv::Mat_<double>(3,3) << 1,0,-1,2,0,-2,1,0,-1); - cv::Mat image_filtered_v1; - cv::Mat image_filtered_v2; - cv::Mat image_filtered_v3; - cv::Mat image_filtered_v4; - int temp30; - //filtering - cv::filter2D(regionMask, image_filtered_v1, -1, kernel_filter_1, cv::Point(-1,-1), 0, cv::BORDER_DEFAULT ); - cv::filter2D(regionMask, image_filtered_v2, -1, kernel_filter_2, cv::Point(-1,-1), 0, cv::BORDER_DEFAULT ); - cv::filter2D(regionMask, image_filtered_v3, -1, kernel_filter_3, cv::Point(-1,-1), 0, cv::BORDER_DEFAULT ); - cv::filter2D(regionMask, image_filtered_v4, -1, kernel_filter_4, cv::Point(-1,-1), 0, cv::BORDER_DEFAULT ); - - //Differential Excitation New - float temp_1; - float temp_2; - float temp_3_alpha; - float temp_4_quantized_alpha; - int quantized[8] = {0,1,2,3,4,5,6,7}; - int quantized_count = 0; - int differential_excitation_image_matrix[regionMask.rows][regionMask.cols]; - cv::Mat differential_excitation_image = regionMask.clone(); - - for(int r = 0; r < regionMask.rows; r++) - { - for(int c = 0; c < regionMask.cols; c++) - { - differential_excitation_image_matrix[r][c] = 0; - } - } - for(int r = 0; r < regionMask.rows; r++) - { - for(int c = 0; c < regionMask.cols; c++) - { - temp_1 = image_filtered_v1.at<schar>(r,c); - temp_2 = image_filtered_v2.at<schar>(r,c); - if(temp_2 != 0) - { - temp_3_alpha = atan(temp_1/temp_2); - } - else if(temp_2 == 0 && temp_1 > 0) - { - temp_3_alpha = M_PI/2; - } - else if(temp_2 == 0 && temp_1 < 0) - { - temp_3_alpha = -M_PI/2; - } - else if(temp_2 == 0 && temp_1 == 0) - { - temp_3_alpha = 0; - } - temp_4_quantized_alpha = floor(((temp_3_alpha + M_PI/2)/M_PI)*7); - differential_excitation_image_matrix[r][c] = temp_4_quantized_alpha; - differential_excitation_image.at<uchar>(r,c) = temp_4_quantized_alpha; - } - } - /* for(int r = 0; r < img.rows; r++) - { - for(int c = 0; c < img.cols; c++) - { - cout << differential_excitation_image_matrix[r][c] << " "; - } - cout << endl; - } - */ - float range[] = { hist_range_min, hist_range_max } ; - const float* histRange = { range }; - bool uniform = true; - bool accumulate = false; - cv::Mat differential_excitation_hist; - - /// Compute the histograms: - cv::calcHist(&differential_excitation_image, 1, 0, cv::Mat(), differential_excitation_hist, 1, - &histSize, &histRange, uniform, accumulate ); - return differential_excitation_hist; - } - - void refineRegions(std::vector<std::vector<int> > sp, int total_masks, SelectiveSearchModel regions[], int min_height, int min_width) - { - for(int i = 0; i < total_masks; i++) - { - regions[i].setLabel(i); - regions[i].min_x = 100000; - regions[i].min_y = 100000; - regions[i].max_x = -1; - regions[i].max_y = -1; - } - for(int r = 0; r < sp.size(); r++) - { - for(int c = 0; c < sp[0].size(); c++) - { - regions[sp[r][c]].size++; - if(regions[sp[r][c]].min_x > c) - regions[sp[r][c]].min_x = c; - if(regions[sp[r][c]].min_y > r) - regions[sp[r][c]].min_y = r; - if(regions[sp[r][c]].max_x < c) - regions[sp[r][c]].max_x = c; - if(regions[sp[r][c]].max_y < r) - regions[sp[r][c]].max_y = r; - } - } - for(int i = 0; i < total_masks; i++) - { - if((regions[i].max_x - regions[i].min_x > min_width) && (regions[i].max_y - regions[i].min_y > min_height)) - regions[i].validity = true; - else - regions[i].validity = false; - } - } - - - - void createModel(std::vector<std::vector<int> > sp, int total_masks, const cv::Mat & grayMask, SelectiveSearchModel regions[], int histSize, - float hist_range_min, float hist_range_max) - { - cv::Mat regionMask = grayMask.clone(); - - for(int i = 0; i < total_masks; i++) - { - regionMask = grayMask.clone(); - // cout << i << endl; - for(int r = 0; r < sp.size(); r++) - { - for(int c = 0; c < sp[0].size(); c++) - { - if(sp[r][c] != i) - regionMask.at<uchar>(r,c) = 0; - } - } - if(regions[i].size<200) - regions[i].validity = false; - // cout << i << " hessian" << endl; - //Hessian Matrix - regions[i].xx_hist = get_hess_hist_xx(regionMask, histSize, hist_range_min, hist_range_max); - regions[i].xy_hist = get_hess_hist_xy(regionMask, histSize, hist_range_min, hist_range_max); - regions[i].yy_hist = get_hess_hist_yy(regionMask, histSize, hist_range_min, hist_range_max); - - // cout << i << " orien" << endl; - //orientation Matrix - regions[i].orientation_image_hist = get_orientation_hist(regionMask, histSize, hist_range_min, hist_range_max); - - // cout << i << " diff" << endl; - //Differential Excitation Matrix - regions[i].differential_excitation_hist = get_diff_exci_hist(regionMask, histSize, hist_range_min, hist_range_max); - - // cout << i << " color" << endl; - //Color Histogram - float range[] = {hist_range_min, hist_range_max}; - const float* histRange = {range}; - bool uniform = true; - bool accumulate = false; - cv::calcHist(&grayMask, 1, 0, cv::Mat(), regions[i].color_hist, 1, &histSize, &histRange, uniform, accumulate); - - - } - } - - bool checkNeighbors(const SelectiveSearchModel & a, const SelectiveSearchModel & b) - { - if( - ((a.min_x < b.min_x) && (b.min_x < a.max_x) && (a.min_y < b.min_y) && (b.min_y < a.max_y)) - || - ((a.min_x < b.max_x) && (b.max_x < a.max_x) && (a.min_y < b.max_y) && (b.max_y < a.max_y)) - || - ((a.min_x < b.min_x) && (b.min_x < a.max_x) && (a.min_y < b.max_y) && (b.max_y < a.max_y)) - || - ((a.min_x < b.max_x) && (b.min_x < a.max_x) && (a.min_y < b.max_y) && (b.max_y < a.max_y)) - ) - { - - return true; - } - - return false; - } - - std::vector<std::vector<int> > findNeighbors(SelectiveSearchModel regions[], int total_masks, const cv::Mat & regionMask, - const std::vector<std::vector<int> > & sp) - { - std::vector<std::vector<int> > neighbors; - std::vector<int> rows; - rows.push_back(0); - rows.push_back(0); - int num = 0; - for(int i = 1; i < total_masks-1; i++) - { - for(int j = i+1; j < i+20; j++) - { - if(j < total_masks-2) - { - if(checkNeighbors(regions[i], regions[j]) && regions[i].validity && regions[j].validity) - { - /* cout << i << " " << j << endl; - cout << regions[i].min_x << " " << regions[i].min_y << " " << regions[i].max_x << " " << regions[i].max_y << endl; - cout << regions[j].min_x << " " << regions[j].min_y << " " << regions[j].max_x << " " << regions[j].max_y << endl; - for (int r = 0; r < regionMask.rows; r++) - { - for(int c = 0; c < regionMask.cols; c++) - { - if(sp[r][c] != i) - { - regionMask.at<Vec3b>(r,c)[0] = 0; - regionMask.at<Vec3b>(r,c)[1] = 0; - regionMask.at<Vec3b>(r,c)[2] = 0; - } - else - { - regionMask.at<Vec3b>(r,c)[0] = 0; - regionMask.at<Vec3b>(r,c)[1] = 0; - regionMask.at<Vec3b>(r,c)[2] = 255; - } - } - } - for (int r = 0; r < regionMask.rows; r++) - { - for(int c = 0; c < regionMask.cols; c++) - { - if(sp[r][c] != j and regionMask.at<Vec3b>(r,c)[2] != 255) - { - regionMask.at<Vec3b>(r,c)[0] = 0; - regionMask.at<Vec3b>(r,c)[1] = 0; - regionMask.at<Vec3b>(r,c)[2] = 0; - } - else - { - regionMask.at<Vec3b>(r,c)[0] = 0; - regionMask.at<Vec3b>(r,c)[1] = 255; - regionMask.at<Vec3b>(r,c)[2] = 0; - } - } - } - imshow("regionMask", regionMask); - waitKey(0); - */ - rows[0] = i; - rows[1] = j; - neighbors.push_back(rows); - } - } - } - } - return neighbors; - } - - float calcSimilarities(const SelectiveSearchModel & a, const SelectiveSearchModel & b, float spSize) - { - double sim = 0.0; - sim += compareHist( a.xx_hist, b.xx_hist, 1); - sim += compareHist( a.xy_hist, b.xy_hist, 1); - sim += compareHist( a.yy_hist, b.yy_hist, 1); - sim += compareHist( a.orientation_image_hist, b.orientation_image_hist, 1); - sim += compareHist( a.differential_excitation_hist, b.differential_excitation_hist, 1); - sim += compareHist( a.color_hist, b.color_hist, 1); - sim += 100 * ((a.size + b.size)/spSize); - double bbsize = ((std::max(a.max_x, b.max_x) - std::min(a.min_x, b.min_x)) * (std::max(a.max_y, b.max_y) - std::min(a.min_y, b.min_y))); - sim += 100 * ((bbsize - a.size - b.size) / spSize); - return sim; - } - - void mergeRegions(int value, SelectiveSearchModel regions[], const std::vector<std::vector<int> > & sp_neighbors, const cv::Mat &grayMask, - const std::vector<std::vector<int> > & sp, int minRegionSize, int histSize, float hist_range_min, float hist_range_max) - { - /* - void setBoundaries(int ix, int iy, int ax, int ay); - int label; - int min_x; - int max_x; - int min_y; - int max_y; - int size; - Mat xx_hist, xy_hist, yy_hist, orientation_image_hist,differential_excitation_hist, color_hist; - vector <int> neighbors; - bool validity; - */ - regions[sp_neighbors[value][0]].validity = true; - regions[sp_neighbors[value][1]].validity = false; - regions[sp_neighbors[value][0]].min_x = std::min(regions[sp_neighbors[value][0]].min_x, regions[sp_neighbors[value][1]].min_x); - regions[sp_neighbors[value][0]].max_y = std::max(regions[sp_neighbors[value][0]].max_y, regions[sp_neighbors[value][1]].max_y); - regions[sp_neighbors[value][0]].min_x = std::min(regions[sp_neighbors[value][0]].min_x, regions[sp_neighbors[value][1]].min_x); - regions[sp_neighbors[value][0]].max_y = std::max(regions[sp_neighbors[value][0]].max_y, regions[sp_neighbors[value][1]].max_y); - regions[sp_neighbors[value][0]].size = regions[sp_neighbors[value][0]].size + regions[sp_neighbors[value][1]].size; - - cv::Mat regionMask = grayMask.clone(); - int i = sp_neighbors[value][0]; - for(int r = 0; r < sp.size(); r++) - { - for(int c = 0; c < sp[0].size(); c++) - { - if(sp[r][c] != i) - regionMask.at<uchar>(r,c) = 0; - } - } - if(regions[i].size<minRegionSize) - regions[i].validity = false; - // cout << i << " hessian " << regionMask.channels() << endl; - //Hessian Matrix - regions[i].xx_hist = get_hess_hist_xx(regionMask, histSize, hist_range_min, hist_range_max); - regions[i].xy_hist = get_hess_hist_xy(regionMask, histSize, hist_range_min, hist_range_max); - regions[i].yy_hist = get_hess_hist_yy(regionMask, histSize, hist_range_min, hist_range_max); - - // cout << i << " orien" << endl; - //orientation Matrix - regions[i].orientation_image_hist = get_orientation_hist(regionMask, histSize, hist_range_min, hist_range_max); - - // cout << i << " diff" << endl; - //Differential Excitation Matrix - regions[i].differential_excitation_hist = get_diff_exci_hist(regionMask, histSize, hist_range_min, hist_range_max); - - // cout << i << " color" << endl; - //Color Histogram - float range[] = { hist_range_min, hist_range_max } ; - const float* histRange = { range }; - bool uniform = true; - bool accumulate = false; - cv::calcHist(&grayMask, 1, 0, cv::Mat(), regions[i].color_hist, 1, &histSize, &histRange, uniform, accumulate); - } - - bool checkRounds(int totals, SelectiveSearchModel regions[], int numRounds) - { - int num = 0; - for(int i = 0; i < totals; i++) - { - if(regions[i].validity == false) - { - num++; - } - } - // cout << "num = " << num << endl; - if(num > numRounds) - return true; - else - return false; - } - - std::vector<std::vector<int> > extractROIs(int total_masks, SelectiveSearchModel regions[], int numRounds, float spSize, - const std::vector<std::vector<int> > & sp, const cv::Mat & img, const cv::Mat & gray_mask, - int minRegionSize, int histSize, float hist_range_min, float hist_range_max) - { - std::vector<std::vector<int> > pts; - int totals = total_masks; - int value = 10; - std::vector<std::vector<int> > sp_neighbors; - - while(checkRounds(totals, regions, numRounds) && value > 0) - { - sp_neighbors = findNeighbors(regions, total_masks, img, sp); - std::vector<float> similarities; - for(int i = 0; i < sp_neighbors.size(); i++) - { - - float sim = calcSimilarities(regions[sp_neighbors[i][0]],regions[sp_neighbors[i][1]], spSize); - // cout << i << " " << sp_neighbors[i][0] << " " << sp_neighbors[i][1] << " " << sim << endl; - similarities.push_back(sim); - } - - //finding closest two regions - value = min_element(similarities.begin(), similarities.end()) - similarities.begin(); - // cout << "in here" << endl; - - //merging - mergeRegions(value, regions, sp_neighbors, gray_mask, sp, minRegionSize, histSize, hist_range_min, hist_range_max); - // cout << "value = " << value << endl; - for(int i = 0; i < total_masks; i++) - { - if(regions[i].validity == true) - { - std::vector<int> temp; - temp.push_back(regions[i].min_x); - temp.push_back(regions[i].min_y); - temp.push_back(regions[i].max_x); - temp.push_back(regions[i].max_y); - pts.push_back(temp); - // rectangle(outputImg, Point(regions[i].min_x, regions[i].min_y), Point(regions[i].max_x, regions[i].max_y), Scalar(0, 0, 255)); - } - } - } - std::sort(pts.begin(), pts.end()); - pts.erase(std::unique(pts.begin(), pts.end()), pts.end()); - std::cout << "Total bounding boxes = " << pts.size() << std::endl; - return pts; - } - - } -} - - diff --git a/detectors/src/global2D/training/ConvTrainer.cpp b/detectors/src/global2D/training/ConvTrainer.cpp deleted file mode 100644 index e60bf9be..00000000 --- a/detectors/src/global2D/training/ConvTrainer.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "od/detectors/global2D/training/ConvTrainer.h" - -namespace od -{ - namespace g2d - { - - ConvTrainer::ConvTrainer(const std::string & training_input_location, - const std::string & trained_data_location): - Trainer(training_input_location, trained_data_location){} - - int ConvTrainer::train() - { - return 1; - } - - void ConvTrainer::setSolverLocation(const std::string & location) - { - solverLocation = location; - } - - void ConvTrainer::setSolverProperties(int argc, char *argv[]) - { - auto app = Gtk::Application::create(argc, argv, std::string("org.gtkmm.example")); - SolverProperties solverProperties; - solverProperties.set_default_geometry(10000, 10000); - app->run(solverProperties); - solverLocation = solverProperties.solverFileName; - } - - void ConvTrainer::createCustomNetwork(int argc, char *argv[]) - { - auto app = Gtk::Application::create(argc, argv, std::string("org.gtkmm.example")); - NetworkCreator networkCreator; - networkCreator.set_default_geometry(10000, 10000); - app->run(networkCreator); - } - - void ConvTrainer::startTraining() - { -#if(WITH_GPU) - caffe::Caffe::SetDevice(0); - caffe::Caffe::set_mode(caffe::Caffe::GPU); -#else - caffe::Caffe::set_mode(caffe::Caffe::CPU); -#endif - caffe::SGDSolver<float> s(solverLocation); - s.Solve(); - } - - - } -} diff --git a/detectors/src/global2D/training/Network.cpp b/detectors/src/global2D/training/Network.cpp deleted file mode 100644 index 927ba7b2..00000000 --- a/detectors/src/global2D/training/Network.cpp +++ /dev/null @@ -1,1667 +0,0 @@ -#include "od/detectors/global2D/training/Network.h" -#include "od/detectors/global2D/training/MainWindow.h" -#include "od/detectors/global2D/training/ActivationWindow.h" -#include "od/detectors/global2D/training/DisplayWindow.h" -#include "od/detectors/global2D/training/CriticalWindow.h" -#include "od/detectors/global2D/training/NormalizationWindow.h" -#include "od/detectors/global2D/training/LossWindow.h" -#include "od/detectors/global2D/training/ExtraWindow.h" -#include "od/detectors/global2D/training/Node.h" - -struct Node * newHead; -struct Node * headLayer = new Node; - -NetworkCreator::NetworkCreator(): - label_networkFileName(""), - button_networkFileName("Update"), - text_networkFileName(), - - button_activationLayerType("Append Activation Layer"), - label_activationLayerType(""), - - button_criticalLayerType("Append Crtitical Layer"), - label_criticalLayerType(""), - - label_normalizationLayerType(""), - - label_lossLayerType(""), - - label_extraLayerType(""), - - button_setActivationParameters("Add This Layer"), - button_addMoreLayer("Add More Layers"), - text_activationLayerTop(), - text_activationLayerBottom(), - text_activationLayerName(), - text_activationLayerType(), - text_activationLayerScale(), - text_activationLayerShift(), - text_activationLayerBase(), - text_activationLayerNegativeSlope(), - label_activationLayerTop(""), - label_activationLayerBottom(""), - label_activationLayerName(""), - label_activationLayerScale(""), - label_activationLayerShift(""), - label_activationLayerBase(""), - label_activationLayerNegativeSlope(""), - - button_setCriticalParameters("Add This Layer"), - button_addMoreLayer2("Add more Layers"), - label_criticalLayerTop(""), - label_criticalLayerBottom1(""), - label_criticalLayerBottom2(""), - label_criticalLayerName(""), - label_criticalLayerFilterLr(""), - label_criticalLayerFilterDm(""), - label_criticalLayerBiasLr(""), - label_criticalLayerBiasDm(""), - label_criticalLayerNumOutput(""), - label_criticalLayerKernelW(""), - label_criticalLayerKernelH(""), - label_criticalLayerStrideW(""), - label_criticalLayerStrideH(""), - label_criticalLayerPadW(""), - label_criticalLayerPadH(""), - label_criticalLayerWeightFiller(""), - label_criticalLayerWeightFillerConstantValue(""), - label_criticalLayerWeightFillerUniformMin(""), - label_criticalLayerWeightFillerUniformMax(""), - label_criticalLayerWeightFillerGaussianMean(""), - label_criticalLayerWeightFillerGaussianStd(""), - label_criticalLayerWeightFillerXavierVariance(""), - label_criticalLayerWeightFillerMSRAVariance(""), - label_criticalLayerDropoutRatio(""), - label_criticalLayerPool(""), - text_criticalLayerTop(), - text_criticalLayerBottom1(), - text_criticalLayerBottom2(), - text_criticalLayerName(), - text_criticalLayerFilterLr(), - text_criticalLayerFilterDm(), - text_criticalLayerBiasLr(), - text_criticalLayerBiasDm(), - text_criticalLayerNumOutput(), - text_criticalLayerKernelW(), - text_criticalLayerKernelH(), - text_criticalLayerStrideW(), - text_criticalLayerStrideH(), - text_criticalLayerPadW(), - text_criticalLayerPadH(), - text_criticalLayerWeightFillerConstantValue(), - text_criticalLayerWeightFillerUniformMin(), - text_criticalLayerWeightFillerUniformMax(), - text_criticalLayerWeightFillerGaussianMean(), - text_criticalLayerWeightFillerGaussianStd(), - text_criticalLayerDropoutRatio(), - rbutton_criticalLayerWeightFillerConstant("Constant"), rbutton_criticalLayerWeightFillerUniform("Uniform"), - rbutton_criticalLayerWeightGaussian("Gaussian"), rbutton_criticalLayerWeightFillerPositiveUnitBall("Positive Unit Ball"), - rbutton_criticalLayerWeightFillerXavier("Xavier"), rbutton_criticalLayerWeightFillerMSRA("MSRA"), - rbutton_criticalLayerWeightFillerBilinear("Bilinear"), - rbutton_criticalLayerWeightFillerXavierIn("FAN_IN"), rbutton_criticalLayerWeightFillerXavierOut("FAN_OUT"), - rbutton_criticalLayerWeightFillerXavierAvg("AVERAGE"), - rbutton_criticalLayerWeightFillerMSRAIn("FAN_IN"), rbutton_criticalLayerWeightFillerMSRAOut("FAN_OUT"), - rbutton_criticalLayerWeightFillerMSRAAvg("AVERAGE"), - rbutton_criticalLayerPoolMax("MAX"), rbutton_criticalLayerPoolAve("AVE"), - - button_normalizationLayerType("Append Normalization Layer"), - button_lossLayerType("Add Loss Layer"), - button_extraLayerType("Append Layer"), - button_addMoreLayer3("Add More Layers"), - button_setNormalizationParameters("Add This Layer"), - text_normalizationLayerTop(), - text_normalizationLayerBottom(), - text_normalizationLayerName(), - label_normalizationLayerTop(""), - label_normalizationLayerBottom(""), - label_normalizationLayerName(""), - text_normalizationLayerlocalSize(), - label_normalizationLayerlocalSize(""), - text_normalizationLayerAlpha(), - text_normalizationLayerBeta(), - text_normalizationLayerK(), - label_normalizationLayerAlpha(""), - label_normalizationLayerBeta(""), - label_normalizationLayerK(""), - label_normalizationLayerNormRegion(""), - rbutton_normalizationLayerLRNWithin("WITHIN_CHANNEL"), rbutton_normalizationLayerLRNAcross("ACCROSS_CHANNEL"), - text_normalizationLayerAcrossChannel(), - text_normalizationLayerNormalizeVariance(), - text_normalizationLayerEps(), - label_normalizationLayerAcrossChannel(""), - label_normalizationLayerNormalizeVariance(""), - label_normalizationLayerEps(""), - text_lossLayerNormalize(), - label_lossLayerNormalize(""), - - button_addMoreLayer4("Add More Layers"), - text_lossLayerTop(), - text_lossLayerBottom1(), - text_lossLayerBottom2(), - text_lossLayerName(), - label_lossLayerTop(""), - label_lossLayerBottom1(""), - label_lossLayerBottom2(""), - label_lossLayerName(""), - button_setLossParameters("Add this Loss Layer"), - label_lossLayerNormalization(""), - - button_addMoreLayer5("Add More Layers"), - title_normalizationLayerType(""), - title_lossLayerType(""), - title_extraLayerType(""), - - button_displayCnnLayers("Display the Network"), - button_editMore("Add more layers"), - box_fullCnnLayerMatter(Gtk::ORIENTATION_VERTICAL), - button_deleteLayerAtEnd("Delete Layer at the end"), - button_saveFile("Save File") -{ - numLayers = 0; - set_title("Network Creator"); - set_border_width(10); - add(m_sw1); - m_grid1.set_column_spacing (10); - m_grid1.set_row_spacing (50); - - //level 0 - - label_networkFileName.set_text("1) Give a proper name to the Network file: "); - label_networkFileName.set_line_wrap(); - label_networkFileName.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_networkFileName,0,0,2,1); - label_networkFileName.show(); - - text_networkFileName.set_max_length(100); - text_networkFileName.set_text("../examples/objectdetector/Mnist_Train/train1.prototxt"); - text_networkFileName.select_region(0, text_networkFileName.get_text_length()); - m_grid1.attach(text_networkFileName,2,0,1,1); - text_networkFileName.show(); - - - button_networkFileName.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "networkFileName")); - m_grid1.attach(button_networkFileName,3,0,1,1); - button_networkFileName.show(); - - //level 1 - - label_activationLayerType.set_text("1) Select activation layer:\n(Append activation layer as next layer) "); - label_activationLayerType.set_line_wrap(); - label_activationLayerType.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_activationLayerType,0,1,2,1); - label_activationLayerType.show(); - - ref_activationLayerType = Gtk::ListStore::create(column_activationLayerType); - combo_activationLayerType.set_model(ref_activationLayerType); - - Gtk::TreeModel::Row row_activationLayerType = *(ref_activationLayerType->append()); - row_activationLayerType[column_activationLayerType.m_col_id] = 1; - row_activationLayerType[column_activationLayerType.m_col_name] = "AbsVal"; - row_activationLayerType[column_activationLayerType.m_col_extra] = "Absolute Value Layer"; - combo_activationLayerType.set_active(row_activationLayerType); - - row_activationLayerType = *(ref_activationLayerType->append()); - row_activationLayerType[column_activationLayerType.m_col_id] = 2; - row_activationLayerType[column_activationLayerType.m_col_name] = "Exp"; - row_activationLayerType[column_activationLayerType.m_col_extra] = "Exponential Layer"; - - - row_activationLayerType = *(ref_activationLayerType->append()); - row_activationLayerType[column_activationLayerType.m_col_id] = 3; - row_activationLayerType[column_activationLayerType.m_col_name] = "Log"; - row_activationLayerType[column_activationLayerType.m_col_extra] = "Log Layer"; - - row_activationLayerType = *(ref_activationLayerType->append()); - row_activationLayerType[column_activationLayerType.m_col_id] = 4; - row_activationLayerType[column_activationLayerType.m_col_name] = "Power"; - row_activationLayerType[column_activationLayerType.m_col_extra] = "Power Layer"; - - row_activationLayerType = *(ref_activationLayerType->append()); - row_activationLayerType[column_activationLayerType.m_col_id] = 5; - row_activationLayerType[column_activationLayerType.m_col_name] = "PReLU"; - row_activationLayerType[column_activationLayerType.m_col_extra] = "PReLU Layer"; - - row_activationLayerType = *(ref_activationLayerType->append()); - row_activationLayerType[column_activationLayerType.m_col_id] = 6; - row_activationLayerType[column_activationLayerType.m_col_name] = "ReLU"; - row_activationLayerType[column_activationLayerType.m_col_extra] = "ReLU Layer"; - - row_activationLayerType = *(ref_activationLayerType->append()); - row_activationLayerType[column_activationLayerType.m_col_id] = 7; - row_activationLayerType[column_activationLayerType.m_col_name] = "Sigmoid"; - row_activationLayerType[column_activationLayerType.m_col_extra] = "Sigmoid Layer"; - - row_activationLayerType = *(ref_activationLayerType->append()); - row_activationLayerType[column_activationLayerType.m_col_id] = 8; - row_activationLayerType[column_activationLayerType.m_col_name] = "TanH"; - row_activationLayerType[column_activationLayerType.m_col_extra] = "Hyperbolic tangent Layer"; - - combo_activationLayerType.pack_start(column_activationLayerType.m_col_id); - combo_activationLayerType.pack_start(column_activationLayerType.m_col_name); - combo_activationLayerType.set_cell_data_func(cell_activationLayerType, sigc::mem_fun(*this, &NetworkCreator::on_cell_data_extra)); - combo_activationLayerType.pack_start(cell_activationLayerType); - - m_grid1.attach(combo_activationLayerType,2,1,2,1); - combo_activationLayerType.signal_changed().connect( sigc::mem_fun(*this, &NetworkCreator::on_combo_changed) ); - - button_activationLayerType.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "activationLayerType")); - m_grid1.attach(button_activationLayerType,4,1,1,1); - button_activationLayerType.show(); - - - // level 2 - - label_criticalLayerType.set_text("2) Select Critical Layer:\n(Append critical layer as next layer) "); - label_criticalLayerType.set_line_wrap(); - label_criticalLayerType.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_criticalLayerType,0,2,2,1); - label_criticalLayerType.show(); - - ref_criticalLayerType = Gtk::ListStore::create(column_criticalLayerType); - combo_criticalLayerType.set_model(ref_criticalLayerType); - - Gtk::TreeModel::Row row_criticalLayerType = *(ref_criticalLayerType->append()); - row_criticalLayerType[column_criticalLayerType.m_col_id] = 1; - row_criticalLayerType[column_criticalLayerType.m_col_name] = "Accuracy"; - row_criticalLayerType[column_criticalLayerType.m_col_extra] = "Accuracy Layer (Validation phase)"; - combo_criticalLayerType.set_active(row_criticalLayerType); - - row_criticalLayerType = *(ref_criticalLayerType->append()); - row_criticalLayerType[column_criticalLayerType.m_col_id] = 2; - row_criticalLayerType[column_criticalLayerType.m_col_name] = "Concat"; - row_criticalLayerType[column_criticalLayerType.m_col_extra] = "Concatenation Layer"; - - row_criticalLayerType = *(ref_criticalLayerType->append()); - row_criticalLayerType[column_criticalLayerType.m_col_id] = 3; - row_criticalLayerType[column_criticalLayerType.m_col_name] = "Convolution"; - row_criticalLayerType[column_criticalLayerType.m_col_extra] = "Convolution Layer"; - - row_criticalLayerType = *(ref_criticalLayerType->append()); - row_criticalLayerType[column_criticalLayerType.m_col_id] = 4; - row_criticalLayerType[column_criticalLayerType.m_col_name] = "Deconvolution"; - row_criticalLayerType[column_criticalLayerType.m_col_extra] = "De-Convolution Layer"; - - row_criticalLayerType = *(ref_criticalLayerType->append()); - row_criticalLayerType[column_criticalLayerType.m_col_id] = 5; - row_criticalLayerType[column_criticalLayerType.m_col_name] = "Dropout"; - row_criticalLayerType[column_criticalLayerType.m_col_extra] = "Dropout Layer"; - - row_criticalLayerType = *(ref_criticalLayerType->append()); - row_criticalLayerType[column_criticalLayerType.m_col_id] = 6; - row_criticalLayerType[column_criticalLayerType.m_col_name] = "InnerProduct"; - row_criticalLayerType[column_criticalLayerType.m_col_extra] = "Inner Product (Fully Connected) Layer"; - - row_criticalLayerType = *(ref_criticalLayerType->append()); - row_criticalLayerType[column_criticalLayerType.m_col_id] = 7; - row_criticalLayerType[column_criticalLayerType.m_col_name] = "Pooling"; - row_criticalLayerType[column_criticalLayerType.m_col_extra] = "Pooling Layer"; - - row_criticalLayerType = *(ref_criticalLayerType->append()); - row_criticalLayerType[column_criticalLayerType.m_col_id] = 8; - row_criticalLayerType[column_criticalLayerType.m_col_name] = "Softmax"; - row_criticalLayerType[column_criticalLayerType.m_col_extra] = "Softmax Classification Layer"; - - combo_criticalLayerType.pack_start(column_criticalLayerType.m_col_id); - combo_criticalLayerType.pack_start(column_criticalLayerType.m_col_name); - combo_criticalLayerType.set_cell_data_func(cell_criticalLayerType, sigc::mem_fun(*this, &NetworkCreator::on_cell_data_extra)); - combo_criticalLayerType.pack_start(cell_criticalLayerType); - - m_grid1.attach(combo_criticalLayerType,2,2,2,1); - combo_criticalLayerType.signal_changed().connect( sigc::mem_fun(*this, &NetworkCreator::on_combo_changed) ); - - button_criticalLayerType.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "criticalLayerType")); - m_grid1.attach(button_criticalLayerType,4,2,1,1); - button_criticalLayerType.show(); - - //level 3 - - label_normalizationLayerType.set_text("3) Select Normalization Layer:\n(Append normalization layer as loss layer) "); - label_normalizationLayerType.set_line_wrap(); - label_normalizationLayerType.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_normalizationLayerType,0,3,2,1); - label_normalizationLayerType.show(); - - ref_normalizationLayerType = Gtk::ListStore::create(column_normalizationLayerType); - combo_normalizationLayerType.set_model(ref_normalizationLayerType); - - Gtk::TreeModel::Row row_normalizationLayerType = *(ref_normalizationLayerType->append()); - row_normalizationLayerType[column_normalizationLayerType.m_col_id] = 1; - row_normalizationLayerType[column_normalizationLayerType.m_col_name] = "BatchNorm"; - row_normalizationLayerType[column_criticalLayerType.m_col_extra] = "Batch Normalization Layer"; - combo_normalizationLayerType.set_active(row_normalizationLayerType); - - row_normalizationLayerType = *(ref_normalizationLayerType->append()); - row_normalizationLayerType[column_normalizationLayerType.m_col_id] = 2; - row_normalizationLayerType[column_normalizationLayerType.m_col_name] = "LRN"; - row_normalizationLayerType[column_normalizationLayerType.m_col_extra] = "Local Response Normalization Layer"; - - row_normalizationLayerType = *(ref_normalizationLayerType->append()); - row_normalizationLayerType[column_normalizationLayerType.m_col_id] = 3; - row_normalizationLayerType[column_normalizationLayerType.m_col_name] = "MVN"; - row_normalizationLayerType[column_normalizationLayerType.m_col_extra] = "Multi Variate Normalization Layer"; - - combo_normalizationLayerType.pack_start(column_normalizationLayerType.m_col_id); - combo_normalizationLayerType.pack_start(column_normalizationLayerType.m_col_name); - combo_normalizationLayerType.set_cell_data_func(cell_normalizationLayerType, sigc::mem_fun(*this, &NetworkCreator::on_cell_data_extra)); - combo_normalizationLayerType.pack_start(cell_normalizationLayerType); - - m_grid1.attach(combo_normalizationLayerType,2,3,2,1); - combo_normalizationLayerType.signal_changed().connect( sigc::mem_fun(*this, &NetworkCreator::on_combo_changed) ); - - button_normalizationLayerType.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "normalizationLayerType")); - m_grid1.attach(button_normalizationLayerType,4,3,1,1); - button_normalizationLayerType.show(); - - //level 4 - - label_lossLayerType.set_text("4) Select Loss Layer:\n(Append loss layer as next layer) "); - label_lossLayerType.set_line_wrap(); - label_lossLayerType.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_lossLayerType,0,4,2,1); - label_lossLayerType.show(); - - ref_lossLayerType = Gtk::ListStore::create(column_lossLayerType); - combo_lossLayerType.set_model(ref_lossLayerType); - - Gtk::TreeModel::Row row_lossLayerType = *(ref_lossLayerType->append()); - row_lossLayerType[column_lossLayerType.m_col_id] = 1; - row_lossLayerType[column_lossLayerType.m_col_name] = "SoftmaxWithLoss"; - row_lossLayerType[column_lossLayerType.m_col_extra] = "Loss Layer with softmax activation"; - combo_lossLayerType.set_active(row_lossLayerType); - - row_lossLayerType = *(ref_lossLayerType->append()); - row_lossLayerType[column_lossLayerType.m_col_id] = 2; - row_lossLayerType[column_lossLayerType.m_col_name] = "HingeLoss"; - row_lossLayerType[column_lossLayerType.m_col_extra] = "Hinge Loss Layer"; - - row_lossLayerType = *(ref_lossLayerType->append()); - row_lossLayerType[column_lossLayerType.m_col_id] = 3; - row_lossLayerType[column_lossLayerType.m_col_name] = "ContrastiveLoss"; - row_lossLayerType[column_lossLayerType.m_col_extra] = "Contrastive Loss Layer"; - - row_lossLayerType = *(ref_lossLayerType->append()); - row_lossLayerType[column_lossLayerType.m_col_id] = 4; - row_lossLayerType[column_lossLayerType.m_col_name] = "EuclideanLoss"; - row_lossLayerType[column_lossLayerType.m_col_extra] = "Euclidean Loss Layer"; - - row_lossLayerType = *(ref_lossLayerType->append()); - row_lossLayerType[column_lossLayerType.m_col_id] = 5; - row_lossLayerType[column_lossLayerType.m_col_name] = "InfogainLoss"; - row_lossLayerType[column_lossLayerType.m_col_extra] = "Infogain Loss Layer"; - - row_lossLayerType = *(ref_lossLayerType->append()); - row_lossLayerType[column_lossLayerType.m_col_id] = 6; - row_lossLayerType[column_lossLayerType.m_col_name] = "MultinomialLogisticLoss"; - row_lossLayerType[column_lossLayerType.m_col_extra] = "Multinomial Logistic Loss Layer"; - - row_lossLayerType = *(ref_lossLayerType->append()); - row_lossLayerType[column_lossLayerType.m_col_id] = 7; - row_lossLayerType[column_lossLayerType.m_col_name] = "EuclideanLoss"; - row_lossLayerType[column_lossLayerType.m_col_extra] = "Euclidean Loss Layer"; - - row_lossLayerType = *(ref_lossLayerType->append()); - row_lossLayerType[column_lossLayerType.m_col_id] = 8; - row_lossLayerType[column_lossLayerType.m_col_name] = "SigmoidCrossEntropyLoss"; - row_lossLayerType[column_lossLayerType.m_col_extra] = "Sigmoid Cross Entropy Loss Layer"; - - combo_lossLayerType.pack_start(column_lossLayerType.m_col_id); - combo_lossLayerType.pack_start(column_lossLayerType.m_col_name); - combo_lossLayerType.set_cell_data_func(cell_lossLayerType, sigc::mem_fun(*this, &NetworkCreator::on_cell_data_extra)); - combo_lossLayerType.pack_start(cell_lossLayerType); - - m_grid1.attach(combo_lossLayerType,2,4,2,1); - combo_lossLayerType.signal_changed().connect( sigc::mem_fun(*this, &NetworkCreator::on_combo_changed) ); - - button_lossLayerType.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "lossLayerType")); - m_grid1.attach(button_lossLayerType,4,4,1,1); - button_lossLayerType.show(); - - - //level 5 - - label_extraLayerType.set_text("6) A few extra layers:\n(Append these layer as next layer) "); - label_extraLayerType.set_line_wrap(); - label_extraLayerType.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_extraLayerType,0,5,2,1); - label_extraLayerType.show(); - - ref_extraLayerType = Gtk::ListStore::create(column_extraLayerType); - combo_extraLayerType.set_model(ref_extraLayerType); - - Gtk::TreeModel::Row row_extraLayerType = *(ref_extraLayerType->append()); - row_extraLayerType[column_extraLayerType.m_col_id] = 1; - row_extraLayerType[column_extraLayerType.m_col_name] = "ArgMax"; - row_extraLayerType[column_extraLayerType.m_col_extra] = "Maximum Argument Layer"; - combo_extraLayerType.set_active(row_extraLayerType); - - row_extraLayerType = *(ref_extraLayerType->append()); - row_extraLayerType[column_extraLayerType.m_col_id] = 2; - row_extraLayerType[column_extraLayerType.m_col_name] = "BNLL"; - row_extraLayerType[column_extraLayerType.m_col_extra] = "Binomial Normal Log Likelihood Layer"; - - row_extraLayerType = *(ref_extraLayerType->append()); - row_extraLayerType[column_extraLayerType.m_col_id] = 3; - row_extraLayerType[column_extraLayerType.m_col_name] = "Eltwise"; - row_extraLayerType[column_extraLayerType.m_col_extra] = "Element Wise Operation Layer"; - - row_extraLayerType = *(ref_extraLayerType->append()); - row_extraLayerType[column_extraLayerType.m_col_id] = 4; - row_extraLayerType[column_extraLayerType.m_col_name] = "Eltwise"; - row_extraLayerType[column_extraLayerType.m_col_extra] = "Element Wise Operation Layer"; - - combo_extraLayerType.pack_start(column_extraLayerType.m_col_id); - combo_extraLayerType.pack_start(column_extraLayerType.m_col_name); - combo_extraLayerType.set_cell_data_func(cell_extraLayerType, sigc::mem_fun(*this, &NetworkCreator::on_cell_data_extra)); - combo_extraLayerType.pack_start(cell_extraLayerType); - - m_grid1.attach(combo_extraLayerType,2,5,2,1); - combo_extraLayerType.signal_changed().connect( sigc::mem_fun(*this, &NetworkCreator::on_combo_changed) ); - - button_extraLayerType.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "extraLayerType")); - m_grid1.attach(button_extraLayerType,4,5,1,1); - button_extraLayerType.show(); - - button_displayCnnLayers.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "displayCnnLayers")); - m_grid1.attach(button_displayCnnLayers,0,6,2,1); - button_displayCnnLayers.show(); - - button_saveFile.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "saveFile")); - m_grid1.attach(button_saveFile,0,7,2,1); - button_saveFile.show(); - - - - m_sw1.add(m_grid1); - m_sw1.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); -// m_grid1.show(); - show_all_children(); - m_sw1.show(); - - - //Activation Window - button_setActivationParameters.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "setActivationParameters")); - m_grid_activationLayerType.attach(button_setActivationParameters,0,7,2,1); - - button_addMoreLayer.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "addMoreLayer")); - m_grid_activationLayerType.attach(button_addMoreLayer,2,7,1,1); - - label_activationLayerBottom.set_text("Bottom Layer Name: "); - label_activationLayerBottom.set_line_wrap(); - label_activationLayerBottom.set_justify(Gtk::JUSTIFY_FILL); - m_grid_activationLayerType.attach(label_activationLayerBottom,0,1,2,1); - label_activationLayerBottom.show(); - - text_activationLayerBottom.set_max_length(100); - text_activationLayerBottom.set_text(""); - text_activationLayerBottom.select_region(0, text_activationLayerBottom.get_text_length()); - m_grid_activationLayerType.attach(text_activationLayerBottom,2,1,1,1); - text_activationLayerBottom.show(); - - label_activationLayerTop.set_text("Top Layer Name: "); - label_activationLayerTop.set_line_wrap(); - label_activationLayerTop.set_justify(Gtk::JUSTIFY_FILL); - m_grid_activationLayerType.attach(label_activationLayerTop,0,2,2,1); - label_activationLayerTop.show(); - - text_activationLayerTop.set_max_length(100); - text_activationLayerTop.set_text(""); - text_activationLayerTop.select_region(0, text_activationLayerTop.get_text_length()); - m_grid_activationLayerType.attach(text_activationLayerTop,2,2,1,1); - text_activationLayerTop.show(); - - label_activationLayerName.set_text("Current Layer Name: "); - label_activationLayerName.set_line_wrap(); - label_activationLayerName.set_justify(Gtk::JUSTIFY_FILL); - m_grid_activationLayerType.attach(label_activationLayerName,0,3,2,1); - label_activationLayerName.show(); - - text_activationLayerName.set_max_length(100); - text_activationLayerName.set_text(""); - text_activationLayerName.select_region(0, text_activationLayerName.get_text_length()); - m_grid_activationLayerType.attach(text_activationLayerName,2,3,1,1); - text_activationLayerName.show(); - - label_activationLayerScale.set_text("Layer Parameter Scale: "); - label_activationLayerScale.set_line_wrap(); - label_activationLayerScale.set_justify(Gtk::JUSTIFY_FILL); - m_grid_activationLayerType.attach(label_activationLayerScale,0,4,2,1); - label_activationLayerScale.show(); - - text_activationLayerScale.set_max_length(100); - text_activationLayerScale.set_text("1"); - text_activationLayerScale.select_region(0, text_activationLayerScale.get_text_length()); - m_grid_activationLayerType.attach(text_activationLayerScale,2,4,1,1); - text_activationLayerScale.show(); - - label_activationLayerShift.set_text("Layer Parameter Shift: "); - label_activationLayerShift.set_line_wrap(); - label_activationLayerShift.set_justify(Gtk::JUSTIFY_FILL); - m_grid_activationLayerType.attach(label_activationLayerShift,0,5,2,1); - label_activationLayerShift.show(); - - text_activationLayerShift.set_max_length(100); - text_activationLayerShift.set_text("0"); - text_activationLayerShift.select_region(0, text_activationLayerShift.get_text_length()); - m_grid_activationLayerType.attach(text_activationLayerShift,2,5,1,1); - text_activationLayerShift.show(); - - label_activationLayerBase.set_text("Layer Parameter Base: "); - label_activationLayerBase.set_line_wrap(); - label_activationLayerBase.set_justify(Gtk::JUSTIFY_FILL); - m_grid_activationLayerType.attach(label_activationLayerBase,0,6,2,1); - label_activationLayerBase.show(); - - text_activationLayerBase.set_max_length(100); - text_activationLayerBase.set_text("-1"); - text_activationLayerBase.select_region(0, text_activationLayerBase.get_text_length()); - m_grid_activationLayerType.attach(text_activationLayerBase,2,6,1,1); - text_activationLayerBase.show(); - - label_activationLayerNegativeSlope.set_text("Relu Param Negative Slope: "); - label_activationLayerNegativeSlope.set_line_wrap(); - label_activationLayerNegativeSlope.set_justify(Gtk::JUSTIFY_FILL); - m_grid_activationLayerType.attach(label_activationLayerNegativeSlope,0,4,2,1); - label_activationLayerNegativeSlope.show(); - - text_activationLayerNegativeSlope.set_max_length(100); - text_activationLayerNegativeSlope.set_text("0"); - text_activationLayerNegativeSlope.select_region(0, text_activationLayerNegativeSlope.get_text_length()); - m_grid_activationLayerType.attach(text_activationLayerNegativeSlope,2,4,1,1); - text_activationLayerNegativeSlope.show(); - - title_activationLayerType.set_text("Set the Properties of Activation Layer type: "); - title_activationLayerType.set_line_wrap(); - title_activationLayerType.set_justify(Gtk::JUSTIFY_FILL); - m_grid_activationLayerType.attach(title_activationLayerType,0,0,2,1); - title_activationLayerType.show(); - - m_sw_activationLayerType.add(m_grid_activationLayerType); - - - //Critical Layer Window - title_criticalLayerType.set_text("Set the Properties of Critical Layer type: "); - title_criticalLayerType.set_line_wrap(); - title_criticalLayerType.set_justify(Gtk::JUSTIFY_FILL); - m_grid_criticalLayerType.attach(title_criticalLayerType,0,0,2,1); - title_criticalLayerType.show(); - - button_setCriticalParameters.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "setCriticalParameters")); - m_grid_criticalLayerType.attach(button_setCriticalParameters,0,25,2,1); - button_addMoreLayer2.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "addMoreLayer2")); - m_grid_criticalLayerType.attach(button_addMoreLayer2,2,25,1,1); - - label_criticalLayerBottom1.set_text("Bottom1 Layer Name: "); - label_criticalLayerBottom1.set_line_wrap(); - label_criticalLayerBottom1.set_justify(Gtk::JUSTIFY_FILL); - m_grid_criticalLayerType.attach(label_criticalLayerBottom1,0,1,2,1); - label_criticalLayerBottom1.show(); - - text_criticalLayerBottom1.set_max_length(100); - text_criticalLayerBottom1.set_text(""); - text_criticalLayerBottom1.select_region(0, text_criticalLayerBottom1.get_text_length()); - m_grid_criticalLayerType.attach(text_criticalLayerBottom1,2,1,1,1); - text_criticalLayerBottom1.show(); - - label_criticalLayerBottom2.set_text("Bottom2 Layer Name: "); - label_criticalLayerBottom2.set_line_wrap(); - label_criticalLayerBottom2.set_justify(Gtk::JUSTIFY_FILL); - m_grid_criticalLayerType.attach(label_criticalLayerBottom2,0,2,2,1); - label_criticalLayerBottom2.show(); - - text_criticalLayerBottom2.set_max_length(100); - text_criticalLayerBottom2.set_text(""); - text_criticalLayerBottom2.select_region(0, text_criticalLayerBottom2.get_text_length()); - m_grid_criticalLayerType.attach(text_criticalLayerBottom2,2,2,1,1); - text_criticalLayerBottom2.show(); - - label_criticalLayerTop.set_text("Top Layer Name: "); - label_criticalLayerTop.set_line_wrap(); - label_criticalLayerTop.set_justify(Gtk::JUSTIFY_FILL); - m_grid_criticalLayerType.attach(label_criticalLayerTop,0,3,2,1); - label_criticalLayerTop.show(); - - text_criticalLayerTop.set_max_length(100); - text_criticalLayerTop.set_text(""); - text_criticalLayerTop.select_region(0, text_criticalLayerTop.get_text_length()); - m_grid_criticalLayerType.attach(text_criticalLayerTop,2,3,1,1); - text_criticalLayerTop.show(); - - label_criticalLayerName.set_text("Current Layer Name: "); - label_criticalLayerName.set_line_wrap(); - label_criticalLayerName.set_justify(Gtk::JUSTIFY_FILL); - m_grid_criticalLayerType.attach(label_criticalLayerName,0,4,2,1); - label_criticalLayerName.show(); - - text_criticalLayerName.set_max_length(100); - text_criticalLayerName.set_text(""); - text_criticalLayerName.select_region(0, text_criticalLayerName.get_text_length()); - m_grid_criticalLayerType.attach(text_criticalLayerName,2,4,1,1); - text_criticalLayerName.show(); - - label_criticalLayerFilterLr.set_text("Set filter lr_mult:\n(Leave unchanged if not needed) "); - label_criticalLayerFilterLr.set_line_wrap(); - label_criticalLayerFilterLr.set_justify(Gtk::JUSTIFY_FILL); - m_grid_criticalLayerType.attach(label_criticalLayerFilterLr,0,5,2,1); - label_criticalLayerFilterLr.show(); - - text_criticalLayerFilterLr.set_max_length(100); - text_criticalLayerFilterLr.set_text("1"); - text_criticalLayerFilterLr.select_region(0, text_criticalLayerFilterLr.get_text_length()); - m_grid_criticalLayerType.attach(text_criticalLayerFilterLr,2,5,1,1); - text_criticalLayerFilterLr.show(); - - label_criticalLayerFilterDm.set_text("Set filter decay_mult:\n(Leave unchanged if not needed) "); - label_criticalLayerFilterDm.set_line_wrap(); - label_criticalLayerFilterDm.set_justify(Gtk::JUSTIFY_FILL); - m_grid_criticalLayerType.attach(label_criticalLayerFilterDm,0,6,2,1); - label_criticalLayerFilterDm.show(); - - text_criticalLayerFilterDm.set_max_length(100); - text_criticalLayerFilterDm.set_text("1"); - text_criticalLayerFilterDm.select_region(0, text_criticalLayerFilterDm.get_text_length()); - m_grid_criticalLayerType.attach(text_criticalLayerFilterDm,2,6,1,1); - text_criticalLayerFilterDm.show(); - - label_criticalLayerBiasLr.set_text("Set bias lr_mult:\n(Leave unchanged if not needed) "); - label_criticalLayerBiasLr.set_line_wrap(); - label_criticalLayerBiasLr.set_justify(Gtk::JUSTIFY_FILL); - m_grid_criticalLayerType.attach(label_criticalLayerBiasLr,0,7,2,1); - label_criticalLayerBiasLr.show(); - - text_criticalLayerBiasLr.set_max_length(100); - text_criticalLayerBiasLr.set_text("2"); - text_criticalLayerBiasLr.select_region(0, text_criticalLayerBiasLr.get_text_length()); - m_grid_criticalLayerType.attach(text_criticalLayerBiasLr,2,7,1,1); - text_criticalLayerBiasLr.show(); - - label_criticalLayerBiasDm.set_text("Set bias decay_mult:\n(Leave unchanged if not needed) "); - label_criticalLayerBiasDm.set_line_wrap(); - label_criticalLayerBiasDm.set_justify(Gtk::JUSTIFY_FILL); - m_grid_criticalLayerType.attach(label_criticalLayerBiasDm,0,8,2,1); - label_criticalLayerBiasDm.show(); - - text_criticalLayerBiasDm.set_max_length(100); - text_criticalLayerBiasDm.set_text("0"); - text_criticalLayerBiasDm.select_region(0, text_criticalLayerBiasDm.get_text_length()); - m_grid_criticalLayerType.attach(text_criticalLayerBiasDm,2,8,1,1); - text_criticalLayerBiasDm.show(); - - label_criticalLayerNumOutput.set_text("Set param num_output: "); - label_criticalLayerNumOutput.set_line_wrap(); - label_criticalLayerNumOutput.set_justify(Gtk::JUSTIFY_FILL); - m_grid_criticalLayerType.attach(label_criticalLayerNumOutput,0,9,2,1); - label_criticalLayerNumOutput.show(); - - text_criticalLayerNumOutput.set_max_length(100); - text_criticalLayerNumOutput.set_text("64"); - text_criticalLayerNumOutput.select_region(0, text_criticalLayerNumOutput.get_text_length()); - m_grid_criticalLayerType.attach(text_criticalLayerNumOutput,2,9,1,1); - text_criticalLayerNumOutput.show(); - - label_criticalLayerKernelW.set_text("Set param kernel_w: "); - label_criticalLayerKernelW.set_line_wrap(); - label_criticalLayerKernelW.set_justify(Gtk::JUSTIFY_FILL); - m_grid_criticalLayerType.attach(label_criticalLayerKernelW,0,10,2,1); - label_criticalLayerKernelW.show(); - - text_criticalLayerKernelW.set_max_length(100); - text_criticalLayerKernelW.set_text("3"); - text_criticalLayerKernelW.select_region(0, text_criticalLayerKernelW.get_text_length()); - m_grid_criticalLayerType.attach(text_criticalLayerKernelW,2,10,1,1); - text_criticalLayerKernelW.show(); - - label_criticalLayerKernelH.set_text("Set param kernel_h: "); - label_criticalLayerKernelH.set_line_wrap(); - label_criticalLayerKernelH.set_justify(Gtk::JUSTIFY_FILL); - m_grid_criticalLayerType.attach(label_criticalLayerKernelH,0,11,2,1); - label_criticalLayerKernelH.show(); - - text_criticalLayerKernelH.set_max_length(100); - text_criticalLayerKernelH.set_text("3"); - text_criticalLayerKernelH.select_region(0, text_criticalLayerKernelH.get_text_length()); - m_grid_criticalLayerType.attach(text_criticalLayerKernelH,2,11,1,1); - text_criticalLayerKernelH.show(); - - label_criticalLayerStrideW.set_text("Set param stride_w: "); - label_criticalLayerStrideW.set_line_wrap(); - label_criticalLayerStrideW.set_justify(Gtk::JUSTIFY_FILL); - m_grid_criticalLayerType.attach(label_criticalLayerStrideW,0,12,2,1); - label_criticalLayerStrideW.show(); - - text_criticalLayerStrideW.set_max_length(100); - text_criticalLayerStrideW.set_text("1"); - text_criticalLayerStrideW.select_region(0, text_criticalLayerStrideW.get_text_length()); - m_grid_criticalLayerType.attach(text_criticalLayerStrideW,2,12,1,1); - text_criticalLayerStrideW.show(); - - label_criticalLayerStrideH.set_text("Set param stride_h: "); - label_criticalLayerStrideH.set_line_wrap(); - label_criticalLayerStrideH.set_justify(Gtk::JUSTIFY_FILL); - m_grid_criticalLayerType.attach(label_criticalLayerStrideH,0,13,2,1); - label_criticalLayerStrideH.show(); - - text_criticalLayerStrideH.set_max_length(100); - text_criticalLayerStrideH.set_text("1"); - text_criticalLayerStrideH.select_region(0, text_criticalLayerStrideH.get_text_length()); - m_grid_criticalLayerType.attach(text_criticalLayerStrideH,2,13,1,1); - text_criticalLayerStrideH.show(); - - label_criticalLayerPadW.set_text("Set param pad_w: "); - label_criticalLayerPadW.set_line_wrap(); - label_criticalLayerPadW.set_justify(Gtk::JUSTIFY_FILL); - m_grid_criticalLayerType.attach(label_criticalLayerPadW,0,14,2,1); - label_criticalLayerPadW.show(); - - text_criticalLayerPadW.set_max_length(100); - text_criticalLayerPadW.set_text("1"); - text_criticalLayerPadW.select_region(0, text_criticalLayerPadW.get_text_length()); - m_grid_criticalLayerType.attach(text_criticalLayerPadW,2,14,1,1); - text_criticalLayerPadW.show(); - - label_criticalLayerPadH.set_text("Set param pad_h: "); - label_criticalLayerPadH.set_line_wrap(); - label_criticalLayerPadH.set_justify(Gtk::JUSTIFY_FILL); - m_grid_criticalLayerType.attach(label_criticalLayerPadH,0,15,2,1); - label_criticalLayerPadH.show(); - - text_criticalLayerPadH.set_max_length(100); - text_criticalLayerPadH.set_text("1"); - text_criticalLayerPadH.select_region(0, text_criticalLayerPadH.get_text_length()); - m_grid_criticalLayerType.attach(text_criticalLayerPadH,2,15,1,1); - text_criticalLayerPadH.show(); - - label_criticalLayerWeightFiller.set_text("Set Weight Filler : "); - label_criticalLayerWeightFiller.set_line_wrap(); - label_criticalLayerWeightFiller.set_justify(Gtk::JUSTIFY_FILL); - m_grid_criticalLayerType.attach(label_criticalLayerWeightFiller,0,16,2,1); - label_criticalLayerWeightFiller.show(); - - Gtk::RadioButton::Group group1 = rbutton_criticalLayerWeightFillerConstant.get_group(); - rbutton_criticalLayerWeightFillerUniform.set_group(group1); - rbutton_criticalLayerWeightGaussian.set_group(group1); - rbutton_criticalLayerWeightFillerPositiveUnitBall.set_group(group1); - rbutton_criticalLayerWeightFillerXavier.set_group(group1); - rbutton_criticalLayerWeightFillerMSRA.set_group(group1); - rbutton_criticalLayerWeightFillerBilinear.set_group(group1); - rbutton_criticalLayerWeightFillerConstant.set_active(); - m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerConstant,2,16,1,1); - rbutton_criticalLayerWeightFillerConstant.show(); - m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerUniform,2,17,1,1); - rbutton_criticalLayerWeightFillerUniform.show(); - m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightGaussian,2,18,1,1); - rbutton_criticalLayerWeightGaussian.show(); - m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerPositiveUnitBall,2,19,1,1); - rbutton_criticalLayerWeightFillerPositiveUnitBall.show(); - m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavier,2,20,1,1); - rbutton_criticalLayerWeightFillerXavier.show(); - m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerMSRA,2,21,1,1); - rbutton_criticalLayerWeightFillerMSRA.show(); - m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerBilinear,2,22,1,1); - rbutton_criticalLayerWeightFillerBilinear.show(); - - label_criticalLayerWeightFillerConstantValue.set_text("value: "); - label_criticalLayerWeightFillerConstantValue.set_line_wrap(); - label_criticalLayerWeightFillerConstantValue.set_justify(Gtk::JUSTIFY_FILL); - m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerConstantValue,4,16,1,1); - label_criticalLayerWeightFillerConstantValue.show(); - - text_criticalLayerWeightFillerConstantValue.set_max_length(100); - text_criticalLayerWeightFillerConstantValue.set_text("0.5"); - text_criticalLayerWeightFillerConstantValue.select_region(0, text_criticalLayerWeightFillerConstantValue.get_text_length()); - m_grid_criticalLayerType.attach(text_criticalLayerWeightFillerConstantValue,5,16,1,1); - text_criticalLayerWeightFillerConstantValue.show(); - - label_criticalLayerWeightFillerUniformMin.set_text("min: "); - label_criticalLayerWeightFillerUniformMin.set_line_wrap(); - label_criticalLayerWeightFillerUniformMin.set_justify(Gtk::JUSTIFY_FILL); - m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerUniformMin,4,17,1,1); - label_criticalLayerWeightFillerUniformMin.show(); - - text_criticalLayerWeightFillerUniformMin.set_max_length(100); - text_criticalLayerWeightFillerUniformMin.set_text("0"); - text_criticalLayerWeightFillerUniformMin.select_region(0, text_criticalLayerWeightFillerUniformMin.get_text_length()); - m_grid_criticalLayerType.attach(text_criticalLayerWeightFillerUniformMin,5,17,1,1); - text_criticalLayerWeightFillerUniformMin.show(); - - label_criticalLayerWeightFillerUniformMax.set_text("max: "); - label_criticalLayerWeightFillerUniformMax.set_line_wrap(); - label_criticalLayerWeightFillerUniformMax.set_justify(Gtk::JUSTIFY_FILL); - m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerUniformMax,6,17,1,1); - label_criticalLayerWeightFillerUniformMax.show(); - - text_criticalLayerWeightFillerUniformMax.set_max_length(100); - text_criticalLayerWeightFillerUniformMax.set_text("1"); - text_criticalLayerWeightFillerUniformMax.select_region(0, text_criticalLayerWeightFillerUniformMax.get_text_length()); - m_grid_criticalLayerType.attach(text_criticalLayerWeightFillerUniformMax,7,17,1,1); - text_criticalLayerWeightFillerUniformMax.show(); - - label_criticalLayerWeightFillerGaussianMean.set_text("mean: "); - label_criticalLayerWeightFillerGaussianMean.set_line_wrap(); - label_criticalLayerWeightFillerGaussianMean.set_justify(Gtk::JUSTIFY_FILL); - m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerGaussianMean,4,18,1,1); - label_criticalLayerWeightFillerGaussianMean.show(); - - text_criticalLayerWeightFillerGaussianMean.set_max_length(100); - text_criticalLayerWeightFillerGaussianMean.set_text("0"); - text_criticalLayerWeightFillerGaussianMean.select_region(0, text_criticalLayerWeightFillerGaussianMean.get_text_length()); - m_grid_criticalLayerType.attach(text_criticalLayerWeightFillerGaussianMean,5,18,1,1); - text_criticalLayerWeightFillerGaussianMean.show(); - - label_criticalLayerWeightFillerGaussianStd.set_text("std: "); - label_criticalLayerWeightFillerGaussianStd.set_line_wrap(); - label_criticalLayerWeightFillerGaussianStd.set_justify(Gtk::JUSTIFY_FILL); - m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerGaussianStd,6,18,1,1); - label_criticalLayerWeightFillerGaussianStd.show(); - - text_criticalLayerWeightFillerGaussianStd.set_max_length(100); - text_criticalLayerWeightFillerGaussianStd.set_text("0.1"); - text_criticalLayerWeightFillerGaussianStd.select_region(0, text_criticalLayerWeightFillerGaussianStd.get_text_length()); - m_grid_criticalLayerType.attach(text_criticalLayerWeightFillerGaussianStd,7,18,1,1); - text_criticalLayerWeightFillerGaussianStd.show(); - - label_criticalLayerWeightFillerXavierVariance.set_text("variance_norm: "); - label_criticalLayerWeightFillerXavierVariance.set_line_wrap(); - label_criticalLayerWeightFillerXavierVariance.set_justify(Gtk::JUSTIFY_FILL); - m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerXavierVariance,4,20,1,1); - label_criticalLayerWeightFillerXavierVariance.show(); - - Gtk::RadioButton::Group group2 = rbutton_criticalLayerWeightFillerXavierIn.get_group(); - rbutton_criticalLayerWeightFillerXavierOut.set_group(group2); - rbutton_criticalLayerWeightFillerXavierAvg.set_group(group2); - rbutton_criticalLayerWeightFillerXavierIn.set_active(); - m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavierIn,5,20,1,1); - rbutton_criticalLayerWeightFillerXavierIn.show(); - m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavierOut,6,20,1,1); - rbutton_criticalLayerWeightFillerXavierOut.show(); - m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerXavierAvg,7,20,1,1); - rbutton_criticalLayerWeightFillerXavierAvg.show(); - - label_criticalLayerWeightFillerMSRAVariance.set_text("variance_norm: "); - label_criticalLayerWeightFillerMSRAVariance.set_line_wrap(); - label_criticalLayerWeightFillerMSRAVariance.set_justify(Gtk::JUSTIFY_FILL); - m_grid_criticalLayerType.attach(label_criticalLayerWeightFillerMSRAVariance,4,21,1,1); - label_criticalLayerWeightFillerMSRAVariance.show(); - - Gtk::RadioButton::Group group3 = rbutton_criticalLayerWeightFillerMSRAIn.get_group(); - rbutton_criticalLayerWeightFillerMSRAOut.set_group(group3); - rbutton_criticalLayerWeightFillerMSRAAvg.set_group(group3); - rbutton_criticalLayerWeightFillerMSRAIn.set_active(); - m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerMSRAIn,5,21,1,1); - rbutton_criticalLayerWeightFillerMSRAIn.show(); - m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerMSRAOut,6,21,1,1); - rbutton_criticalLayerWeightFillerMSRAOut.show(); - m_grid_criticalLayerType.attach(rbutton_criticalLayerWeightFillerMSRAAvg,7,21,1,1); - rbutton_criticalLayerWeightFillerMSRAAvg.show(); - - label_criticalLayerDropoutRatio.set_text("Set Dropout Ratio: "); - label_criticalLayerDropoutRatio.set_line_wrap(); - label_criticalLayerDropoutRatio.set_justify(Gtk::JUSTIFY_FILL); - m_grid_criticalLayerType.attach(label_criticalLayerDropoutRatio,0,22,2,1); - label_criticalLayerDropoutRatio.show(); - - text_criticalLayerDropoutRatio.set_max_length(100); - text_criticalLayerDropoutRatio.set_text("0.5"); - text_criticalLayerDropoutRatio.select_region(0, text_criticalLayerDropoutRatio.get_text_length()); - m_grid_criticalLayerType.attach(text_criticalLayerDropoutRatio,2,22,1,1); - text_criticalLayerDropoutRatio.show(); - - label_criticalLayerPool.set_text("Set Pool Type: "); - label_criticalLayerPool.set_line_wrap(); - label_criticalLayerPool.set_justify(Gtk::JUSTIFY_FILL); - m_grid_criticalLayerType.attach(label_criticalLayerPool,0,23,2,1); - label_criticalLayerPool.show(); - - Gtk::RadioButton::Group group4 = rbutton_criticalLayerPoolMax.get_group(); - rbutton_criticalLayerPoolAve.set_group(group4); - rbutton_criticalLayerPoolMax.set_active(); - m_grid_criticalLayerType.attach(rbutton_criticalLayerPoolMax,2,23,1,1); - rbutton_criticalLayerPoolMax.show(); - m_grid_criticalLayerType.attach(rbutton_criticalLayerPoolAve,3,23,1,1); - rbutton_criticalLayerPoolAve.show(); - - label_criticalLayerBias.set_text("Set bias value: "); - label_criticalLayerBias.set_line_wrap(); - label_criticalLayerBias.set_justify(Gtk::JUSTIFY_FILL); - m_grid_criticalLayerType.attach(label_criticalLayerBias,0,24,2,1); - label_criticalLayerBias.show(); - - text_criticalLayerBias.set_max_length(100); - text_criticalLayerBias.set_text("0.1"); - text_criticalLayerBias.select_region(0, text_criticalLayerBias.get_text_length()); - m_grid_criticalLayerType.attach(text_criticalLayerBias,2,24,1,1); - text_criticalLayerBias.show(); - - m_sw_criticalLayerType.add(m_grid_criticalLayerType); - - - //Normalization Layer Window - title_normalizationLayerType.set_text("Will be updated soon"); - title_normalizationLayerType.set_line_wrap(); - title_normalizationLayerType.set_justify(Gtk::JUSTIFY_FILL); - m_grid_normalizationLayerType.attach(title_normalizationLayerType,0,0,2,1); - title_normalizationLayerType.show(); - - button_setNormalizationParameters.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "setNormalizationParameters")); - m_grid_normalizationLayerType.attach(button_setNormalizationParameters,0,25,2,1); - - button_addMoreLayer3.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "addMoreLayer3")); - m_grid_normalizationLayerType.attach(button_addMoreLayer3,2,25,2,1); - - label_normalizationLayerBottom.set_text("Bottom Layer Name: "); - label_normalizationLayerBottom.set_line_wrap(); - label_normalizationLayerBottom.set_justify(Gtk::JUSTIFY_FILL); - m_grid_normalizationLayerType.attach(label_normalizationLayerBottom,0,1,2,1); - label_normalizationLayerBottom.show(); - - text_normalizationLayerBottom.set_max_length(100); - text_normalizationLayerBottom.set_text(""); - text_normalizationLayerBottom.select_region(0, text_normalizationLayerBottom.get_text_length()); - m_grid_normalizationLayerType.attach(text_normalizationLayerBottom,2,1,1,1); - text_normalizationLayerBottom.show(); - - label_normalizationLayerTop.set_text("Top Layer Name: "); - label_normalizationLayerTop.set_line_wrap(); - label_normalizationLayerTop.set_justify(Gtk::JUSTIFY_FILL); - m_grid_normalizationLayerType.attach(label_normalizationLayerTop,0,3,2,1); - label_normalizationLayerTop.show(); - - text_normalizationLayerTop.set_max_length(100); - text_normalizationLayerTop.set_text(""); - text_normalizationLayerTop.select_region(0, text_normalizationLayerTop.get_text_length()); - m_grid_normalizationLayerType.attach(text_normalizationLayerTop,2,3,1,1); - text_normalizationLayerTop.show(); - - label_normalizationLayerName.set_text("Current Layer Name: "); - label_normalizationLayerName.set_line_wrap(); - label_normalizationLayerName.set_justify(Gtk::JUSTIFY_FILL); - m_grid_normalizationLayerType.attach(label_normalizationLayerName,0,4,2,1); - label_normalizationLayerName.show(); - - text_normalizationLayerName.set_max_length(100); - text_normalizationLayerName.set_text(""); - text_normalizationLayerName.select_region(0, text_normalizationLayerName.get_text_length()); - m_grid_normalizationLayerType.attach(text_normalizationLayerName,2,4,1,1); - text_normalizationLayerName.show(); - - label_normalizationLayerlocalSize.set_text("Inner Parameter - local_size: "); - label_normalizationLayerlocalSize.set_line_wrap(); - label_normalizationLayerlocalSize.set_justify(Gtk::JUSTIFY_FILL); - m_grid_normalizationLayerType.attach(label_normalizationLayerlocalSize,0,5,2,1); - label_normalizationLayerlocalSize.show(); - - text_normalizationLayerlocalSize.set_max_length(100); - text_normalizationLayerlocalSize.set_text("5"); - text_normalizationLayerlocalSize.select_region(0, text_normalizationLayerlocalSize.get_text_length()); - m_grid_normalizationLayerType.attach(text_normalizationLayerlocalSize,2,5,1,1); - text_normalizationLayerlocalSize.show(); - - label_normalizationLayerAlpha.set_text("Inner Parameter - alpha: "); - label_normalizationLayerAlpha.set_line_wrap(); - label_normalizationLayerAlpha.set_justify(Gtk::JUSTIFY_FILL); - m_grid_normalizationLayerType.attach(label_normalizationLayerAlpha,0,6,2,1); - label_normalizationLayerAlpha.show(); - - text_normalizationLayerAlpha.set_max_length(100); - text_normalizationLayerAlpha.set_text("0.0001"); - text_normalizationLayerAlpha.select_region(0, text_normalizationLayerAlpha.get_text_length()); - m_grid_normalizationLayerType.attach(text_normalizationLayerAlpha,2,6,1,1); - text_normalizationLayerAlpha.show(); - - label_normalizationLayerBeta.set_text("Inner Parameter - beta: "); - label_normalizationLayerBeta.set_line_wrap(); - label_normalizationLayerBeta.set_justify(Gtk::JUSTIFY_FILL); - m_grid_normalizationLayerType.attach(label_normalizationLayerBeta,0,7,2,1); - label_normalizationLayerBeta.show(); - - text_normalizationLayerBeta.set_max_length(100); - text_normalizationLayerBeta.set_text("0.0001"); - text_normalizationLayerBeta.select_region(0, text_normalizationLayerBeta.get_text_length()); - m_grid_normalizationLayerType.attach(text_normalizationLayerBeta,2,7,1,1); - text_normalizationLayerBeta.show(); - - label_normalizationLayerK.set_text("Inner Parameter - k: "); - label_normalizationLayerK.set_line_wrap(); - label_normalizationLayerK.set_justify(Gtk::JUSTIFY_FILL); - m_grid_normalizationLayerType.attach(label_normalizationLayerK,0,8,2,1); - label_normalizationLayerK.show(); - - text_normalizationLayerK.set_max_length(100); - text_normalizationLayerK.set_text("1"); - text_normalizationLayerK.select_region(0, text_normalizationLayerK.get_text_length()); - m_grid_normalizationLayerType.attach(text_normalizationLayerK,2,8,1,1); - text_normalizationLayerK.show(); - - label_normalizationLayerNormRegion.set_text("Inner Parameter - norm_region: "); - label_normalizationLayerNormRegion.set_line_wrap(); - label_normalizationLayerNormRegion.set_justify(Gtk::JUSTIFY_FILL); - m_grid_normalizationLayerType.attach(label_normalizationLayerNormRegion,0,9,2,1); - label_normalizationLayerNormRegion.show(); - - Gtk::RadioButton::Group group5 = rbutton_normalizationLayerLRNWithin.get_group(); - rbutton_normalizationLayerLRNAcross.set_group(group5); - rbutton_normalizationLayerLRNWithin.set_active(); - m_grid_normalizationLayerType.attach(rbutton_normalizationLayerLRNWithin,2,9,1,1); - rbutton_normalizationLayerLRNWithin.show(); - m_grid_normalizationLayerType.attach(rbutton_normalizationLayerLRNAcross,3,9,1,1); - rbutton_normalizationLayerLRNAcross.show(); - - label_normalizationLayerAcrossChannel.set_text("Inner Parameter - across_channels: \n(boolean- 0 or 1)"); - label_normalizationLayerAcrossChannel.set_line_wrap(); - label_normalizationLayerAcrossChannel.set_justify(Gtk::JUSTIFY_FILL); - m_grid_normalizationLayerType.attach(label_normalizationLayerAcrossChannel,0,10,2,1); - label_normalizationLayerAcrossChannel.show(); - - text_normalizationLayerAcrossChannel.set_max_length(100); - text_normalizationLayerAcrossChannel.set_text("0"); - text_normalizationLayerAcrossChannel.select_region(0, text_normalizationLayerAcrossChannel.get_text_length()); - m_grid_normalizationLayerType.attach(text_normalizationLayerAcrossChannel,2,10,1,1); - text_normalizationLayerAcrossChannel.show(); - - label_normalizationLayerNormalizeVariance.set_text("Inner Parameter - normalize_variance: \n(boolean- 0 or 1)"); - label_normalizationLayerNormalizeVariance.set_line_wrap(); - label_normalizationLayerNormalizeVariance.set_justify(Gtk::JUSTIFY_FILL); - m_grid_normalizationLayerType.attach(label_normalizationLayerNormalizeVariance,0,11,2,1); - label_normalizationLayerNormalizeVariance.show(); - - text_normalizationLayerNormalizeVariance.set_max_length(100); - text_normalizationLayerNormalizeVariance.set_text("0"); - text_normalizationLayerNormalizeVariance.select_region(0, text_normalizationLayerNormalizeVariance.get_text_length()); - m_grid_normalizationLayerType.attach(text_normalizationLayerNormalizeVariance,2,11,1,1); - text_normalizationLayerNormalizeVariance.show(); - - label_normalizationLayerEps.set_text("Inner Parameter - eps: "); - label_normalizationLayerEps.set_line_wrap(); - label_normalizationLayerEps.set_justify(Gtk::JUSTIFY_FILL); - m_grid_normalizationLayerType.attach(label_normalizationLayerEps,0,12,2,1); - label_normalizationLayerEps.show(); - - text_normalizationLayerEps.set_max_length(100); - text_normalizationLayerEps.set_text("100"); - text_normalizationLayerEps.select_region(0, text_normalizationLayerEps.get_text_length()); - m_grid_normalizationLayerType.attach(text_normalizationLayerEps,2,12,1,1); - text_normalizationLayerEps.show(); - - - m_sw_normalizationLayerType.add(m_grid_normalizationLayerType); - - - //Loss Layer Window - title_lossLayerType.set_text("Will be updated soon"); - title_lossLayerType.set_line_wrap(); - title_lossLayerType.set_justify(Gtk::JUSTIFY_FILL); - m_grid_lossLayerType.attach(title_lossLayerType,0,0,2,1); - title_lossLayerType.show(); - - - button_setLossParameters.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "setLossParameters")); - m_grid_lossLayerType.attach(button_setLossParameters,0,25,2,1); - - button_addMoreLayer4.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "addMoreLayer4")); - m_grid_lossLayerType.attach(button_addMoreLayer4,2,25,1,1); - - label_lossLayerBottom1.set_text("Bottom1 Layer Name: "); - label_lossLayerBottom1.set_line_wrap(); - label_lossLayerBottom1.set_justify(Gtk::JUSTIFY_FILL); - m_grid_lossLayerType.attach(label_lossLayerBottom1,0,1,2,1); - label_lossLayerBottom1.show(); - - text_lossLayerBottom1.set_max_length(100); - text_lossLayerBottom1.set_text(""); - text_lossLayerBottom1.select_region(0, text_lossLayerBottom1.get_text_length()); - m_grid_lossLayerType.attach(text_lossLayerBottom1,2,1,1,1); - text_lossLayerBottom1.show(); - - label_lossLayerBottom2.set_text("Bottom2 Layer Name: "); - label_lossLayerBottom2.set_line_wrap(); - label_lossLayerBottom2.set_justify(Gtk::JUSTIFY_FILL); - m_grid_lossLayerType.attach(label_lossLayerBottom2,0,2,2,1); - label_lossLayerBottom2.show(); - - text_lossLayerBottom2.set_max_length(100); - text_lossLayerBottom2.set_text(""); - text_lossLayerBottom2.select_region(0, text_lossLayerBottom2.get_text_length()); - m_grid_lossLayerType.attach(text_lossLayerBottom2,2,2,1,1); - text_lossLayerBottom2.show(); - - label_lossLayerTop.set_text("Top Layer Name: "); - label_lossLayerTop.set_line_wrap(); - label_lossLayerTop.set_justify(Gtk::JUSTIFY_FILL); - m_grid_lossLayerType.attach(label_lossLayerTop,0,3,2,1); - label_lossLayerTop.show(); - - text_lossLayerTop.set_max_length(100); - text_lossLayerTop.set_text(""); - text_lossLayerTop.select_region(0, text_lossLayerTop.get_text_length()); - m_grid_lossLayerType.attach(text_lossLayerTop,2,3,1,1); - text_lossLayerTop.show(); - - label_lossLayerName.set_text("Current Layer Name: "); - label_lossLayerName.set_line_wrap(); - label_lossLayerName.set_justify(Gtk::JUSTIFY_FILL); - m_grid_lossLayerType.attach(label_lossLayerName,0,4,2,1); - label_lossLayerName.show(); - - text_lossLayerName.set_max_length(100); - text_lossLayerName.set_text(""); - text_lossLayerName.select_region(0, text_lossLayerName.get_text_length()); - m_grid_lossLayerType.attach(text_lossLayerName,2,4,1,1); - text_lossLayerName.show(); - - label_lossLayerNormalize.set_text("Normalize: \n(bool value)"); - label_lossLayerNormalize.set_line_wrap(); - label_lossLayerNormalize.set_justify(Gtk::JUSTIFY_FILL); - m_grid_lossLayerType.attach(label_lossLayerNormalize,0,5,2,1); - label_lossLayerNormalize.show(); - - text_lossLayerNormalize.set_max_length(100); - text_lossLayerNormalize.set_text(""); - text_lossLayerNormalize.select_region(0, text_lossLayerNormalize.get_text_length()); - m_grid_lossLayerType.attach(text_lossLayerNormalize,2,5,1,1); - text_lossLayerNormalize.show(); - - label_lossLayerNormalize.set_text("Normalization: \n(select type)"); - label_lossLayerNormalize.set_line_wrap(); - label_lossLayerNormalize.set_justify(Gtk::JUSTIFY_FILL); - m_grid_lossLayerType.attach(label_lossLayerNormalize,0,6,2,1); - label_lossLayerNormalize.show(); - - - m_sw_lossLayerType.add(m_grid_lossLayerType); - - - //Extra Layer Window - title_extraLayerType.set_text("Will be updated soon"); - title_extraLayerType.set_line_wrap(); - title_extraLayerType.set_justify(Gtk::JUSTIFY_FILL); - m_grid_extraLayerType.attach(title_extraLayerType,0,0,2,1); - title_extraLayerType.show(); - - button_addMoreLayer5.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "addMoreLayer5")); - m_grid_extraLayerType.attach(button_addMoreLayer5,0,2,1,1); - - m_sw_extraLayerType.add(m_grid_extraLayerType); - - //Display Window - - set_title("Activation Layer"); - set_border_width(10); -// add(box_fullCnnLayerMatter); - m_sw_fullCnnLayerMatter.add(textView_fullCnnLayerMatter); - m_sw_fullCnnLayerMatter.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); - box_fullCnnLayerMatter.pack_start(m_sw_fullCnnLayerMatter); - button_editMore.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "editMore")); - button_deleteLayerAtEnd.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &NetworkCreator::on_button_clicked), "deleteLayerAtEnd")); - box_fullCnnLayerMatter.pack_start(buttonBox_fullCnnLayerMatter, Gtk::PACK_SHRINK); - buttonBox_fullCnnLayerMatter.pack_start(button_editMore, Gtk::PACK_SHRINK); - buttonBox_fullCnnLayerMatter.pack_start(button_deleteLayerAtEnd, Gtk::PACK_SHRINK); - buttonBox_fullCnnLayerMatter.set_border_width(5); - buttonBox_fullCnnLayerMatter.set_spacing(5); - buttonBox_fullCnnLayerMatter.set_layout(Gtk::BUTTONBOX_END); - buffer_fullCnnLayerMatter = Gtk::TextBuffer::create(); -} - -NetworkCreator::~NetworkCreator() -{ -} - - -void NetworkCreator::on_button_clicked(Glib::ustring data) -{ - if(data == "networkFileName") - { - networkFileName = text_networkFileName.get_text(); - std::cout << "Network File Name set as: " << networkFileName << std::endl; - } - else if(data == "activationLayerType") - { - showWindow_activationLayerType(activationLayerTypeData); - } - else if(data == "addMoreLayer") - { - showWindow_main(); - } - else if(data == "setActivationParameters") - { - activationLayerTypeMatter = ""; - if(activationLayerTypeData == "AbsVal" or activationLayerTypeData == "PReLU" or activationLayerTypeData == "Sigmoid" or activationLayerTypeData == "TanH" or activationLayerTypeData == "") - { - activationLayerTypeMatter = "layer{"; - activationLayerTypeMatter += "\n\tbottom: \"" + text_activationLayerBottom.get_text() + "\""; - activationLayerTypeMatter += "\n\ttop: \"" + text_activationLayerTop.get_text() + "\""; - activationLayerTypeMatter += "\n\tname: \"" + text_activationLayerName.get_text() + "\""; - if(activationLayerTypeData == "") - activationLayerTypeData = "AbsVal"; - activationLayerTypeMatter += "\n\ttype: \"" + activationLayerTypeData + "\""; - activationLayerTypeMatter += "\n}\n"; - } - else if(activationLayerTypeData == "ReLU") - { - activationLayerTypeMatter = "layer{"; - activationLayerTypeMatter += "\n\tbottom: \"" + text_activationLayerBottom.get_text() + "\""; - activationLayerTypeMatter += "\n\ttop: \"" + text_activationLayerTop.get_text() + "\""; - activationLayerTypeMatter += "\n\tname: \"" + text_activationLayerName.get_text() + "\""; - activationLayerTypeMatter += "\n\ttype: \"" + activationLayerTypeData + "\""; - activationLayerTypeMatter += "\n\trelu_param{"; - activationLayerTypeMatter += "\n\t\tnegative_slope: " + text_activationLayerNegativeSlope.get_text(); - activationLayerTypeMatter += "\n\t}"; - activationLayerTypeMatter += "\n}\n"; - } - else if(activationLayerTypeData == "Exp" or activationLayerTypeData == "Log") - { - - activationLayerTypeMatter = "layer{"; - activationLayerTypeMatter += "\n\tbottom: \"" + text_activationLayerBottom.get_text() + "\""; - activationLayerTypeMatter += "\n\ttop: \"" + text_activationLayerTop.get_text() + "\""; - activationLayerTypeMatter += "\n\tname: \"" + text_activationLayerName.get_text() + "\""; - activationLayerTypeMatter += "\n\ttype: \"" + activationLayerTypeData + "\""; - if(activationLayerTypeData == "Exp") - activationLayerTypeMatter += "\n\texp_param{"; - else if(activationLayerTypeData == "Log") - activationLayerTypeMatter += "\n\texp_param{"; - activationLayerTypeMatter += "\n\t\tscale: " + text_activationLayerScale.get_text(); - activationLayerTypeMatter += "\n\t\tshift: " + text_activationLayerShift.get_text(); - activationLayerTypeMatter += "\n\t\tbase: " + text_activationLayerBase.get_text(); - activationLayerTypeMatter += "\n\t}"; - activationLayerTypeMatter += "\n}\n"; - } - else if(activationLayerTypeData == "Power") - { - activationLayerTypeMatter = "layer{"; - activationLayerTypeMatter += "\n\tbottom: \"" + text_activationLayerBottom.get_text() + "\""; - activationLayerTypeMatter += "\n\ttop: \"" + text_activationLayerTop.get_text() + "\""; - activationLayerTypeMatter += "\n\tname: \"" + text_activationLayerName.get_text() + "\""; - activationLayerTypeMatter += "\n\ttype: \"" + data + "\""; - activationLayerTypeMatter += "\n\tpower_param{"; - activationLayerTypeMatter += "\n\t\tscale: " + text_activationLayerScale.get_text(); - activationLayerTypeMatter += "\n\t\tshift: " + text_activationLayerShift.get_text(); - activationLayerTypeMatter += "\n\t\tpower: " + text_activationLayerBase.get_text(); - activationLayerTypeMatter += "\n\t}"; - activationLayerTypeMatter += "\n}\n"; - } - - -// std::cout << activationLayerTypeMatter << std::endl; - if(numLayers == 0) - { - initializeLayer(headLayer,activationLayerTypeMatter); - numLayers++; - fullCnnLayers.push_back(activationLayerTypeMatter); - } - else - { - appendLayer(headLayer,activationLayerTypeMatter); - numLayers++; - fullCnnLayers.push_back(activationLayerTypeMatter); - } - } - else if(data == "displayCnnLayers") - { - fullCnnLayerMatter = displayCNN(headLayer); - showWindow_displayWindow(); - } - else if(data == "editMore") - { - showWindow_main(); - } - else if(data == "deleteLayerAtEnd") - { - if(numLayers>1) //cannot delete the first created layer - { - Glib::ustring lastLayer = fullCnnLayers[numLayers-1]; -// std::cout << lastLayer << std:: endl; - fullCnnLayers.pop_back(); - numLayers--; - Node *layer = searchLayer(headLayer,lastLayer); - if(deleteLayer(&headLayer,layer)) - std::cout << "numLayers = "<< numLayers << "\n"; - fullCnnLayerMatter = displayCNN(headLayer); - showWindow_displayWindow(); - } - - } - else if(data == "criticalLayerType") - { - showWindow_criticalLayerType(criticalLayerTypeData); - } - else if(data == "addMoreLayer2") - { - showWindow_main(); - } - else if(data == "setCriticalParameters") - { - criticalLayerTypeMatter = ""; - if(criticalLayerTypeData == "") - criticalLayerTypeData = "Accuracy"; - if(criticalLayerTypeData == "Accuracy" or criticalLayerTypeData == "Softmax") - { - criticalLayerTypeMatter = "layer{"; - criticalLayerTypeMatter += "\n\tbottom: \"" + text_criticalLayerBottom1.get_text() + "\""; - criticalLayerTypeMatter += "\n\tbottom: \"" + text_criticalLayerBottom2.get_text() + "\""; - criticalLayerTypeMatter += "\n\ttop: \"" + text_criticalLayerTop.get_text() + "\""; - criticalLayerTypeMatter += "\n\tname: \"" + text_criticalLayerName.get_text() + "\""; - criticalLayerTypeMatter += "\n\ttype: \"" + criticalLayerTypeData + "\""; - criticalLayerTypeMatter += "\n\tinclude{"; - criticalLayerTypeMatter += "\n\t\tphase: TEST"; - criticalLayerTypeMatter += "\n\t}"; - criticalLayerTypeMatter += "\n}\n"; - } - else if(criticalLayerTypeData == "Convolution" or criticalLayerTypeData == "Deconvolution") - { - criticalLayerTypeMatter = "layer{"; - criticalLayerTypeMatter += "\n\tbottom: \"" + text_criticalLayerBottom1.get_text() + "\""; - criticalLayerTypeMatter += "\n\ttop: \"" + text_criticalLayerTop.get_text() + "\""; - criticalLayerTypeMatter += "\n\tname: \"" + text_criticalLayerName.get_text() + "\""; - criticalLayerTypeMatter += "\n\ttype: \"" + criticalLayerTypeData + "\""; - criticalLayerTypeMatter += "\n\tparam{"; - criticalLayerTypeMatter += "\n\t\tlr_mult: " + text_criticalLayerFilterLr.get_text(); - criticalLayerTypeMatter += "\n\t\tdecay_mult: " + text_criticalLayerFilterDm.get_text(); - criticalLayerTypeMatter += "\n\t}"; - criticalLayerTypeMatter += "\n\tparam{"; - criticalLayerTypeMatter += "\n\t\tlr_mult: " + text_criticalLayerBiasLr.get_text(); - criticalLayerTypeMatter += "\n\t\tdecay_mult: " + text_criticalLayerBiasDm.get_text(); - criticalLayerTypeMatter += "\n\t}"; - criticalLayerTypeMatter += "\n\tconvolution_param{"; - criticalLayerTypeMatter += "\n\t\tnum_output: " + text_criticalLayerNumOutput.get_text(); - criticalLayerTypeMatter += "\n\t\tkernel_w: " + text_criticalLayerKernelW.get_text(); - criticalLayerTypeMatter += "\n\t\tkernel_h: " + text_criticalLayerKernelH.get_text(); - criticalLayerTypeMatter += "\n\t\tstride_w: " + text_criticalLayerStrideW.get_text(); - criticalLayerTypeMatter += "\n\t\tstride_h: " + text_criticalLayerStrideH.get_text(); - criticalLayerTypeMatter += "\n\t\tpad_w: " + text_criticalLayerPadW.get_text(); - criticalLayerTypeMatter += "\n\t\tpad_h: " + text_criticalLayerPadH.get_text(); - criticalLayerTypeMatter += "\n\t\tweight_filler{"; - if(rbutton_criticalLayerWeightFillerConstant.get_active() == 1) - { - criticalLayerTypeMatter += "\n\t\t\ttype: \"constant\""; - criticalLayerTypeMatter += "\n\t\t\tvalue: " + text_criticalLayerWeightFillerConstantValue.get_text(); - } - else if(rbutton_criticalLayerWeightFillerUniform.get_active() == 1) - { - criticalLayerTypeMatter += "\n\t\t\ttype: \"uniform\""; - criticalLayerTypeMatter += "\n\t\t\tmin: " + text_criticalLayerWeightFillerUniformMin.get_text(); - criticalLayerTypeMatter += "\n\t\t\tmax: " + text_criticalLayerWeightFillerUniformMax.get_text(); - } - else if(rbutton_criticalLayerWeightGaussian.get_active() == 1) - { - criticalLayerTypeMatter += "\n\t\t\ttype: \"gaussian\""; - criticalLayerTypeMatter += "\n\t\t\tmean: " + text_criticalLayerWeightFillerGaussianMean.get_text(); - criticalLayerTypeMatter += "\n\t\t\tstd: " + text_criticalLayerWeightFillerGaussianStd.get_text(); - } - else if(rbutton_criticalLayerWeightFillerPositiveUnitBall.get_active() == 1) - { - criticalLayerTypeMatter += "\n\t\t\ttype: \"positive_unitball\""; - } - else if(rbutton_criticalLayerWeightFillerXavier.get_active() == 1) - { - criticalLayerTypeMatter += "\n\t\t\ttype: \"xavier\""; - if(rbutton_criticalLayerWeightFillerXavierIn.get_active() == 1) - criticalLayerTypeMatter += "\n\t\t\tvariance_norm: FAN_IN"; - else if(rbutton_criticalLayerWeightFillerXavierOut.get_active() == 1) - criticalLayerTypeMatter += "\n\t\t\tvariance_norm: FAN_OUT"; - else if(rbutton_criticalLayerWeightFillerXavierAvg.get_active() == 1) - criticalLayerTypeMatter += "\n\t\t\tvariance_norm: AVERAGE"; - } - else if(rbutton_criticalLayerWeightFillerMSRA.get_active() == 1) - { - criticalLayerTypeMatter += "\n\t\t\ttype: \"msra\""; - if(rbutton_criticalLayerWeightFillerMSRAIn.get_active() == 1) - criticalLayerTypeMatter += "\n\t\t\tvariance_norm: FAN_IN"; - else if(rbutton_criticalLayerWeightFillerMSRAOut.get_active() == 1) - criticalLayerTypeMatter += "\n\t\t\tvariance_norm: FAN_OUT"; - else if(rbutton_criticalLayerWeightFillerMSRAAvg.get_active() == 1) - criticalLayerTypeMatter += "\n\t\t\tvariance_norm: AVERAGE"; - } - else if(rbutton_criticalLayerWeightFillerBilinear.get_active() == 1) - { - criticalLayerTypeMatter += "\n\t\t\ttype: \"bilinear\""; - } - criticalLayerTypeMatter += "\n\t\t}"; - criticalLayerTypeMatter += "\n\t\tbias_filler{"; - criticalLayerTypeMatter += "\n\t\t\ttype: \"constant\""; //only type of filler as of now - criticalLayerTypeMatter += "\n\t\t\tvalue: " + text_criticalLayerBias.get_text(); - criticalLayerTypeMatter += "\n\t\t}"; - criticalLayerTypeMatter += "\n\t}"; - criticalLayerTypeMatter += "\n}\n"; - - } - else if(criticalLayerTypeData == "Dropout") - { - criticalLayerTypeMatter = "layer{"; - criticalLayerTypeMatter += "\n\tbottom: \"" + text_criticalLayerBottom1.get_text() + "\""; - criticalLayerTypeMatter += "\n\ttop: \"" + text_criticalLayerTop.get_text() + "\""; - criticalLayerTypeMatter += "\n\tname: \"" + text_criticalLayerName.get_text() + "\""; - criticalLayerTypeMatter += "\n\ttype: \"" + criticalLayerTypeData + "\""; - criticalLayerTypeMatter += "\n\tdropout_param{"; - criticalLayerTypeMatter += "\n\t\tdropout_ratio: " + text_criticalLayerDropoutRatio.get_text(); - criticalLayerTypeMatter += "\n\t}"; - criticalLayerTypeMatter += "\n}\n"; - } - else if(criticalLayerTypeData == "InnerProduct") - { - criticalLayerTypeMatter = "layer{"; - criticalLayerTypeMatter += "\n\tbottom: \"" + text_criticalLayerBottom1.get_text() + "\""; - criticalLayerTypeMatter += "\n\ttop: \"" + text_criticalLayerTop.get_text() + "\""; - criticalLayerTypeMatter += "\n\tname: \"" + text_criticalLayerName.get_text() + "\""; - criticalLayerTypeMatter += "\n\ttype: \"" + criticalLayerTypeData + "\""; - criticalLayerTypeMatter += "\n\tparam{"; - criticalLayerTypeMatter += "\n\t\tlr_mult: " + text_criticalLayerFilterLr.get_text(); - criticalLayerTypeMatter += "\n\t\tdecay_mult: " + text_criticalLayerFilterDm.get_text(); - criticalLayerTypeMatter += "\n\t}"; - criticalLayerTypeMatter += "\n\tparam{"; - criticalLayerTypeMatter += "\n\t\tlr_mult: " + text_criticalLayerBiasLr.get_text(); - criticalLayerTypeMatter += "\n\t\tdecay_mult: " + text_criticalLayerBiasDm.get_text(); - criticalLayerTypeMatter += "\n\t}"; - criticalLayerTypeMatter += "\n\tinner_product_param{"; - criticalLayerTypeMatter += "\n\t\tnum_output: " + text_criticalLayerNumOutput.get_text(); - criticalLayerTypeMatter += "\n\t\tweight_filler{"; - if(rbutton_criticalLayerWeightFillerConstant.get_active() == 1) - { - criticalLayerTypeMatter += "\n\t\t\ttype: \"constant\""; - criticalLayerTypeMatter += "\n\t\t\tvalue: " + text_criticalLayerWeightFillerConstantValue.get_text(); - } - else if(rbutton_criticalLayerWeightFillerUniform.get_active() == 1) - { - criticalLayerTypeMatter += "\n\t\t\ttype: \"uniform\""; - criticalLayerTypeMatter += "\n\t\t\tmin: " + text_criticalLayerWeightFillerUniformMin.get_text(); - criticalLayerTypeMatter += "\n\t\t\tmax: " + text_criticalLayerWeightFillerUniformMax.get_text(); - } - else if(rbutton_criticalLayerWeightGaussian.get_active() == 1) - { - criticalLayerTypeMatter += "\n\t\t\ttype: \"gaussian\""; - criticalLayerTypeMatter += "\n\t\t\tmean: " + text_criticalLayerWeightFillerGaussianMean.get_text(); - criticalLayerTypeMatter += "\n\t\t\tstd: " + text_criticalLayerWeightFillerGaussianStd.get_text(); - } - else if(rbutton_criticalLayerWeightFillerPositiveUnitBall.get_active() == 1) - { - criticalLayerTypeMatter += "\n\t\t\ttype: \"positive_unitball\""; - } - else if(rbutton_criticalLayerWeightFillerXavier.get_active() == 1) - { - criticalLayerTypeMatter += "\n\t\t\ttype: \"xavier\""; - if(rbutton_criticalLayerWeightFillerXavierIn.get_active() == 1) - criticalLayerTypeMatter += "\n\t\t\tvariance_norm: FAN_IN"; - else if(rbutton_criticalLayerWeightFillerXavierOut.get_active() == 1) - criticalLayerTypeMatter += "\n\t\t\tvariance_norm: FAN_OUT"; - else if(rbutton_criticalLayerWeightFillerXavierAvg.get_active() == 1) - criticalLayerTypeMatter += "\n\t\t\tvariance_norm: AVERAGE"; - } - else if(rbutton_criticalLayerWeightFillerMSRA.get_active() == 1) - { - criticalLayerTypeMatter += "\n\t\t\ttype: \"msra\""; - if(rbutton_criticalLayerWeightFillerMSRAIn.get_active() == 1) - criticalLayerTypeMatter += "\n\t\t\tvariance_norm: FAN_IN"; - else if(rbutton_criticalLayerWeightFillerMSRAOut.get_active() == 1) - criticalLayerTypeMatter += "\n\t\t\tvariance_norm: FAN_OUT"; - else if(rbutton_criticalLayerWeightFillerMSRAAvg.get_active() == 1) - criticalLayerTypeMatter += "\n\t\t\tvariance_norm: AVERAGE"; - } - else if(rbutton_criticalLayerWeightFillerBilinear.get_active() == 1) - { - criticalLayerTypeMatter += "\n\t\t\ttype: \"bilinear\""; - } - criticalLayerTypeMatter += "\n\t\t}"; - criticalLayerTypeMatter += "\n\t\tbias_filler{"; - criticalLayerTypeMatter += "\n\t\t\ttype: \"constant\""; //only type of filler as of now - criticalLayerTypeMatter += "\n\t\t\tvalue: " + text_criticalLayerBias.get_text(); - criticalLayerTypeMatter += "\n\t\t}"; - criticalLayerTypeMatter += "\n\t}"; - criticalLayerTypeMatter += "\n}\n"; - } - else if(criticalLayerTypeData == "Pooling") - { - criticalLayerTypeMatter = "layer{"; - criticalLayerTypeMatter += "\n\tbottom: \"" + text_criticalLayerBottom1.get_text() + "\""; - criticalLayerTypeMatter += "\n\ttop: \"" + text_criticalLayerTop.get_text() + "\""; - criticalLayerTypeMatter += "\n\tname: \"" + text_criticalLayerName.get_text() + "\""; - criticalLayerTypeMatter += "\n\ttype: \"" + criticalLayerTypeData + "\""; - criticalLayerTypeMatter += "\n\tparam{"; - criticalLayerTypeMatter += "\n\t\tlr_mult: " + text_criticalLayerFilterLr.get_text(); - criticalLayerTypeMatter += "\n\t\tdecay_mult: " + text_criticalLayerFilterDm.get_text(); - criticalLayerTypeMatter += "\n\t}"; - criticalLayerTypeMatter += "\n\tparam{"; - criticalLayerTypeMatter += "\n\t\tlr_mult: " + text_criticalLayerBiasLr.get_text(); - criticalLayerTypeMatter += "\n\t\tdecay_mult: " + text_criticalLayerBiasDm.get_text(); - criticalLayerTypeMatter += "\n\t}"; - criticalLayerTypeMatter += "\n\tpooling_param{"; - criticalLayerTypeMatter += "\n\t\tnum_output: " + text_criticalLayerNumOutput.get_text(); - criticalLayerTypeMatter += "\n\t\tkernel_w: " + text_criticalLayerKernelW.get_text(); - criticalLayerTypeMatter += "\n\t\tkernel_h: " + text_criticalLayerKernelH.get_text(); - criticalLayerTypeMatter += "\n\t\tstride_w: " + text_criticalLayerStrideW.get_text(); - criticalLayerTypeMatter += "\n\t\tstride_h: " + text_criticalLayerStrideH.get_text(); - criticalLayerTypeMatter += "\n\t\tpad_w: " + text_criticalLayerPadW.get_text(); - criticalLayerTypeMatter += "\n\t\tpad_h: " + text_criticalLayerPadH.get_text(); - if(rbutton_criticalLayerPoolMax.get_active() == 1) - criticalLayerTypeMatter += "\n\t\tpool: MAX"; - else if(rbutton_criticalLayerPoolAve.get_active() == 1) - criticalLayerTypeMatter += "\n\t\tpool: AVE"; - criticalLayerTypeMatter += "\n\t}"; - criticalLayerTypeMatter += "\n}\n"; - } - if(numLayers == 0) - { - initializeLayer(headLayer,criticalLayerTypeMatter); - numLayers++; - fullCnnLayers.push_back(criticalLayerTypeMatter); - } - else - { - appendLayer(headLayer,criticalLayerTypeMatter); - numLayers++; - fullCnnLayers.push_back(criticalLayerTypeMatter); - } - } - else if(data == "normalizationLayerType") - { - showWindow_normalizationLayerType(normalizationLayerTypeData); - } - else if(data == "setNormalizationParameters") - { - normalizationLayerTypeMatter = ""; - if(normalizationLayerTypeData == "BatchNorm" or normalizationLayerTypeData == "") - { - normalizationLayerTypeMatter = "layer{"; - normalizationLayerTypeMatter += "\n\tbottom: \"" + text_normalizationLayerBottom.get_text() + "\""; - normalizationLayerTypeMatter += "\n\ttop: \"" + text_normalizationLayerTop.get_text() + "\""; - normalizationLayerTypeMatter += "\n\tname: \"" + text_normalizationLayerName.get_text() + "\""; - if(normalizationLayerTypeData == "") - normalizationLayerTypeData = "BatchNorm"; - normalizationLayerTypeMatter += "\n\ttype: \"" + normalizationLayerTypeData + "\""; - normalizationLayerTypeMatter += "\n}\n"; - } - else if(normalizationLayerTypeData == "LRN") - { - normalizationLayerTypeMatter = "layer{"; - normalizationLayerTypeMatter += "\n\tbottom: \"" + text_normalizationLayerBottom.get_text() + "\""; - normalizationLayerTypeMatter += "\n\ttop: \"" + text_normalizationLayerTop.get_text() + "\""; - normalizationLayerTypeMatter += "\n\tname: \"" + text_normalizationLayerName.get_text() + "\""; - normalizationLayerTypeMatter += "\n\ttype: \"" + normalizationLayerTypeData + "\""; - normalizationLayerTypeMatter += "\n\tlrn_param{"; - normalizationLayerTypeMatter += "\n\t\tlocal_size: " + text_normalizationLayerlocalSize.get_text(); - normalizationLayerTypeMatter += "\n\t\talpha: " + text_normalizationLayerAlpha.get_text(); - normalizationLayerTypeMatter += "\n\t\tbeta: " + text_normalizationLayerBeta.get_text(); - normalizationLayerTypeMatter += "\n\t\tk: " + text_normalizationLayerK.get_text(); - if(rbutton_normalizationLayerLRNWithin.get_active() == 1) - normalizationLayerTypeMatter += "\n\t\tnorm_region: WITHIN_CHANNEL"; - else - normalizationLayerTypeMatter += "\n\t\tnorm_region: ACROSS_CHANNEL"; - normalizationLayerTypeMatter += "\n\t}"; - normalizationLayerTypeMatter += "\n}\n"; - } - else if(normalizationLayerTypeData == "MVN") - { - normalizationLayerTypeMatter = "layer{"; - normalizationLayerTypeMatter += "\n\tbottom: \"" + text_normalizationLayerBottom.get_text() + "\""; - normalizationLayerTypeMatter += "\n\ttop: \"" + text_normalizationLayerTop.get_text() + "\""; - normalizationLayerTypeMatter += "\n\tname: \"" + text_normalizationLayerName.get_text() + "\""; - normalizationLayerTypeMatter += "\n\ttype: \"" + normalizationLayerTypeData + "\""; - normalizationLayerTypeMatter += "\n\tmvn_param{"; - normalizationLayerTypeMatter += "\n\t\tacross_channels: " + text_normalizationLayerAcrossChannel.get_text(); - normalizationLayerTypeMatter += "\n\t\tnormalize_variance: " + text_normalizationLayerNormalizeVariance.get_text(); - normalizationLayerTypeMatter += "\n\t\teps: " + text_normalizationLayerEps.get_text(); - normalizationLayerTypeMatter += "\n\t}"; - normalizationLayerTypeMatter += "\n}\n"; - } - if(numLayers == 0) - { - initializeLayer(headLayer,normalizationLayerTypeMatter); - numLayers++; - fullCnnLayers.push_back(normalizationLayerTypeMatter); - } - else - { - appendLayer(headLayer,normalizationLayerTypeMatter); - numLayers++; - fullCnnLayers.push_back(normalizationLayerTypeMatter); - } - } - else if(data == "addMoreLayer3") - { - showWindow_main(); - } - else if(data == "lossLayerType") - { - showWindow_lossLayerType(lossLayerTypeData); - } - else if(data == "addMoreLayer4") - { - showWindow_main(); - } - else if(data == "extraLayerType") - { - showWindow_extraLayerType(extraLayerTypeData); - } - else if(data == "addMoreLayer5") - { - showWindow_main(); - } - else if(data == "saveFile") - { - networkFileName = text_networkFileName.get_text(); - std::ofstream myfile; - myfile.open(networkFileName); - std::cout << "Network File Name saved as: " << networkFileName << std::endl; - myfile << "#File generated using OpenDetection" << std::endl; - myfile.close(); - } - -} - - diff --git a/detectors/src/global2D/training/Solver.cpp b/detectors/src/global2D/training/Solver.cpp deleted file mode 100644 index 636a2b24..00000000 --- a/detectors/src/global2D/training/Solver.cpp +++ /dev/null @@ -1,1001 +0,0 @@ -#include "od/detectors/global2D/training/Solver.h" -#include <iostream> -#include <fstream> - -SolverProperties::SolverProperties(): - label_solverFileName(""), - button_solverFileName("Update"), - text_solverFileName(), - - label_trainNetworkFileType(""), - rbutton_trainNetworkFileType_net("net"), rbutton_trainNetworkFileType_tt("train_net"), - label_trainNetworkFileName(""), - button_trainNetworkFileName("Update"), - text_trainNetworkFileName(), - - label_enableTestNet(""), - rbutton_enableTestNet_no("No"), rbutton_enableTestNet_yes("Yes"), - label_testNetworkFileName(""), - button_testNetworkFileName("Update"), - text_testNetworkFileName(), - - label_enableValidationParameters(""), - rbutton_enableValidationParameters_no("No"), rbutton_enableValidationParameters_yes("Yes"), - label_testIter(""), - button_testIter("Update"), - text_testIter(), - - label_testInterval(""), - button_testInterval("Update"), - text_testInterval(), - - label_enableAverageLoss(""), - rbutton_enableAverageLoss_no("No"), rbutton_enableAverageLoss_yes("Yes"), - label_averageLoss(""), - button_averageLoss("Update"), - text_averageLoss(), - - label_enableRandomSample(""), - rbutton_enableRandomSample_no("No"), rbutton_enableRandomSample_yes("Yes"), - label_randomSample(""), - button_randomSample("Update"), - text_randomSample(), - - label_display(""), - button_display("Update"), - text_display(), - - label_enableDebugInfo(""), - rbutton_enableDebugInfo_no("No"), rbutton_enableDebugInfo_yes("Yes"), - button_debugInfo("Update"), - - label_snapshot(""), - button_snapshot("Update"), - text_snapshot(), - - label_enableTestComputeLoss(""), - rbutton_enableTestComputeLoss_no("No"), rbutton_enableTestComputeLoss_yes("Yes"), - button_testComputeLoss("Update"), - - label_snapshotPrefix(""), - button_snapshotPrefix("Update"), - text_snapshotPrefix(), - - label_maxIter(""), - button_maxIter("Update"), - text_maxIter(), - - label_type(""), - button_type("Update"), - rbutton_typeSGD_yes("SGD"), rbutton_typeAdadelta_yes("AdaDelta"), rbutton_typeAdagrad_yes("AdaGrad"), rbutton_typeAdam_yes("Adam"), - rbutton_typeRMSProp_yes("RMSProp"), rbutton_typeNesterov_yes("Nesterov"), - - label_learningRatePolicy(""), - button_learningRatePolicy("Update"), - rbutton_learningRatePolicyFixed_yes("fixed"), rbutton_learningRatePolicyExp_yes("exp"), rbutton_learningRatePolicyStep_yes("step"), - rbutton_learningRatePolicyInv_yes("inv"), rbutton_learningRatePolicyMultistep_yes("multistep"), - rbutton_learningRatePolicyPoly_yes("poly"), rbutton_learningRatePolicySigmoid_yes("sigmoid"), - - label_baseLearningRate(""), - button_baseLearningRate("Update"), - text_baseLearningRate(), - - label_gamma(""), - button_gamma("Update"), - text_gamma(), - - label_power(""), - button_power("Update"), - text_power(), - - label_stepSize(""), - button_stepSize("Update"), - text_stepSize(), - - label_stepSizeValue(""), - button_stepSizeValue("Update"), - text_stepSizeValue(), - - label_weightDecay(""), - button_weightDecay("Update"), - text_weightDecay(), - - label_momentum(""), - button_momentum("Update"), - text_momentum(), - - button_saveFile("Save File") -{ - set_title("Solver"); - set_border_width(10); - add(m_sw1); - m_grid1.set_column_spacing (10); - m_grid1.set_row_spacing (50); - - //level0 - - label_solverFileName.set_text("1) Give a proper name to the solver file: "); - label_solverFileName.set_line_wrap(); - label_solverFileName.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_solverFileName,0,0,2,1); - label_solverFileName.show(); - - text_solverFileName.set_max_length(100); - text_solverFileName.set_text("../examples/objectdetector/Mnist_Train/solverCustom1.prototxt"); - text_solverFileName.select_region(0, text_solverFileName.get_text_length()); - m_grid1.attach(text_solverFileName,2,0,5,1); - text_solverFileName.show(); - - button_solverFileName.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "solverFileName")); - m_grid1.attach(button_solverFileName,7,0,1,1); - button_solverFileName.show(); - - //level1 - - label_trainNetworkFileType.set_text("2)Select type of training network file type.\nUsually these exists two types,\nfirst adds validation and training in the same file,\nWhile other adds them in two different files"); - label_trainNetworkFileType.set_line_wrap(); - label_trainNetworkFileType.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_trainNetworkFileType,0,1,2,1); - label_trainNetworkFileType.show(); - - Gtk::RadioButton::Group group = rbutton_trainNetworkFileType_net.get_group(); - rbutton_trainNetworkFileType_tt.set_group(group); - rbutton_trainNetworkFileType_net.set_active(); - m_grid1.attach(rbutton_trainNetworkFileType_net,2,1,1,1); - rbutton_trainNetworkFileType_net.show(); - m_grid1.attach(rbutton_trainNetworkFileType_tt,3,1,1,1); - rbutton_trainNetworkFileType_tt.show(); - - //level2 - - label_trainNetworkFileName.set_text("2.1) net: or train_net:\n(Parameter Details: Give location of \nthe net file or the train_net file) "); - label_trainNetworkFileName.set_line_wrap(); - label_trainNetworkFileName.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_trainNetworkFileName,0,2,2,1); - label_trainNetworkFileName.show(); - - text_trainNetworkFileName.set_max_length(500); - text_trainNetworkFileName.set_text("../examples/objectdetector/Mnist_Train/train1.prototxt"); - text_trainNetworkFileName.select_region(0, text_solverFileName.get_text_length()); - m_grid1.attach(text_trainNetworkFileName,2,2,5,1); - text_trainNetworkFileName.show(); - - button_trainNetworkFileName.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "trainNetworkFileName")); - m_grid1.attach(button_trainNetworkFileName,7,2,1,1); - button_trainNetworkFileName.show(); - - //level3 - - label_enableTestNet.set_text("3) Enable Test Network Parameter:\n(Enable only with using \"train_net\" parameter.)"); - label_enableTestNet.set_line_wrap(); - label_enableTestNet.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_enableTestNet,0,3,2,1); - label_enableTestNet.show(); - - Gtk::RadioButton::Group group2 = rbutton_enableTestNet_no.get_group(); - rbutton_enableTestNet_yes.set_group(group2); - rbutton_enableTestNet_no.set_active(); - m_grid1.attach(rbutton_enableTestNet_no,2,3,1,1); - rbutton_enableTestNet_no.show(); - m_grid1.attach(rbutton_enableTestNet_yes,3,3,1,1); - rbutton_enableTestNet_yes.show(); - - label_testNetworkFileName.set_text("3.1) test_net:"); - label_testNetworkFileName.set_line_wrap(); - label_testNetworkFileName.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_testNetworkFileName,4,3,1,1); - label_testNetworkFileName.show(); - - text_testNetworkFileName.set_max_length(500); - text_testNetworkFileName.set_text("../examples/objectdetector/Mnist_Train/test1.prototxt"); - text_testNetworkFileName.select_region(0, text_testNetworkFileName.get_text_length()); - m_grid1.attach(text_testNetworkFileName,5,3,3,1); - text_testNetworkFileName.show(); - - button_testNetworkFileName.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "testNetworkFileName")); - m_grid1.attach(button_testNetworkFileName,8,3,1,1); - button_testNetworkFileName.show(); - - - //level4 - - label_enableValidationParameters.set_text("4) Enable Validation(test) phase Parameters:\nParameters are \"test_iter\" and \"test_interval\""); - label_enableValidationParameters.set_line_wrap(); - label_enableValidationParameters.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_enableValidationParameters,0,4,2,1); - label_enableValidationParameters.show(); - - Gtk::RadioButton::Group group3 = rbutton_enableValidationParameters_no.get_group(); - rbutton_enableValidationParameters_yes.set_group(group3); - rbutton_enableValidationParameters_no.set_active(); - m_grid1.attach(rbutton_enableValidationParameters_no,2,4,1,1); - rbutton_enableValidationParameters_no.show(); - m_grid1.attach(rbutton_enableValidationParameters_yes,3,4,1,1); - rbutton_enableValidationParameters_yes.show(); - - //level 5 - - label_testIter.set_text("4.1) test_iter:\n(Set number of iterations during validation phase)"); - label_testIter.set_line_wrap(); - label_testIter.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_testIter,0,5,2,1); - label_testIter.show(); - - text_testIter.set_max_length(100); - text_testIter.set_text("100"); - text_testIter.select_region(0, text_testNetworkFileName.get_text_length()); - m_grid1.attach(text_testIter,2,5,1,1); - text_testIter.show(); - - button_testIter.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "testIter")); - m_grid1.attach(button_testIter,3,5,1,1); - button_testIter.show(); - - - //level 6 - - label_testInterval.set_text("4.2) test_interval:\n(Specifies that after a set of mentioned training iterations,\na validation phase is initiated)"); - label_testInterval.set_line_wrap(); - label_testInterval.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_testInterval,0,6,2,1); - label_testInterval.show(); - - text_testInterval.set_max_length(100); - text_testInterval.set_text("100"); - text_testInterval.select_region(0, text_testNetworkFileName.get_text_length()); - m_grid1.attach(text_testInterval,2,6,1,1); - text_testInterval.show(); - - button_testInterval.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "testInterval")); - m_grid1.attach(button_testInterval,3,6,1,1); - button_testInterval.show(); - - //level 7 - - label_enableAverageLoss.set_text("5) Enable \"average_loss\" parameter: "); - label_enableAverageLoss.set_line_wrap(); - label_enableAverageLoss.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_enableAverageLoss,0,7,2,1); - label_enableAverageLoss.show(); - - Gtk::RadioButton::Group group4 = rbutton_enableAverageLoss_no.get_group(); - rbutton_enableAverageLoss_yes.set_group(group4); - rbutton_enableAverageLoss_no.set_active(); - m_grid1.attach(rbutton_enableAverageLoss_no,2,7,1,1); - rbutton_enableAverageLoss_no.show(); - m_grid1.attach(rbutton_enableAverageLoss_yes,3,7,1,1); - rbutton_enableAverageLoss_yes.show(); - - label_averageLoss.set_text("average_loss:"); - label_averageLoss.set_line_wrap(); - label_averageLoss.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_averageLoss,4,7,1,1); - label_averageLoss.show(); - - text_averageLoss.set_max_length(100); - text_averageLoss.set_text("1.0"); - text_averageLoss.select_region(0, text_averageLoss.get_text_length()); - m_grid1.attach(text_averageLoss,5,7,1,1); - text_averageLoss.show(); - - button_averageLoss.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "averageLoss")); - m_grid1.attach(button_averageLoss,6,7,1,1); - button_averageLoss.show(); - - - //level 8 - - label_enableRandomSample.set_text("6) Enable \"random_seed\" parameter: "); - label_enableRandomSample.set_line_wrap(); - label_enableRandomSample.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_enableRandomSample,0,8,2,1); - label_enableRandomSample.show(); - - Gtk::RadioButton::Group group5 = rbutton_enableRandomSample_no.get_group(); - rbutton_enableRandomSample_yes.set_group(group5); - rbutton_enableRandomSample_no.set_active(); - m_grid1.attach(rbutton_enableRandomSample_no,2,8,1,1); - rbutton_enableRandomSample_no.show(); - m_grid1.attach(rbutton_enableRandomSample_yes,3,8,1,1); - rbutton_enableRandomSample_yes.show(); - - label_randomSample.set_text("random_seed:"); - label_randomSample.set_line_wrap(); - label_randomSample.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_randomSample,4,8,1,1); - label_randomSample.show(); - - text_randomSample.set_max_length(100); - text_randomSample.set_text("1"); - text_randomSample.select_region(0, text_randomSample.get_text_length()); - m_grid1.attach(text_randomSample,5,8,1,1); - text_randomSample.show(); - - button_randomSample.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "randomSample")); - m_grid1.attach(button_randomSample,6,8,1,1); - button_randomSample.show(); - - //label 9 - - label_display.set_text("7)display:\n(Used to diplay output afer every specified number of iterations)"); - label_display.set_line_wrap(); - label_display.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_display,0,9,2,1); - label_display.show(); - - text_display.set_max_length(100); - text_display.set_text("100"); - text_display.select_region(0, text_display.get_text_length()); - m_grid1.attach(text_display,2,9,1,1); - text_display.show(); - - button_display.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "display")); - m_grid1.attach(button_display,3,9,1,1); - button_display.show(); - - - //level 10 - - label_enableDebugInfo.set_text("8) Enable \"debug_info\" parameter:\n(Used to see every step in training,\nsuitable for resolving bugs"); - label_enableDebugInfo.set_line_wrap(); - label_enableDebugInfo.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_enableDebugInfo,0,10,2,1); - label_enableDebugInfo.show(); - - Gtk::RadioButton::Group group6 = rbutton_enableDebugInfo_no.get_group(); - rbutton_enableDebugInfo_yes.set_group(group6); - rbutton_enableDebugInfo_no.set_active(); - m_grid1.attach(rbutton_enableDebugInfo_no,2,10,1,1); - rbutton_enableDebugInfo_no.show(); - m_grid1.attach(rbutton_enableDebugInfo_yes,3,10,1,1); - rbutton_enableDebugInfo_yes.show(); - - button_debugInfo.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "debugInfo")); - m_grid1.attach(button_debugInfo,4,10,1,1); - button_debugInfo.show(); - - //level 11 - - label_snapshot.set_text("9)snapshot:\n(Used to save trained weights afer every specified number of iterations)"); - label_snapshot.set_line_wrap(); - label_snapshot.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_snapshot,0,11,2,1); - label_snapshot.show(); - - text_snapshot.set_max_length(100); - text_snapshot.set_text("100"); - text_snapshot.select_region(0, text_snapshot.get_text_length()); - m_grid1.attach(text_snapshot,2,11,1,1); - text_snapshot.show(); - - button_snapshot.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "snapshot")); - m_grid1.attach(button_snapshot,3,11,1,1); - button_snapshot.show(); - - - //level 12 - - label_enableTestComputeLoss.set_text("8) Enable \"test_compute_loss\" parameter:\n(Set as 1 when needed to calculate loss in validation phase"); - label_enableTestComputeLoss.set_line_wrap(); - label_enableTestComputeLoss.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_enableTestComputeLoss,0,12,2,1); - label_enableTestComputeLoss.show(); - - Gtk::RadioButton::Group group7 = rbutton_enableTestComputeLoss_no.get_group(); - rbutton_enableTestComputeLoss_yes.set_group(group7); - rbutton_enableTestComputeLoss_no.set_active(); - m_grid1.attach(rbutton_enableTestComputeLoss_no,2,12,1,1); - rbutton_enableTestComputeLoss_no.show(); - m_grid1.attach(rbutton_enableTestComputeLoss_yes,3,12,1,1); - rbutton_enableTestComputeLoss_yes.show(); - - button_testComputeLoss.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "testComputeLoss")); - m_grid1.attach(button_testComputeLoss,4,12,1,1); - button_testComputeLoss.show(); - - //level 13 - - label_snapshotPrefix.set_text("10)snapshot_prefix:\n(Provide prefix string to save the weights.\nProvide path to the saved weights.)"); - label_snapshotPrefix.set_line_wrap(); - label_snapshotPrefix.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_snapshotPrefix,0,13,2,1); - label_snapshotPrefix.show(); - - text_snapshotPrefix.set_max_length(500); - text_snapshotPrefix.set_text("../examples/objectdetector/Mnist_Train/sample_prefix"); - text_snapshotPrefix.select_region(0, text_snapshotPrefix.get_text_length()); - m_grid1.attach(text_snapshotPrefix,2,13,5,1); - text_snapshotPrefix.show(); - - button_snapshotPrefix.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "snapshotPrefix")); - m_grid1.attach(button_snapshotPrefix,7,13,1,1); - button_snapshotPrefix.show(); - - - - //level 14 - - label_maxIter.set_text("11)max_iter:\n(Provide maximum number of iteratios to be performed"); - label_maxIter.set_line_wrap(); - label_maxIter.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_maxIter,0,14,2,1); - label_maxIter.show(); - - text_maxIter.set_max_length(100); - text_maxIter.set_text("10000"); - text_maxIter.select_region(0, text_maxIter.get_text_length()); - m_grid1.attach(text_maxIter,2,14,1,1); - text_maxIter.show(); - - button_maxIter.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "maxIter")); - m_grid1.attach(button_maxIter,3,14,1,1); - button_maxIter.show(); - - - //level 15 - - label_type.set_text("12)type:\n(Select Type of Solver"); - label_type.set_line_wrap(); - label_type.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_type,0,15,2,1); - label_type.show(); - - Gtk::RadioButton::Group group8 = rbutton_typeSGD_yes.get_group(); - rbutton_typeAdadelta_yes.set_group(group8); - rbutton_typeAdagrad_yes.set_group(group8); - rbutton_typeAdam_yes.set_group(group8); - rbutton_typeRMSProp_yes.set_group(group8); - rbutton_typeNesterov_yes.set_group(group8); - rbutton_typeSGD_yes.set_active(); - m_grid1.attach(rbutton_typeSGD_yes,2,15,1,1); - rbutton_typeSGD_yes.show(); - m_grid1.attach(rbutton_typeAdadelta_yes,3,15,1,1); - rbutton_typeAdadelta_yes.show(); - m_grid1.attach(rbutton_typeAdagrad_yes,4,15,1,1); - rbutton_typeAdagrad_yes.show(); - m_grid1.attach(rbutton_typeAdam_yes,5,15,1,1); - rbutton_typeAdam_yes.show(); - m_grid1.attach(rbutton_typeRMSProp_yes,6,15,1,1); - rbutton_typeRMSProp_yes.show(); - m_grid1.attach(rbutton_typeNesterov_yes,7,15,1,1); - rbutton_typeNesterov_yes.show(); - - button_type.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "type")); - m_grid1.attach(button_type,8,15,1,1); - button_type.show(); - - //level 16 - - label_learningRatePolicy.set_text("13)lr_policy:\n(Set learning rate policy"); - label_learningRatePolicy.set_line_wrap(); - label_learningRatePolicy.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_learningRatePolicy,0,16,2,1); - label_learningRatePolicy.show(); - - Gtk::RadioButton::Group group9 = rbutton_learningRatePolicyFixed_yes.get_group(); - rbutton_learningRatePolicyExp_yes.set_group(group9); - rbutton_learningRatePolicyStep_yes.set_group(group9); - rbutton_learningRatePolicyInv_yes.set_group(group9); - rbutton_learningRatePolicyMultistep_yes.set_group(group9); - rbutton_learningRatePolicyPoly_yes.set_group(group9); - rbutton_learningRatePolicySigmoid_yes.set_group(group9); - rbutton_learningRatePolicyFixed_yes.set_active(); - m_grid1.attach(rbutton_learningRatePolicyFixed_yes,2,16,1,1); - rbutton_learningRatePolicyFixed_yes.show(); - m_grid1.attach(rbutton_learningRatePolicyExp_yes,3,16,1,1); - rbutton_learningRatePolicyExp_yes.show(); - m_grid1.attach(rbutton_learningRatePolicyStep_yes,4,16,1,1); - rbutton_learningRatePolicyStep_yes.show(); - m_grid1.attach(rbutton_learningRatePolicyInv_yes,5,16,1,1); - rbutton_learningRatePolicyInv_yes.show(); - m_grid1.attach(rbutton_learningRatePolicyMultistep_yes,6,16,1,1); - rbutton_learningRatePolicyMultistep_yes.show(); - m_grid1.attach(rbutton_learningRatePolicyPoly_yes,7,16,1,1); - rbutton_learningRatePolicyPoly_yes.show(); - m_grid1.attach(rbutton_learningRatePolicySigmoid_yes,8,16,1,1); - rbutton_learningRatePolicySigmoid_yes.show(); - - button_learningRatePolicy.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "learningRatePolicy")); - m_grid1.attach(button_learningRatePolicy,9,16,1,1); - button_learningRatePolicy.show(); - - //level 17 - - label_baseLearningRate.set_text("11)base_lr:\n(Set initial learning rate"); - label_baseLearningRate.set_line_wrap(); - label_baseLearningRate.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_baseLearningRate,0,17,2,1); - label_baseLearningRate.show(); - - text_baseLearningRate.set_max_length(100); - text_baseLearningRate.set_text("0.01"); - text_baseLearningRate.select_region(0, text_baseLearningRate.get_text_length()); - m_grid1.attach(text_baseLearningRate,2,17,1,1); - text_baseLearningRate.show(); - - button_baseLearningRate.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "baseLearningRate")); - m_grid1.attach(button_baseLearningRate,3,17,1,1); - button_baseLearningRate.show(); - - //level 18 - - label_gamma.set_text("14)gamma:\n(Set gamma value. Used in learning rate policies)"); - label_gamma.set_line_wrap(); - label_gamma.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_gamma,0,18,2,1); - label_gamma.show(); - - text_gamma.set_max_length(100); - text_gamma.set_text("0.0001"); - text_gamma.select_region(0, text_gamma.get_text_length()); - m_grid1.attach(text_gamma,2,18,1,1); - text_gamma.show(); - - button_gamma.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "gamma")); - m_grid1.attach(button_gamma,3,18,1,1); - button_gamma.show(); - - - //level 19 - - label_power.set_text("15)power:\n(Set power value. Used in learning rate policies)"); - label_power.set_line_wrap(); - label_power.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_power,0,19,2,1); - label_power.show(); - - text_power.set_max_length(100); - text_power.set_text("0.75"); - text_power.select_region(0, text_power.get_text_length()); - m_grid1.attach(text_power,2,19,1,1); - text_power.show(); - - button_power.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "power")); - m_grid1.attach(button_power,3,19,1,1); - button_power.show(); - - - //level 20 - - label_stepSize.set_text("16)stepsize:\n(Set stepSize. Used in learning rate policies)"); - label_stepSize.set_line_wrap(); - label_stepSize.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_stepSize,0,20,2,1); - label_stepSize.show(); - - text_stepSize.set_max_length(100); - text_stepSize.set_text("100"); - text_stepSize.select_region(0, text_stepSize.get_text_length()); - m_grid1.attach(text_stepSize,2,20,1,1); - text_stepSize.show(); - - button_stepSize.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "stepSize")); - m_grid1.attach(button_stepSize,3,20,1,1); - button_stepSize.show(); - - - //level 21 - - label_stepSizeValue.set_text("17)stepvalue:\n(Set stepvalue. Used in learning rate policu \"multistep\")"); - label_stepSizeValue.set_line_wrap(); - label_stepSizeValue.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_stepSizeValue,0,21,2,1); - label_stepSizeValue.show(); - - text_stepSizeValue.set_max_length(100); - text_stepSizeValue.set_text("100"); - text_stepSizeValue.select_region(0, text_stepSizeValue.get_text_length()); - m_grid1.attach(text_stepSizeValue,2,21,1,1); - text_stepSizeValue.show(); - - button_stepSizeValue.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "stepSizeValue")); - m_grid1.attach(button_stepSizeValue,3,21,1,1); - button_stepSizeValue.show(); - - //level 22 - - label_weightDecay.set_text("18)weight_decay:"); - label_weightDecay.set_line_wrap(); - label_weightDecay.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_weightDecay,0,22,2,1); - label_weightDecay.show(); - - text_weightDecay.set_max_length(100); - text_weightDecay.set_text("0.0005"); - text_weightDecay.select_region(0, text_weightDecay.get_text_length()); - m_grid1.attach(text_weightDecay,2,22,1,1); - text_weightDecay.show(); - - button_weightDecay.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "weightDecay")); - m_grid1.attach(button_weightDecay,3,22,1,1); - button_weightDecay.show(); - - //level 23 - - label_momentum.set_text("19)momentum:"); - label_momentum.set_line_wrap(); - label_momentum.set_justify(Gtk::JUSTIFY_FILL); - m_grid1.attach(label_momentum,0,23,2,1); - label_momentum.show(); - - text_momentum.set_max_length(100); - text_momentum.set_text("0.9"); - text_momentum.select_region(0, text_momentum.get_text_length()); - m_grid1.attach(text_momentum,2,23,1,1); - text_momentum.show(); - - button_momentum.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "momentum")); - m_grid1.attach(button_momentum,3,23,1,1); - button_momentum.show(); - - - - button_saveFile.signal_clicked().connect(sigc::bind<Glib::ustring>( - sigc::mem_fun(*this, &SolverProperties::on_button_clicked), "saveFile")); - m_grid1.attach(button_saveFile,0,24,2,1); - button_saveFile.show(); - - - - m_sw1.add(m_grid1); - m_sw1.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); -// m_grid1.show(); - show_all_children(); - m_sw1.show(); -} - -SolverProperties::~SolverProperties() -{ -} - -void SolverProperties::on_button_clicked(Glib::ustring data) -{ - if(data == "solverFileName") - { - solverFileName = text_solverFileName.get_text(); - std::cout << "Solver File Name set as: " << solverFileName << std::endl; - } - else if(data == "trainNetworkFileName") - { - trainNetworkFileName = text_trainNetworkFileName.get_text(); - std::cout << "Train Network File Name set as: " << trainNetworkFileName << std::endl; - } - else if(data == "testNetworkFileName") - { - testNetworkFileName = text_testNetworkFileName.get_text(); - std::cout << "Test Network File Name set as: " << testNetworkFileName << std::endl; - if(rbutton_enableTestNet_yes.get_active() == 1 and rbutton_trainNetworkFileType_net.get_active() == 1) - { - Gtk::MessageDialog dialog(*this, "\"test_net\" parameter not required"); - dialog.set_secondary_text("\"test_net\" parameter is only required when \"train_net\" parameter is specified"); - dialog.run(); - } - } - else if(data == "testIter") - { - testIter = text_testIter.get_text(); - std::cout << "Validation(test) Phase iterations set as: " << testIter << std::endl; - if((rbutton_enableTestNet_yes.get_active() == 1 and rbutton_enableValidationParameters_no.get_active() == 1)) - { - Gtk::MessageDialog dialog(*this, "Validation parameters required"); - dialog.set_secondary_text("Validation parameters are required when \"test_net\" parameter is specified."); - dialog.run(); - } - else if(rbutton_enableValidationParameters_no.get_active() == 1) - { - Gtk::MessageDialog dialog(*this, "Enable Validation Parameters"); - dialog.set_secondary_text("Validation parameters can be updated only after enabling them"); - dialog.run(); - } - } - else if(data == "testInterval") - { - testInterval = text_testInterval.get_text(); - std::cout << "Validation(test) Phase iterations set as: " << testInterval << std::endl; - if((rbutton_enableTestNet_yes.get_active() == 1 and rbutton_enableValidationParameters_no.get_active() == 1)) - { - Gtk::MessageDialog dialog(*this, "Validation parameters required"); - dialog.set_secondary_text("Validation parameters are required when \"test_net\" parameter is specified."); - dialog.run(); - } - else if(rbutton_enableValidationParameters_no.get_active() == 1) - { - Gtk::MessageDialog dialog(*this, "Enable Validation Parameters"); - dialog.set_secondary_text("Validation parameters can be updated only after enabling them"); - dialog.run(); - } - } - else if(data == "averageLoss") - { - averageLoss = text_averageLoss.get_text(); - std::cout << "Average Loss set as: " << averageLoss << std::endl; - if(rbutton_enableAverageLoss_no.get_active() == 1) - { - Gtk::MessageDialog dialog(*this, "Enable Average Loss Parameter"); - dialog.set_secondary_text("Average Loss Parameter can be updated only after enabling it"); - dialog.run(); - } - } - else if(data == "randomSample") - { - randomSample = text_randomSample.get_text(); - std::cout << "Random Sample Parameters set as: " << randomSample << std::endl; - if(rbutton_enableRandomSample_no.get_active() == 1) - { - Gtk::MessageDialog dialog(*this, "Enable Random Sample Parameter"); - dialog.set_secondary_text("Random Sample Parameter can be updated only after enabling it"); - dialog.run(); - } - } - else if(data == "display") - { - display = text_display.get_text(); - std::cout << "Display after every: " << display << " iterations" << std::endl; - } - else if(data == "debugInfo") - { - if(rbutton_enableDebugInfo_yes.get_active()) - debugInfo = "1"; - else if(rbutton_enableDebugInfo_no.get_active()) - debugInfo = "0"; - std::cout << "Debug Information: " << debugInfo << std::endl; - } - else if(data == "snapshot") - { - snapshot = text_snapshot.get_text(); - std::cout << "Snapshot saved after every: " << snapshot << " iterations" << std::endl; - } - else if(data == "testComputeLoss") - { - if(rbutton_enableTestComputeLoss_yes.get_active()) - testComputeLoss = "1"; - else if(rbutton_enableTestComputeLoss_no.get_active()) - testComputeLoss = "0"; - std::cout << "Compute Loss in Validation Phase: " << testComputeLoss << std::endl; - } - else if(data == "snapshotPrefix") - { - snapshotPrefix = text_snapshotPrefix.get_text(); - std::cout << "Snapshot saved with prefix: " << snapshotPrefix << std::endl; - } - else if(data == "maxIter") - { - maxIter = text_maxIter.get_text(); - std::cout << "Maximum iterations set as: " << maxIter << std::endl; - } - else if(data == "type") - { - if(rbutton_typeSGD_yes.get_active()) - type = "1"; - else if(rbutton_typeAdadelta_yes.get_active()) - type = "AdaDelta"; - else if(rbutton_typeAdagrad_yes.get_active()) - type = "AdaGrad"; - else if(rbutton_typeAdam_yes.get_active()) - type = "Adam"; - else if(rbutton_typeRMSProp_yes.get_active()) - type = "RMSProp"; - else if(rbutton_typeNesterov_yes.get_active()) - type = "Nesterov"; - std::cout << "Solver Type Set as: " << type << std::endl; - } - else if(data == "learningRatePolicy") - { - if(rbutton_learningRatePolicyFixed_yes.get_active()) - learningRatePolicy = "fixed"; - else if(rbutton_learningRatePolicyExp_yes.get_active()) - learningRatePolicy = "exp"; - else if(rbutton_learningRatePolicyStep_yes.get_active()) - learningRatePolicy = "step"; - else if(rbutton_learningRatePolicyInv_yes.get_active()) - learningRatePolicy = "inv"; - else if(rbutton_learningRatePolicyMultistep_yes.get_active()) - learningRatePolicy = "multistep"; - else if(rbutton_learningRatePolicyPoly_yes.get_active()) - learningRatePolicy = "poly"; - else if(rbutton_learningRatePolicySigmoid_yes.get_active()) - learningRatePolicy = "sigmoid"; - - std::cout << "Learning rate policy is set as: " << learningRatePolicy << std::endl; - } - else if(data == "baseLearningRate") - { - baseLearningRate = text_baseLearningRate.get_text(); - std::cout << "Initial learning rate set as: " << baseLearningRate << std::endl; - } - else if(data == "gamma") - { - gamma = text_gamma.get_text(); - std::cout << "Gamma set as: " << gamma << std::endl; - } - else if(data == "power") - { - power = text_power.get_text(); - std::cout << "Power set as: " << power << std::endl; - } - else if(data == "stepSize") - { - stepSize = text_stepSize.get_text(); - std::cout << "stepSize set as: " << stepSize << std::endl; - } - else if(data == "stepSizeValue") - { - stepSizeValue = text_stepSizeValue.get_text(); - std::cout << "stepSizeValue set as: " << stepSizeValue << std::endl; - } - else if(data == "weightDecay") - { - weightDecay = text_weightDecay.get_text(); - std::cout << "weightDecay set as: " << weightDecay << std::endl; - } - else if(data == "momentum") - { - momentum = text_momentum.get_text(); - std::cout << "momentum set as: " << momentum << std::endl; - } - else if(data == "saveFile") - { - - if(rbutton_enableTestNet_yes.get_active() == 1 and rbutton_trainNetworkFileType_net.get_active() == 1) - { - Gtk::MessageDialog dialog(*this, "\"test_net\" parameter not required"); - dialog.set_secondary_text("\"test_net\" parameter is only required when \"train_net\" parameter is specified"); - dialog.run(); - } - else if(rbutton_enableTestNet_yes.get_active() == 1 and rbutton_enableValidationParameters_no.get_active() == 1) - { - Gtk::MessageDialog dialog(*this, "Validation parameters required"); - dialog.set_secondary_text("Validation parameters are required when \"test_net\" parameter is specified."); - dialog.run(); - } - else - { - solverFileName = text_solverFileName.get_text(); - trainNetworkFileName = text_trainNetworkFileName.get_text(); - testNetworkFileName = text_testNetworkFileName.get_text(); - testIter = text_testIter.get_text(); - testInterval = text_testInterval.get_text(); - averageLoss = text_averageLoss.get_text(); - randomSample = text_randomSample.get_text(); - display = text_display.get_text(); - snapshot = text_snapshot.get_text(); - snapshotPrefix = text_snapshotPrefix.get_text(); - maxIter = text_maxIter.get_text(); - baseLearningRate = text_baseLearningRate.get_text(); - gamma = text_gamma.get_text(); - power = text_power.get_text(); - stepSize = text_stepSize.get_text(); - stepSizeValue = text_stepSizeValue.get_text(); - weightDecay = text_weightDecay.get_text(); - momentum = text_momentum.get_text(); - - if(rbutton_enableDebugInfo_yes.get_active()) - debugInfo = "1"; - else if(rbutton_enableDebugInfo_no.get_active()) - debugInfo = "0"; - - if(rbutton_enableTestComputeLoss_yes.get_active()) - testComputeLoss = "1"; - else if(rbutton_enableTestComputeLoss_no.get_active()) - testComputeLoss = "0"; - - if(rbutton_typeSGD_yes.get_active()) - type = "1"; - else if(rbutton_typeAdadelta_yes.get_active()) - type = "AdaDelta"; - else if(rbutton_typeAdagrad_yes.get_active()) - type = "AdaGrad"; - else if(rbutton_typeAdam_yes.get_active()) - type = "Adam"; - else if(rbutton_typeRMSProp_yes.get_active()) - type = "RMSProp"; - else if(rbutton_typeNesterov_yes.get_active()) - type = "Nesterov"; - - if(rbutton_learningRatePolicyFixed_yes.get_active()) - learningRatePolicy = "fixed"; - else if(rbutton_learningRatePolicyExp_yes.get_active()) - learningRatePolicy = "exp"; - else if(rbutton_learningRatePolicyStep_yes.get_active()) - learningRatePolicy = "step"; - else if(rbutton_learningRatePolicyInv_yes.get_active()) - learningRatePolicy = "inv"; - else if(rbutton_learningRatePolicyMultistep_yes.get_active()) - learningRatePolicy = "multistep"; - else if(rbutton_learningRatePolicyPoly_yes.get_active()) - learningRatePolicy = "poly"; - else if(rbutton_learningRatePolicySigmoid_yes.get_active()) - learningRatePolicy = "sigmoid"; - - std::ofstream myfile; - myfile.open(solverFileName); - myfile << "#File generated using OpenDetection" << std::endl; - myfile.close(); - myfile.open(solverFileName); - if(!myfile) - { - Gtk::MessageDialog dialog(*this, "File Could not be Created"); - dialog.set_secondary_text("Make sure the destination exists or the file is writable"); - dialog.run(); - } - std::cout << "Solver File Name saved as: " << solverFileName << std::endl; - - - if(rbutton_trainNetworkFileType_net.get_active() == 1) - myfile << "net: " << "\"" << trainNetworkFileName << "\"" << std::endl; - else if(rbutton_trainNetworkFileType_tt.get_active() == 1) - myfile << "train_net: " << "\"" << trainNetworkFileName << "\"" << std::endl; - - if(rbutton_enableTestNet_yes.get_active() == 1) - myfile << "test_net: " << "\"" << testNetworkFileName << "\"" << std::endl; - else if(rbutton_enableTestNet_no.get_active() == 1) - myfile << "#test_net: " << "\"" << testNetworkFileName << "\"" << std::endl; - - if(rbutton_enableValidationParameters_yes.get_active() == 1) - { - myfile << "test_iter: " << testIter << std::endl; - myfile << "test_interval: " << testInterval << std::endl; - } - else if(rbutton_enableValidationParameters_no.get_active() == 1) - { - myfile << "#test_iter: " << testIter << std::endl; - myfile << "#test_interval: " << testInterval << std::endl; - } - - if(rbutton_enableAverageLoss_yes.get_active() == 1) - myfile << "average_loss: " << averageLoss << std::endl; - else if(rbutton_enableAverageLoss_no.get_active() == 1) - myfile << "#average_loss: " << averageLoss << std::endl; - - if(rbutton_enableRandomSample_yes.get_active() == 1) - myfile << "random_seed: " << randomSample << std::endl; - else if(rbutton_enableRandomSample_no.get_active() == 1) - myfile << "#random_seed: " << randomSample << std::endl; - - - myfile << "display: " << display << std::endl; - myfile << "debug_info: " << debugInfo << std::endl; - myfile << "snapshot: " << snapshot << std::endl; - myfile << "test_compute_loss: " << testComputeLoss << std::endl; - myfile << "snapshot_prefix: " << "\"" << snapshotPrefix << "\"" << std::endl; - myfile << "max_iter: " << maxIter << std::endl; - myfile << "type: " << "\"" << type << "\"" << std::endl; - - myfile << "lr_policy: " << "\"" << learningRatePolicy << "\"" << std::endl; - myfile << "base_lr: " << baseLearningRate << std::endl; - myfile << "gamma: " << gamma << std::endl; - myfile << "power: " << power << std::endl; - myfile << "stepsize: " << stepSize << std::endl; - myfile << "stepvalue: " << stepSizeValue << std::endl; - - myfile << "weight_decay: " << weightDecay << std::endl; - myfile << "momentum: " << momentum << std::endl; - - - myfile.close(); - } - } - -} diff --git a/examples/objectdetector/CMakeLists.txt b/examples/objectdetector/CMakeLists.txt index fc5e0f9e..94bd63f1 100644 --- a/examples/objectdetector/CMakeLists.txt +++ b/examples/objectdetector/CMakeLists.txt @@ -320,6 +320,8 @@ if(framegenerator_example) LINK_WITH od_common) endif() +if(0) + ################################################################################## option(cnn_classification "Build the cnn classification example" ON) if(Caffe_FOUND) @@ -381,5 +383,5 @@ else() message("!!! cnn_training is set to on but CAFFE was not found") endif(Caffe_FOUND) - +endif(0) diff --git a/examples/objectdetector/cnn_mnist_classification.cpp b/examples/objectdetector/cnn_mnist_classification.cpp deleted file mode 100644 index 09638d74..00000000 --- a/examples/objectdetector/cnn_mnist_classification.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include "od/detectors/global2D/detection/ConvClassification.h" -#include <fstream> - -void help() -{ - std::cout << "Usage: ./examples/objectdetector/od_cnn_mnist_classification <path to weight caffemodel file> <path to network file> <path to image file>" << std::endl; - std::cout << "Example: ./examples/objectdetector/od_cnn_mnist_classification ../../../data/Mnist_Classify/mnist.caffemodel ../../../data/Mnist_Classify/lenet.prototxt ../../../data/Mnist_Classify/3.png" << std::endl << std::endl; - exit(-1); -} - -int main(int argc, char **argv) -{ - if (argc != 4) - { - help(); - } - - od::g2d::ConvClassification mnist_classifier(""); - mnist_classifier.setWeightModelFileLocation(argv[1]); - mnist_classifier.setNetworkModelFileLocation(argv[2]); - mnist_classifier.setImageFileLocation(argv[3]); - - for(unsigned int i = 1; i < argc; i++) - { - std::string check(argv[i]); - if(i == 1) - { - size_t found = check.find(".caffemodel"); - if(found > check.length()) - { - std::cout << "First argument should be caffe weight caffemodel file" << std::endl; - help(); - } - } - else if(i == 2) - { - size_t found = check.find(".prototxt"); - if(found > check.length()) - { - std::cout << "Second argument should be caffe network prototxt file" << std::endl; - help(); - } - - } - std::ifstream ifs; - ifs.open(check.c_str()); - if(!ifs) - { - std::cout << "File not found: " << check << std::endl; - help(); - } - } - std::string weight_file_location = mnist_classifier.getWeightModelFileLocation(); - std::string network_file_location = mnist_classifier.getNetworkModelFileLocation(); - std::string image_file_location = mnist_classifier.getImageFileLocation(); - - mnist_classifier.setTestBlob(1, 28, 28); - mnist_classifier.classify(); - - return 0; -} - - diff --git a/examples/objectdetector/cnn_mnist_train_customSolver.cpp b/examples/objectdetector/cnn_mnist_train_customSolver.cpp deleted file mode 100644 index d51d3e9a..00000000 --- a/examples/objectdetector/cnn_mnist_train_customSolver.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "od/detectors/global2D/training/ConvTrainer.h" -#include "od/detectors/global2D/detection/ConvClassification.h" - -int main(int argc, char *argv[]) -{ - argc = 1; - od::g2d::ConvTrainer mnist_trainer("",""); - mnist_trainer.setSolverProperties(argc, argv); - mnist_trainer.startTraining(); - return 0; -} diff --git a/examples/objectdetector/cnn_mnist_train_simple.cpp b/examples/objectdetector/cnn_mnist_train_simple.cpp deleted file mode 100644 index cc0faf53..00000000 --- a/examples/objectdetector/cnn_mnist_train_simple.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "od/detectors/global2D/training/ConvTrainer.h" -#include "od/detectors/global2D/detection/ConvClassification.h" - -int main(int argc, char **argv) -{ - od::g2d::ConvTrainer mnist_trainer("", ""); - mnist_trainer.setSolverLocation(argv[1]); - mnist_trainer.startTraining(); - return 0; -} - From 3174e1f2f3630da76dd5df0da8a32a1a761a80d7 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Fri, 2 Sep 2016 11:18:13 +0200 Subject: [PATCH 68/79] adds correctly svmlight --- .gitmodules | 3 +++ 3rdparty/--name | 1 + 3rdparty/CMakeLists.txt | 2 +- 3rdparty/svmlight | 1 + svmlightlib | 1 + 5 files changed, 7 insertions(+), 1 deletion(-) create mode 160000 3rdparty/--name create mode 160000 3rdparty/svmlight create mode 160000 svmlightlib diff --git a/.gitmodules b/.gitmodules index 18fa539a..de9d9399 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "3rdparty/SiftGPU"] path = 3rdparty/SiftGPU url = https://github.com/pitzer/SiftGPU.git +[submodule "3rdparty/svmlight"] + path = 3rdparty/svmlight + url = https://github.com/giacomodabisias/svmlightlib.git diff --git a/3rdparty/--name b/3rdparty/--name new file mode 160000 index 00000000..64ecc2c9 --- /dev/null +++ b/3rdparty/--name @@ -0,0 +1 @@ +Subproject commit 64ecc2c9914b667e480d77d5c41b4c796942571d diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index e7f0b2a3..8e740eb2 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -2,7 +2,7 @@ add_subdirectory(pugixml_build) if(WITH_SVMLIGHT) - add_subdirectory(svmlightlib) + add_subdirectory(svmlight) endif() if(WITH_GPU) diff --git a/3rdparty/svmlight b/3rdparty/svmlight new file mode 160000 index 00000000..64ecc2c9 --- /dev/null +++ b/3rdparty/svmlight @@ -0,0 +1 @@ +Subproject commit 64ecc2c9914b667e480d77d5c41b4c796942571d diff --git a/svmlightlib b/svmlightlib new file mode 160000 index 00000000..64ecc2c9 --- /dev/null +++ b/svmlightlib @@ -0,0 +1 @@ +Subproject commit 64ecc2c9914b667e480d77d5c41b4c796942571d From 60e016310506cd50847af395c4eabd5394867396 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Fri, 2 Sep 2016 11:18:34 +0200 Subject: [PATCH 69/79] removes wrong dependency --- 3rdparty/--name | 1 - 1 file changed, 1 deletion(-) delete mode 160000 3rdparty/--name diff --git a/3rdparty/--name b/3rdparty/--name deleted file mode 160000 index 64ecc2c9..00000000 --- a/3rdparty/--name +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 64ecc2c9914b667e480d77d5c41b4c796942571d From a5e9661b95ab63664fe1dd674f3a7f27b7f43c70 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Fri, 2 Sep 2016 11:25:48 +0200 Subject: [PATCH 70/79] fixes modules --- svmlightlib | 1 - 1 file changed, 1 deletion(-) delete mode 160000 svmlightlib diff --git a/svmlightlib b/svmlightlib deleted file mode 160000 index 64ecc2c9..00000000 --- a/svmlightlib +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 64ecc2c9914b667e480d77d5c41b4c796942571d From b70549ef0d0cb1bee85dc78a1e936419860ef8eb Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Fri, 2 Sep 2016 13:46:02 +0200 Subject: [PATCH 71/79] fixes the CLASSIFICATION enum type --- common/include/od/common/bindings/Svmlight.h | 4 ---- common/include/od/common/pipeline/Detection.h | 19 ++++++++++++------- common/src/pipeline/Detection.cpp | 10 +++++----- .../detection/CADDetector3DGlobal.hpp | 4 ++-- .../global2D/detection/HOGDetector.h | 3 +-- detectors/src/global2D/FaceRecognizer.cpp | 2 +- .../detection/CascadeDetectorImpl.cpp | 4 ++-- .../src/global2D/detection/HOGDetector.cpp | 4 ++-- .../detection/CADRecognizer2DLocal.cpp | 2 +- .../detection/CascadeDetectorImpl.cpp | 4 ++-- 10 files changed, 28 insertions(+), 28 deletions(-) diff --git a/common/include/od/common/bindings/Svmlight.h b/common/include/od/common/bindings/Svmlight.h index ea929568..0ce8bf32 100644 --- a/common/include/od/common/bindings/Svmlight.h +++ b/common/include/od/common/bindings/Svmlight.h @@ -61,10 +61,6 @@ namespace svmlight { } #endif -enum type { - CLASSIFICATION, REGRESSION, RANKING, OPTIMIZATION -}; - class SVMlight { private: diff --git a/common/include/od/common/pipeline/Detection.h b/common/include/od/common/pipeline/Detection.h index 9b4ef7a7..c01a56e9 100644 --- a/common/include/od/common/pipeline/Detection.h +++ b/common/include/od/common/pipeline/Detection.h @@ -47,11 +47,16 @@ namespace od * \author Kripasindhu Sarkar * */ + namespace detection { + _DEFINE_ENUM_WITH_STRING_CONVERSIONS(DetectionType, (RECOGNITION)(CLASSIFICATION)(DETECTION)(DETECTION_NULL)) + } + + class Detection { public: - _DEFINE_ENUM_WITH_STRING_CONVERSIONS(DetectionType, (RECOGNITION)(CLASSIFICATION)(DETECTION)(DETECTION_NULL)) + virtual ~Detection(){} @@ -60,7 +65,7 @@ namespace od \param id The detection identifier. \param confidence The detection confidence. */ - Detection(const DetectionType & type = DETECTION_NULL, const std::string & id = std::string(""), double confidence = 1.0); + Detection(const detection::DetectionType & type = detection::DETECTION_NULL, const std::string & id = std::string(""), double confidence = 1.0); /** \brief Prints type and id of the detection. */ @@ -69,12 +74,12 @@ namespace od /** \brief Get the type of the detection. This can be _RECOGNITION, _CLASSIFICATION, _DETECTION, _DETECTION_NULL. \return The type of the detection. */ - const DetectionType & getType() const; + const detection::DetectionType & getType() const; /** \brief Set the type of the detection. This can be _RECOGNITION, _CLASSIFICATION, _DETECTION, _DETECTION_NULL. \param type The type of the detection. */ - void setType(const DetectionType & type); + void setType(const detection::DetectionType & type); /** \brief Get the id of the detection. \return The id of the detection. @@ -98,7 +103,7 @@ namespace od private: - DetectionType type_; + detection::DetectionType type_; std::string id_; double confidence_; @@ -120,7 +125,7 @@ namespace od \param id The detection identifier. \param confidence The detection confidence. */ - Detection2D(const DetectionType & type = DETECTION_NULL, const std::string & id = std::string(""), double confidence = 1.0); + Detection2D(const detection::DetectionType & type = detection::DETECTION_NULL, const std::string & id = std::string(""), double confidence = 1.0); /** \brief Get the 2D location of the detection. \return The 2D vector of the detection position. @@ -174,7 +179,7 @@ namespace od \param id The detection identifier. \param confidence The detection confidence. */ - Detection3D(const DetectionType & type = DETECTION_NULL, const std::string & id = std::string(""), double confidence = 1.0); + Detection3D(const detection::DetectionType & type = detection::DETECTION_NULL, const std::string & id = std::string(""), double confidence = 1.0); /** \brief Get the 3D location of the detection. diff --git a/common/src/pipeline/Detection.cpp b/common/src/pipeline/Detection.cpp index 1118d976..6f06108d 100644 --- a/common/src/pipeline/Detection.cpp +++ b/common/src/pipeline/Detection.cpp @@ -3,7 +3,7 @@ namespace od { - Detection::Detection(const DetectionType & type, const std::string & id, double confidence) : + Detection::Detection(const detection::DetectionType & type, const std::string & id, double confidence) : type_(type), id_(id), confidence_(confidence) {} void Detection::printSelf() @@ -12,12 +12,12 @@ namespace od std::cout << "ID: " << id_ << std::endl; } - const Detection::DetectionType & Detection::getType() const + const detection::DetectionType & Detection::getType() const { return type_; } - void Detection::setType(const DetectionType & type) + void Detection::setType(const detection::DetectionType & type) { type_ = type; } @@ -46,7 +46,7 @@ namespace od - Detection2D::Detection2D(const DetectionType & type, const std::string & id , double confidence) : Detection(type, id, confidence) + Detection2D::Detection2D(const detection::DetectionType & type, const std::string & id , double confidence) : Detection(type, id, confidence) { location_2d_ = Eigen::Vector3d::UnitZ(); } @@ -83,7 +83,7 @@ namespace od - Detection3D::Detection3D(const DetectionType & type, const std::string & id, double confidence) : Detection(type, id, confidence) + Detection3D::Detection3D(const detection::DetectionType & type, const std::string & id, double confidence) : Detection(type, id, confidence) { location_3d_ = Eigen::Vector4d::UnitW(); orientation_.setIdentity(); diff --git a/detectors/impl/od/detectors/global3D/detection/CADDetector3DGlobal.hpp b/detectors/impl/od/detectors/global3D/detection/CADDetector3DGlobal.hpp index 4582357c..75000aa4 100644 --- a/detectors/impl/od/detectors/global3D/detection/CADDetector3DGlobal.hpp +++ b/detectors/impl/od/detectors/global3D/detection/CADDetector3DGlobal.hpp @@ -210,7 +210,7 @@ namespace od //now fill up the detection: shared_ptr<Detection3D> detection = make_shared<Detection3D>(); - detection->setType(Detection::CLASSIFICATION); + detection->setType(detection::CLASSIFICATION); detection->setId(categories[0]); detection->setLocation(centroid); detection->setMetainfoCluster(clusters[i]); @@ -240,7 +240,7 @@ namespace od //detection done! //now fill up the detection: - shared_ptr<Detection> detection(new Detection(Detection::CLASSIFICATION, categories[0], conf[0])); + shared_ptr<Detection> detection(new Detection(detection::CLASSIFICATION, categories[0], conf[0])); detections->push_back(detection); diff --git a/detectors/include/od/detectors/global2D/detection/HOGDetector.h b/detectors/include/od/detectors/global2D/detection/HOGDetector.h index 230a83e1..1219cce0 100644 --- a/detectors/include/od/detectors/global2D/detection/HOGDetector.h +++ b/detectors/include/od/detectors/global2D/detection/HOGDetector.h @@ -32,7 +32,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "od/common/utils/Utils.h" #include "od/common/utils/FeatureDetector2D.h" #include "od/common/utils/Shared_pointers.h" -#include "od/common/bindings/Svmlight.h" #include <iostream> #include <opencv2/objdetect.hpp> @@ -103,7 +102,7 @@ namespace od shared_ptr<Detections> detectOmni(shared_ptr<Scene> scene); protected: - //properteis + //properties cv::Size win_size_; cv::Size block_size_; cv::Size block_stride_; diff --git a/detectors/src/global2D/FaceRecognizer.cpp b/detectors/src/global2D/FaceRecognizer.cpp index ab5e7871..7d9fcbc4 100644 --- a/detectors/src/global2D/FaceRecognizer.cpp +++ b/detectors/src/global2D/FaceRecognizer.cpp @@ -148,7 +148,7 @@ namespace od cv_recognizer_->predict(face_edited, label, confidence); //fill in the detection - shared_ptr<Detection2D> detection(new Detection2D(Detection::CLASSIFICATION, std::to_string(label), confidence)); + shared_ptr<Detection2D> detection(new Detection2D(detection::CLASSIFICATION, std::to_string(label), confidence)); shared_ptr<Detections2D> detections = make_shared<Detections2D>(); detections->push_back(detection); diff --git a/detectors/src/global2D/detection/CascadeDetectorImpl.cpp b/detectors/src/global2D/detection/CascadeDetectorImpl.cpp index 2961d636..3bff63f2 100644 --- a/detectors/src/global2D/detection/CascadeDetectorImpl.cpp +++ b/detectors/src/global2D/detection/CascadeDetectorImpl.cpp @@ -80,7 +80,7 @@ namespace od for(auto & o : objects) { // Process object by object: - shared_ptr<Detection2D> detection2D = make_shared<Detection2D>(Detection::CLASSIFICATION, "OBJ", 1); + shared_ptr<Detection2D> detection2D = make_shared<Detection2D>(detection::CLASSIFICATION, "OBJ", 1); detection2D->setBoundingBox(o); detections->push_back(detection2D); @@ -116,7 +116,7 @@ namespace od haar_cascade_->detectMultiScale(gray, objects, 5, min_neighbors_, 0, gray.size(), gray.size()); if(objects.size() > 0) { - detections->push_back(make_shared<Detection>(Detection::CLASSIFICATION, "OBJ", 1)); + detections->push_back(make_shared<Detection>(detection::CLASSIFICATION, "OBJ", 1)); } return detections; } diff --git a/detectors/src/global2D/detection/HOGDetector.cpp b/detectors/src/global2D/detection/HOGDetector.cpp index 860eb00e..488fffe5 100644 --- a/detectors/src/global2D/detection/HOGDetector.cpp +++ b/detectors/src/global2D/detection/HOGDetector.cpp @@ -104,7 +104,7 @@ namespace od shared_ptr<Detection2D> detection2D = make_shared<Detection2D>(); detection2D->setBoundingBox(found[i]); detection2D->setId("PEOPLE"); - detection2D->setType(od::Detection::CLASSIFICATION); + detection2D->setType(od::detection::CLASSIFICATION); detections->push_back(detection2D); if(meta_info_) @@ -137,7 +137,7 @@ namespace od { shared_ptr<Detection2D> detection2D = make_shared<Detection2D>(); detection2D->setId("PEOPLE"); - detection2D->setType(od::Detection::CLASSIFICATION); + detection2D->setType(od::detection::CLASSIFICATION); detections->push_back(detection2D); } diff --git a/detectors/src/local2D/detection/CADRecognizer2DLocal.cpp b/detectors/src/local2D/detection/CADRecognizer2DLocal.cpp index bbe75f0b..523ea159 100644 --- a/detectors/src/local2D/detection/CADRecognizer2DLocal.cpp +++ b/detectors/src/local2D/detection/CADRecognizer2DLocal.cpp @@ -340,7 +340,7 @@ namespace od detection3D = make_shared<Detection3D>(); detection3D->setLocation(pnp_detection_.getTMatrix()); detection3D->setPose(pnp_detection_.getRMatrix()); - detection3D->setType(Detection::RECOGNITION); + detection3D->setType(detection::RECOGNITION); detection3D->setId(model.id_); return true; diff --git a/gpu/detectors/src/global2D/detection/CascadeDetectorImpl.cpp b/gpu/detectors/src/global2D/detection/CascadeDetectorImpl.cpp index 74812ec9..3fc20051 100644 --- a/gpu/detectors/src/global2D/detection/CascadeDetectorImpl.cpp +++ b/gpu/detectors/src/global2D/detection/CascadeDetectorImpl.cpp @@ -85,7 +85,7 @@ namespace od for(auto & o : objects_) { // Process face by face: - shared_ptr<Detection2D> detection2D = make_shared<Detection2D>(Detection::CLASSIFICATION, "OBJ", 1); + shared_ptr<Detection2D> detection2D = make_shared<Detection2D>(detection::CLASSIFICATION, "OBJ", 1); detection2D->setBoundingBox(o); detections->push_back(detection2D); @@ -132,7 +132,7 @@ namespace od //todo: implement in some other way of fast single detection; currently this will work, but maynot be fast if(objects_.size() > 0) { - detections->push_back(make_shared<Detection>(Detection::CLASSIFICATION, "OBJ", 1)); + detections->push_back(make_shared<Detection>(detection::CLASSIFICATION, "OBJ", 1)); } return detections; } From f920cdb13b456adffc752f3d99049c5ed4423264 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Fri, 2 Sep 2016 14:48:15 +0200 Subject: [PATCH 72/79] fixes svmlight include --- common/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 91110dcf..c59be1e9 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -27,7 +27,7 @@ set(SOURCES if(WITH_SVMLIGHT) set(SOURCES ${SOURCES} "src/bindings/Svmlight.cpp") - include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) + include_directories(${CMAKE_3RDPARTY_DIR}/svmlight/) set(SUBSYS_DEPS ${SUBSYS_DEPS} svmlight) endif() From 4d69a98a4981d1a7b751ff882324e6294d82d66a Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Fri, 2 Sep 2016 15:30:56 +0200 Subject: [PATCH 73/79] fixes svmlight namesspace --- common/src/bindings/Svmlight.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/bindings/Svmlight.cpp b/common/src/bindings/Svmlight.cpp index f541020a..ccb01c29 100644 --- a/common/src/bindings/Svmlight.cpp +++ b/common/src/bindings/Svmlight.cpp @@ -80,7 +80,7 @@ SVMlight::SVMlight(){ learn_parm_.xa_depth = 0; // The HOG paper uses a soft classifier (C = 0.01), set to 0.0 to get the default calculation learn_parm_.svm_c = 0.01; // -c 0.01 - learn_parm_.type = REGRESSION; + learn_parm_.type = svmlight::REGRESSION; learn_parm_.remove_inconsistent = 0; // -i 0 - Important kernel_parm_.rbf_gamma = 1.0; kernel_parm_.coef_lin = 1; From 0add78ced02bb63339c7b4f220badd3bb0fb5316 Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Fri, 2 Sep 2016 15:46:05 +0200 Subject: [PATCH 74/79] fixex svmlight cmake --- detectors/src/global2D/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/detectors/src/global2D/CMakeLists.txt b/detectors/src/global2D/CMakeLists.txt index 1f054e50..cd020622 100644 --- a/detectors/src/global2D/CMakeLists.txt +++ b/detectors/src/global2D/CMakeLists.txt @@ -50,7 +50,7 @@ endif(0) if(WITH_SVMLIGHT) set(SOURCES ${SOURCES} "training/HOGTrainer.cpp" "detection/HOGDetector.cpp") - include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) + include_directories(${CMAKE_3RDPARTY_DIR}/svmlight/) set(SUBSYS_DEPS ${SUBSYS_DEPS} svmlight) endif() From 6b6ca422b697a5bde74935774bbb8bd8ca7434fb Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Tue, 6 Sep 2016 10:31:47 +0200 Subject: [PATCH 75/79] fixes svmlight include --- examples/objectdetector/CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/objectdetector/CMakeLists.txt b/examples/objectdetector/CMakeLists.txt index 94bd63f1..9866efdb 100644 --- a/examples/objectdetector/CMakeLists.txt +++ b/examples/objectdetector/CMakeLists.txt @@ -65,7 +65,7 @@ if(image_hog_files_example) include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) include_directories(${COMMON_IMPL_DIR}) - include_directories(${CMAKE_3RDPARTY_DIR}/svmlightlib/) + include_directories(${CMAKE_3RDPARTY_DIR}/svmlight/) OD_ADD_EXAMPLE(od_image_hog_files FILES image_hog_files.cpp @@ -135,6 +135,7 @@ if(image_customhog_example) include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) include_directories(${COMMON_IMPL_DIR}) + include_directories(${CMAKE_3RDPARTY_DIR}/svmlight/) OD_ADD_EXAMPLE(od_image_customhog FILES image_customhog.cpp @@ -158,6 +159,7 @@ if(multialgo_files_example) include_directories(${COMMON_INCLUDE_DIR}) include_directories(${COMMON_IMPL_DIR}) include_directories(${OpenCV_INCLUDE_DIRS}) + include_directories(${CMAKE_3RDPARTY_DIR}/svmlight/) link_directories(${PCL_LIBRARY_DIRS}) @@ -181,6 +183,7 @@ if(multialgo_pc_example) include_directories(${DETECTORS_INCLUDE_DIR}) include_directories(${COMMON_INCLUDE_DIR}) include_directories(${COMMON_IMPL_DIR}) + include_directories(${CMAKE_3RDPARTY_DIR}/svmlight/) link_directories(${PCL_LIBRARY_DIRS}) From 24a92d544aab546d669eb49d49f17c741e42d98a Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Tue, 6 Sep 2016 15:15:53 +0200 Subject: [PATCH 76/79] fixes example file name --- examples/objectdetector/CMakeLists.txt | 6 +++--- examples/objectdetector/image_cadrecog_camera.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/objectdetector/CMakeLists.txt b/examples/objectdetector/CMakeLists.txt index 9866efdb..8ca2362c 100644 --- a/examples/objectdetector/CMakeLists.txt +++ b/examples/objectdetector/CMakeLists.txt @@ -2,7 +2,7 @@ include_directories(${CMAKE_3RDPARTY_DIR}/SiftGPU/src/SiftGPU/) option(camera_recognition_example "Build the camera recognition example" ON) -if(od_image_camera_example) +if(camera_recognition_example) if(TARGET od_local_image_detector) include_directories(${DETECTORS_INCLUDE_DIR}) @@ -13,9 +13,9 @@ if(od_image_camera_example) FILES image_cadrecog_camera.cpp LINK_WITH od_local_image_detector) else() - message("!!! od_image_camera_example is set to on but BUILD_LOCAL_2D_DETECTION is necessary to build od_image_camera_example") + message("!!! camera_recognition_example is set to on but BUILD_LOCAL_2D_DETECTION is necessary to build camera_recognition_example") endif(TARGET od_local_image_detector) -endif(od_image_camera_example) +endif(camera_recognition_example) ################################################################################## option(image_recognition_example "Build the image recognition example" ON) diff --git a/examples/objectdetector/image_cadrecog_camera.cpp b/examples/objectdetector/image_cadrecog_camera.cpp index f5f0c0aa..2e0df8b0 100644 --- a/examples/objectdetector/image_cadrecog_camera.cpp +++ b/examples/objectdetector/image_cadrecog_camera.cpp @@ -70,7 +70,7 @@ int main(int argc, char *argv[]) boost::shared_ptr<od::SceneImage> scene = frameGenerator.getNextFrame(); //Detect - boost::shared_ptr<Detections3D> detections = detector.detectOmni(scene); + boost::shared_ptr<od::Detections3D> detections = detector.detectOmni(scene); if(detections->size() > 0) viewer.update(detections->getMetainfoImage(), "Overlay"); From 7916c54db3a4f3f3baba676bc0470c608be6c21f Mon Sep 17 00:00:00 2001 From: Giacomo Dabisias <g.dabisias@sssup.it> Date: Tue, 6 Sep 2016 16:04:33 +0200 Subject: [PATCH 77/79] changes installation instructions --- .../installation_instruction.md | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/doc/doxygen/tutorials_doxygen/installation_instruction.md b/doc/doxygen/tutorials_doxygen/installation_instruction.md index bd8a6392..37add70c 100644 --- a/doc/doxygen/tutorials_doxygen/installation_instruction.md +++ b/doc/doxygen/tutorials_doxygen/installation_instruction.md @@ -13,7 +13,7 @@ Dependencies {#installation_instruction1} ##OpenCV 3.0## {#installation_instruction2} -OpenCV 3.0 is to be compiled with the modules xfeatures2d (for features like SIFT) and CUDA. +OpenCV 3.0 is to be compiled with the modules xfeatures2d (for features like SIFT) and CUDA for GPU features if needed. - **Source:** http://opencv.org/downloads.html or https://github.com/Itseez/opencv @@ -30,13 +30,7 @@ OpenCV 3.0 is to be compiled with the modules xfeatures2d (for features like SIF *CMAKE options*: WITH_CUDA=ON - -##VTK 6.0 or above {#installation_instruction3} - -Download and compile latest VTK with its default settings. - - -##PCL 1.8 or above {#installation_instruction4} +##PCL 1.8 or above {#installation_instruction3} - **Source:** https://github.com/PointCloudLibrary/pcl or https://github.com/PointCloudLibrary/pcl/releases @@ -45,39 +39,48 @@ Download and compile latest VTK with its default settings. * *3d_rec_framework* - for ESF, ESF etc recognition pipeline. To build with this setting you need to install OpenNI as well, which is the mandatory dependency for this app. Please refer to the PCL website for the version information. *Additional CMAKE options:* BUILD_apps=ON, BUILD_apps_3d_rec_framework=ON - -##Eigen {#installation_instruction5} -Get the latest version of Eigen (source) using your package manager. + + +##SiftGPU {#installation_instruction4} +SiftGPU comes as a submodule in the 3rdparty folder. It can be enabled or disabled using the WITH_GPU option. -##SVMLight{#installation_instruction51} -We use SVMLight for training. To user the feature of training using SVMLight, you need to download the source and put in a specific folder (we could not include the source in 3rdparty because of its restrictive licence). Please see http://svmlight.joachims.org/ for the details and terms of use. +##PugiXML {#installation_instruction5} +PugiXML comes as a folder in the 3rdparty folder and is mandatory. -Download the source code from http://svmlight.joachims.org/ and extract it to `opendetection/3rdparty/svmlight`. You should have files like `svm_common.c`, `svm_learn.c`, etc. under the directory `opendetection/3rdparty/svmlight`. +##SVMLight{#installation_instruction6} +SVMlight comes as a submodule in the 3rdparty folder. It can be enabled or disabled using the WITH_SVMLIGHT option. -Installing Open Detection {#installation_instruction6} +Installing Open Detection {#installation_instruction7} ==== With the above dependencies installed, OD should compile without any problem. Download the source from https://github.com/krips89/opendetection and compile it with default cmake options. The code while platform independent, is only tested and run in Linux machine. Instructions for the usage for linux are provided below: -##Instructions: {#installation_instruction7} +##Instructions: {#installation_instruction8} Compile out of source using cmake+your favorite compiler. For example: Download the code: @code{.bash} cd <path_to_desired_download_location> -git clone https://github.com/krips89/opendetection.git +git clone --recursive https://github.com/krips89/opendetection.git @endcode -configure with CMake and compile +configure with CMake and compile +There are several options in the cmake file which can be set to build examples, change installation path and add gpu support. @code{.bash} cd <path_to_source> -mkdir build; cd build +mkdir build; +cd build cmake .. -make -j8 +make -j make install @endcode +There are also other targets: +* uninstall to remove the library +* package to build a .deb packge + + \ No newline at end of file From e07b78d34e8554d14e006920fba37603aba04fe3 Mon Sep 17 00:00:00 2001 From: giacomo <giacomo.dabisias@gmail.com> Date: Wed, 7 Sep 2016 00:40:23 +0200 Subject: [PATCH 78/79] adds revised documentation and fixes framgenerator.cpp --- .../tutorials_doxygen/basic_structures.md | 20 ++- doc/doxygen/tutorials_doxygen/detection_2d.md | 139 +++++++++--------- doc/doxygen/tutorials_doxygen/detection_3d.md | 117 ++++++++------- .../gsoc2016_blog_giacomo.md | 2 +- .../tutorials_doxygen/introduction_general.md | 25 ++-- examples/objectdetector/framegenerator.cpp | 1 - 6 files changed, 147 insertions(+), 157 deletions(-) diff --git a/doc/doxygen/tutorials_doxygen/basic_structures.md b/doc/doxygen/tutorials_doxygen/basic_structures.md index abe0a492..75048d97 100644 --- a/doc/doxygen/tutorials_doxygen/basic_structures.md +++ b/doc/doxygen/tutorials_doxygen/basic_structures.md @@ -8,28 +8,28 @@ Basic Structure {#basic_structures1} This article covers the basic structures and pipelines of OD. -The basic classes in OD are Trainers and Detectors. A Trainer (the offline stage) of a detection method acts on `training input data` to produce intermediate data called `trained data`. A corresponding Detector (the online stage) of the same method uses the `trained data` produced by Trainer to detect or recognize object in a given `Scene` (query image/point cloud). `Trained data` is usually stored in a preconfigured directory structure depending on the method starting from the base directory set for OpenDetection - `trained data location`. +The basic classes in OD are Trainers and Detectors. A Trainer (the offline stage) of a detection method acts on `training input data` to produce intermediate data called `trained data`. A corresponding Detector (the online stage) of the same method uses the `trained data` produced by Trainer to detect or recognize object in a given `Scene` (query image/point cloud). `Trained data` is usually stored in a preconfigured directory structure depending on the method starting from the base directory set for OpenDetection - `trained data location`. The data by Trainer can be used by any of the Detectors. The Decector can use data of different types of Trainers (or no trainers at all). Therefore there is many-to-many mapping between Trainers and Detector which is currently resolved by Documentation (i.e. one needs to see the documentation to find out what Trainer to use for a given Detector). In future we plan to associate this mapping by grouping compatible Trainer/Detector under `ObjectDetector`s. ##Trainer {#basic_structures2} -Each `Trainer` (od::ODTrainer) implements a virtual function `train` with the following signature: +Each `Trainer` (od::Trainer) implements a virtual function `train` with the following signature: virtual int train() = 0; ##Detector {#basic_structures3} -Each Detector (od::ODDetector) implements two functions - `detect()` and `detectOmni()` of the following signature. `detectOmni()` performs a detection/recognition on the entire scene (unsegmented and unprocessed) and provides information about the detection as well as its exact location. detect() takes an 'object candidate' or a segmented/processed scene as an input and identifies if the entire scene is a detection. +Each Detector (od::Detector) implements two functions - `detect()` and `detectOmni()` of the following signature. `detectOmni()` performs a detection/recognition on the entire scene (unsegmented and unprocessed) and provides information about the detection as well as its exact location. detect() takes an 'object candidate' or a segmented/processed scene as an input and identifies if the entire scene is a detection. - virtual ODDetections* detect(ODScene *scene); - virtual ODDetections* detectOmni(ODScene *scene); + virtual shared_ptr<Detections> detect(shared_ptr<Scene> scene); + virtual shared_ptr<Detections> detectOmni(shared_ptr<Scene> scene); Depending on the type of scene, Detectors are categorised in od::Detector2D and od::Detector3D. ##Detection {#basic_structures4} -A result of a Detector is Detections - a collection of Detection (od::ODDetection). Detection contains detection/recognition details as well as its exact location in the scene(for example bounding box for od::ODDetection2D and location/orientation for od::Detection3D) . +A result of a Detector is Detections - a collection of Detection (od::Detection). Detection contains detection/recognition details as well as its exact location in the scene(for example bounding box for od::Detection2D and location/orientation for od::Detection3D) . ##Typical code structure {#basic_structures5} @@ -37,7 +37,7 @@ A very typical code looks covering most of the pipeline looks like: \code{.cpp} //train: -od::g2d::ODHOGTrainer *trainer = new od::g2d::ODHOGTrainer("", trained_data_dir); //chose a trainer type +shared_ptr<od::g2d::HOGTrainer> trainer = make_shared<od::g2d::ODHOGTrainer>("", trained_data_dir); //chose a trainer type trainer->setPosSamplesDir(pos_samples); //set all the configurations as required by the trainer, the default values are provided as well trainer->setNegSamplesDir(neg_samples); trainer->setNOFeaturesNeg(10); @@ -45,15 +45,13 @@ trainer->setTrainHardNegetive(true); trainer->train(); //train! //detect: -ODDetector *detector = new od::g2d::ODHOGDetector; //chose a detector type +shared_ptr<Detector> detector = make_shared<od::g2d::ODHOGDetector>(); //chose a detector type detector->setTrainingDataLocation(trained_data_dir); detector->init(); //init with the required options //do as may detections as needed in a loop using the initialized settings: -ODDetections2D *detections = detector->detectOmni(scene); //Use the detect* methods for detection. sene is a scene object from frameGenerator +shared_ptr<Detections2D> detections = detector->detectOmni(scene); //Use the detect* methods for detection. sene is a scene object from frameGenerator -//infer -showimage(detections->renderMetainfo(*scene).getCVImage()) //do something with the detections, \endcode diff --git a/doc/doxygen/tutorials_doxygen/detection_2d.md b/doc/doxygen/tutorials_doxygen/detection_2d.md index fb59aa59..77e7f085 100644 --- a/doc/doxygen/tutorials_doxygen/detection_2d.md +++ b/doc/doxygen/tutorials_doxygen/detection_2d.md @@ -7,85 +7,76 @@ Detection 2D {#detection_2d1} This article goes through the 2D detection methods covered in OD. Specifically, it covers the classes - od::g2d::ODHOGDetector through a tutorial. -2D detection methods are performed by the classes Detector2D. They accept a `SceneImage` and performs detection/recognition on them. Currently Detector2Ds are classified into g2d and l2d namespaces. g2d covers detection methods which uses global 2D features (like HOG/Cascade) while l2d covers detection methods which uses local 2D features (like SIFT/SURF/ORB) for detection/recognition. Different 2D detectors that are available currently: od::g2d::ODHOGDetector, od::g2d::ODCascadeDetector, od::g2d::ODFaceRecognizer, od::l2d::ODCADRecognizer2DLocal +2D detection methods are performed by the classes Detector2D. They accept a `SceneImage` and performs detection/recognition on them. Currently Detector2Ds are classified into g2d and l2d namespaces. g2d covers detection methods which uses global 2D features (like HOG/Cascade) while l2d covers detection methods which uses local 2D features (like SIFT/SURF/ORB) for detection/recognition. Different 2D detectors that are available currently: od::g2d::ODHOGDetector, od::g2d::ODCascadeDetector, od::g2d::ODFaceRecognizer, od::l2d::ODCADRecognizer2DLocal. +The gpu module contains the gpu implementation of the algorithms where present. The ::gpu:: namespace has to be added after od in order to use that interface which has usually exactly the same API as the cpu version. ##HOG feature based detection {#detection_2d2} HOGDetector is a HOG feature based linear classifier. It accepts an image (od::ODSceneImage), computes its HOG feature (in a multiscale mannar for detectOmni() and a single descriptor from the resized image for detect()), runs a linear SVM obtained either by HOGTrainer through training or some default ones (from OpenCV), and informs if the classifier is true thereby providing detection. -A complete example including training is provided in `examples/objectdetector/od_hog_train.cpp`. For positive and negetive example get the data from the INRIA human dataset: http://pascal.inrialpes.fr/data/human/. +A complete example including training is provided in `examples/objectdetector/hog_train.cpp`. For positive and negetive example get the data from the INRIA human dataset: http://pascal.inrialpes.fr/data/human/. In this tutorial we will go through a more complete application in present in `examples/apps/global2D/od_multihog_app.cpp`. The code is provided here verbatime: \code{.cpp} -/** \brief Example of the usage of HOG detector - * - * \author Kripasindhu Sarkar - * - */ +* +* \author Kripasindhu Sarkar +* +*/ -#include "detectors/global2D/detection/ODHOGDetector.h" -#include "common/utils/ODFrameGenerator.h" - -#include "common/pipeline/ObjectDetector.h" -#include "common/pipeline/ODDetection.h" - - -using namespace od; -using namespace std; - -cv::Size sizesingle(640, 480); +//include all necessary files +#include "od/detectors/global2D/training/HOGTrainer.h" +#include "od/detectors/global2D/detection/HOGDetector.h" +#include "od/common/utils/FrameGenerator.h" +#include "od/common/utils/Viewer.h" +#include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) { - std::string trained_data_dir(argv[1]), input_video(argv[2]), output_video = "output.avi"; - if (argc > 3) output_video = argv[3]; - - //get 3 detectors of different types - vector<string> messages; messages.push_back("Original"); - vector<g2d::ODHOGDetector*> detectors; - g2d::ODHOGDetector *detector1 = new g2d::ODHOGDetector; // - messages.push_back("OpenCV Default People"); detectors.push_back(detector1); +//Check the input arguments + if(argc < 5){ + std::cout << "Wrong number of parameters: positive_samples_dir, negative_Samples_dir, trained_data_dir, test_iamges_dir" << std::endl; + return -1; + } - g2d::ODHOGDetector *detector2 = new g2d::ODHOGDetector; detector2->setSvmtype(g2d::ODHOGDetector::OD_DAIMLER_PEOPLE); - messages.push_back("OpenCV Daimler People"); detectors.push_back(detector2); + //define all the directories + std::string pos_samples(argv[1]); + std::string neg_samples(argv[2]); + std::string trained_data_dir(argv[3]); + std::string test_images(argv[4]); - g2d::ODHOGDetector *detector3 = new g2d::ODHOGDetector(trained_data_dir); - messages.push_back("Custom HOG from trained data"); detectors.push_back(detector3); + od::g2d::HOGTrainer trainer("", trained_data_dir); // create the trainer + trainer.setPosSamplesDir(pos_samples); + trainer.setNegSamplesDir(neg_samples); + trainer.setNOFeaturesNeg(10); + trainer.setTrainHardNegetive(true); + trainer.train(); - //init all detectors - for (int i = 0; i < detectors.size(); i++) detectors[i]->init(); + od::g2d::HOGDetector detector; // create the dector + detector.setTrainedDataLocation(trained_data_dir); //set trained director locations + detector.init(); //initialize detector //get scenes - od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_DEVICE> frameGenerator(input_video); - cv::VideoWriter videoWriter(output_video, CV_FOURCC('M','J','P','G'), 25, sizesingle * 2, true); - - + od::FrameGenerator<od::SceneImage, od::GENERATOR_TYPE_FILE_LIST> frameGenerator(test_images); //GUI - cv::namedWindow("Overlay", cv::WINDOW_NORMAL); - while(frameGenerator.isValid() && cv::waitKey(33) != 27) - { - od::ODSceneImage * scene = frameGenerator.getNextFrame(); + od::Viewer viewer; + viewer.initCVWindow(std::string("Overlay")); - vector<cv::Mat> images_to_show; - images_to_show.push_back(scene->getCVImage()); //push the first image + while(frameGenerator.isValid() && viewer.wait(10) != 27) + { + boost::shared_ptr<od::SceneImage> scene = frameGenerator.getNextFrame(); //read a frame (image) - //detect 3 times - for (int i = 0; i < detectors.size(); i++) - { - ODDetections2D *detections = detectors[i]->detectOmni(scene); - if(detections->size() > 0) - images_to_show.push_back(detections->renderMetainfo(*scene).getCVImage()); - else images_to_show.push_back(scene->getCVImage()); - } + //Detect + boost::shared_ptr<od::Detections2D> detections = detector.detectOmni(scene); - cv::Mat multiimage = makeCanvasMultiImages(images_to_show, sizesingle, messages); - cv::imshow("Overlay", multiimage); - videoWriter << multiimage; + if(detections->size() > 0) + viewer.render(detections->renderMetainfo(*scene), "Overlay"); + else + viewer.render(scene, "Overlay"); - delete scene; + viewer.spin(); } return 0; @@ -93,7 +84,7 @@ int main(int argc, char *argv[]) \endcode ###Data {#detection_2d3} -This app compares the results of HOG based detection with three different trained classifiers. For this app you need a pre-trained hog descriptor in your `trained_data` directory. You can either train as in `examples/objectdetector/od_hog_train.cpp`, or get the OD pre-trained data from the \ref getting_started2 "Data Repository". +This app compares the results of HOG based detection with three different trained classifiers. For this app you need a pre-trained hog descriptor in your `trained_data` directory. You can either train as in `examples/objectdetector/hog_train.cpp`, or get the OD pre-trained data from the \ref getting_started2 "Data Repository". For the query video, get a clip containing many pedestrians. For example you can get one from http://www.robots.ox.ac.uk/ActiveVision/Research/Projects/2009bbenfold_headpose/project.html#datasets @@ -113,22 +104,27 @@ Depending on the input video, you will see something like the following: ###Code explanation {#detection_2d4} We first init 3 different instances of HOGDetector of different settings. - vector<g2d::ODHOGDetector*> detectors; - g2d::ODHOGDetector *detector1 = new g2d::ODHOGDetector; // - messages.push_back("OpenCV Default People"); detectors.push_back(detector1); - - g2d::ODHOGDetector *detector2 = new g2d::ODHOGDetector; detector2->setSvmtype(g2d::ODHOGDetector::OD_DAIMLER_PEOPLE); - messages.push_back("OpenCV Daimler People"); detectors.push_back(detector2); - - g2d::ODHOGDetector *detector3 = new g2d::ODHOGDetector(trained_data_dir); - messages.push_back("Custom HOG from trained data"); detectors.push_back(detector3); + std::vector<od::g2d::HOGDetector> detectors; + od::g2d::HOGDetector detector1; // + messages.push_back("OpenCV Default People"); + detectors.push_back(detector1); + + od::g2d::HOGDetector detector2; + detector2.setSvmtype(od::g2d::HOGDetector::_DAIMLER_PEOPLE); + messages.push_back("OpenCV Daimler People"); + detectors.push_back(detector2); + + od::g2d::HOGDetector detector3(trained_data_dir); + messages.push_back("Custom HOG from trained data"); + detectors.push_back(detector3); You can set different types of linear SVMs for HOG detector using `setSvmtype` function. For adding a custom SVM use the type OD_CUSTOM and set the linear SVM weight vector using `setSVMDetector()`. You have to then update the other HOG detector parameters accordingly (like winSize etc) with which the your SVM was trained. If you give a trained data directory, it will use the xml file from the directory. Here, we set three different available HOG detectors. After setting all the parameters you need to call `init()` which is done in the following line. //init all detectors - for (int i = 0; i < detectors.size(); i++) detectors[i]->init(); + for (int i = 0; i < detectors.size(); i++) + detectors[i].init(); Then we create a FrameGenerator object, which is a templated class for grabbing both Images and Point Clouds (from kinect). Use od::GENERATOR_TYPE_DEVICE for grabbing frames from camera (kinekt for Point Cloud) or video which is done in the following line. @@ -140,17 +136,18 @@ The next valid frame can be accessed now by `frameGenerator.getNextFrame();` whi Each ImageScene is then checked by the detector using the detectOmni() function which searches the whole image for detections. The resultant Detections2D contains information about the detections made (like Type/Bounding box etc). We use an function `renderMetainfo` to draw the bounding box of all the detections made. //detect 3 times - for (int i = 0; i < detectors.size(); i++) + for(size_t i = 0; i < detectors.size(); i++) { - ODDetections2D *detections = detectors[i]->detectOmni(scene); + boost::shared_ptr<od::Detections2D> detections = detectors[i].detectOmni(scene); if(detections->size() > 0) images_to_show.push_back(detections->renderMetainfo(*scene).getCVImage()); - else images_to_show.push_back(scene->getCVImage()); + else + images_to_show.push_back(scene->getCVImage()); } The function `od::makeCanvasMultiImages` take N images and concatenate them in order forming a big image with messages. That concatenated image with having information from all the detectors are then being shown. - cv::Mat multiimage = makeCanvasMultiImages(images_to_show, sizesingle, messages); - cv::imshow("Overlay", multiimage); + multi_image = od::makeCanvasMultiImages(images_to_show, size_single, messages); + cv::imshow("Overlay", multi_image); + - \ No newline at end of file diff --git a/doc/doxygen/tutorials_doxygen/detection_3d.md b/doc/doxygen/tutorials_doxygen/detection_3d.md index 2f230c4b..08a2aa42 100644 --- a/doc/doxygen/tutorials_doxygen/detection_3d.md +++ b/doc/doxygen/tutorials_doxygen/detection_3d.md @@ -12,95 +12,95 @@ Detection 3D {#detection_3d1} ##Detection based on global features {#detection_3d2} -In this tutorial we will cover the example in `examples/objectdetector/od_pc_global_real_time.cpp which is a demo of the detector class od::g3d::ODCADDetector3DGlobal. This class detects 3D CAD models using global features of the point cloud. The corresponding Trainer class is od::g3d::ODCADDetectTrainer3DGlobal. They both internally use the functionality of the app 3d_rec_framework of PCL. +In this tutorial we will cover the example in `examples/objectdetector/pc_global_real_time.cpp which is a demo of the detector class od::g3d::CADDetector3DGlobal. This class detects 3D CAD models using global features of the point cloud. The corresponding Trainer class is od::g3d::CADDetectTrainer3DGlobal. They both internally use the functionality of the app 3d_rec_framework of PCL. -By default CADDetector3D (there is a CADDetector2D as well which detects CAD models in a 2D image - od::l2d::ODCADRecognizer2DLocal) uses ESF (Ensemble of Shape Functions) as the global feature. Allowed features are VFH, CVFH and ESF which can be set through `setDescName()` or through the constructor. +By default CADDetector3D (there is a CADDetector2D as well which detects CAD models in a 2D image - od::l2d::CADRecognizer2DLocal) uses ESF (Ensemble of Shape Functions) as the global feature. Allowed features are VFH, CVFH and ESF which can be set through `setDescName()` or through the constructor. ###Data {#detection_3d3} You need to get 3D CAD models of the objects you want to detect in the query scene. You can download CAD models from the CAD model databases available in the internet (eg: 3D warehouse: https://3dwarehouse.sketchup.com/?hl=en) or acquire your own CAD models of the real objects from a 3D scanners (which are popular in Robotics). We have provided some example CAD models in our \ref getting_started2 "Data Repository" at location \<source_to_data\>/training_input/CADModels. If you don't have objects which look like them (which is obvious), you won't get successful detection. If you don't have a 3D scanner, I would suggest you to look at the CAD models in 3D warehouse which looks 'similar' to the real object. Thanks to the the 3D global features which do not require CAD models to be of exactly the same geometry as the real object. Somewhat 'similar looking' CAD model will work as well. -The CAD models is to be placed in a strict directory structure before training which is documented in od::g3d::ODCADDetectTrainer3DGlobal. This particular demo uses kinect to acquire Point Cloud in the real time, so you need a kinect device as well. +The CAD models is to be placed in a strict directory structure before training which is documented in od::g3d::CADDetectTrainer3DGlobal. This particular demo uses kinect to acquire Point Cloud in the real time, so you need a kinect device as well. ###Code {#detection_3d4} The code is provided here verbatim. \code{.cpp} -/** \brief Example of the usage of global pipeline - * - * \author Kripasindhu Sarkar - * - */ - - -#include "common/pipeline/ObjectDetector.h" -#include "common/pipeline/ODDetection.h" -#include "detectors/global3D/ODPointCloudGlobalMatching.h" -#include "common/utils/ODFrameGenerator.h" - +#include "od/detectors/global3D/PointCloudGlobalMatching.h" +#include "od/common/utils/FrameGenerator.h" +#include "od/common/utils/Viewer.h" +#include <string> +#include <boost/shared_ptr.hpp> int main(int argc, char *argv[]) { - std::string training_input_dir(argv[1]), trained_data_dir(argv[2]); + if(argc < 3){ + std::cout << "Wrong number of parameters: training_dir, trained_data_dir" << std::endl; + return -1; + } - //trainer - od::ODTrainer *trainer = new od::g3d::ODCADDetectTrainer3DGlobal(training_input_dir, trained_data_dir); - trainer->train(); + std::string training_input_dir(argv[1]); + std::string trained_data_dir(argv[2]); + //trainer + od::g3d::CADDetectTrainer3DGlobal trainer(training_input_dir, trained_data_dir); + trainer.train(); //detector - od::g3d::ODCADDetector3DGlobal<pcl::PointXYZRGBA> *detector = new od::g3d::ODCADDetector3DGlobal<pcl::PointXYZRGBA>(); - detector->setTrainingInputLocation(training_input_dir); - detector->setTrainedDataLocation(trained_data_dir); - detector->init(); + od::g3d::CADDetector3DGlobal<pcl::PointXYZRGBA> detector;; + detector.setTrainingInputLocation(training_input_dir); + detector.setTrainedDataLocation(trained_data_dir); + detector.init(); //GUI and feedback - od::ODScenePointCloud<pcl::PointXYZRGBA> *frame; - pcl::visualization::PCLVisualizer vis ("kinect"); + od::Viewer viewer; + viewer.initPCLWindow(std::string("kinect")); + od::FrameGenerator<od::ScenePointCloud<pcl::PointXYZRGBA>, od::GENERATOR_TYPE_DEVICE> frameGenerator; + pcl::PointXYZ pos; - od::ODFrameGenerator<od::ODScenePointCloud<pcl::PointXYZRGBA>, od::GENERATOR_TYPE_DEVICE> frameGenerator; while(frameGenerator.isValid()) { - frame = frameGenerator.getNextFrame(); + boost::shared_ptr<od::ScenePointCloud<pcl::PointXYZRGBA>> frame = frameGenerator.getNextFrame(); //remove previous point clouds and text and add new ones in the visualizer - vis.removeAllPointClouds(); - vis.removeAllShapes(); - vis.addPointCloud<pcl::PointXYZRGBA> (frame->getPointCloud(), "frame"); + viewer.removeAll(); + viewer.removeAllShapes(); + viewer.render(frame->getPointCloud(), std::string("frame")); //Detect - od::ODDetections3D * detections = detector->detectOmni(frame); + boost::shared_ptr<od::Detections3D> detections = detector.detectOmni(frame); //add all the detections in the visualizer with its id as text - for (size_t i = 0; i < detections->size (); i++) + for(size_t i = 0; i < detections->size(); ++i) { - std::stringstream cluster_name; - cluster_name << "cluster_" << i; pcl::visualization::PointCloudColorHandlerRandom<pcl::PointXYZ> random_handler (detections->at(i)->getMetainfoCluster()); - vis.addPointCloud<pcl::PointXYZ> (detections->at(i)->getMetainfoCluster(), random_handler, cluster_name.str ()); - - pcl::PointXYZ pos; pos.x = detections->at(i)->getLocation()[0]; pos.y = detections->at(i)->getLocation()[1]; pos.z = detections->at(i)->getLocation()[2]; - vis.addText3D (detections->at(i)->getId(), pos, 0.015f, 1, 0, 1, cluster_name.str() + "_txt", 0); + viewer.render(detections->at(i)->getMetainfoCluster(), random_handler, std::string("cluster_") + std::to_string(i)); + + pos.x = detections->at(i)->getLocation()[0]; + pos.y = detections->at(i)->getLocation()[1]; + pos.z = detections->at(i)->getLocation()[2]; + viewer.addText(detections->at(i)->getId(), pos, 0.015f, cv::Scalar(1, 0, 1)); } - vis.spinOnce (); + viewer.spin(); } return 0; } + \endcode ###Execution {#detection_3d5} After compiling the OD tree, run the binary from the build directory as: - examples/objectdetector/od_example_pc_global_real_time <path_to_CAD_models> <trained_data_directory> + examples/objectdetector/pc_global_real_time <path_to_CAD_models> <trained_data_directory> You will see output similar to the following video: @@ -114,32 +114,35 @@ You will see output similar to the following video: ###Code explanation {#detection_3d6} The code is straightforward and self explanatory. We first initialize detector and trainer with `training input location` with CAD models and the `trained_data location`. - //trainer - od::ODTrainer *trainer = new od::g3d::ODCADDetectTrainer3DGlobal(training_input_dir, trained_data_dir); - trainer->train(); - - //detector - od::g3d::ODCADDetector3DGlobal<pcl::PointXYZRGBA> *detector = new od::g3d::ODCADDetector3DGlobal<pcl::PointXYZRGBA>(); - detector->setTrainingInputLocation(training_input_dir); - detector->setTrainedDataLocation(trained_data_dir); - detector->init(); + //trainer + od::g3d::CADDetectTrainer3DGlobal trainer(training_input_dir, trained_data_dir); + trainer.train(); + + //detector + od::g3d::CADDetector3DGlobal<pcl::PointXYZRGBA> detector;; + detector.setTrainingInputLocation(training_input_dir); + detector.setTrainedDataLocation(trained_data_dir); + detector.init(); + We then get a FrameGenerator of PointCloud and Device types to get Point Cloud Scene from kinect device. We also instantiate a VTK window through PCLVisualizer. - pcl::visualization::PCLVisualizer vis ("kinect"); - od::ODFrameGenerator<od::ODScenePointCloud<pcl::PointXYZRGBA>, od::GENERATOR_TYPE_DEVICE> frameGenerator; + od::Viewer viewer; + viewer.initPCLWindow(std::string("kinect")); We then get Point Cloud scenes in a loop and perform Omnidetection on them. - frame = frameGenerator.getNextFrame(); - //Detect - od::ODDetections3D * detections = detector->detectOmni(frame); + boost::shared_ptr<od::ScenePointCloud<pcl::PointXYZRGBA>> frame = frameGenerator.getNextFrame(); + boost::shared_ptr<od::Detections3D> detections = detector.detectOmni(frame); We then add each detection made in the PCLVisualizer window with different colors and draw its classification ID on top of them. - vis.addPointCloud<pcl::PointXYZ> (detections->at(i)->getMetainfoCluster(), random_handler, cluster_name.str ()); + pcl::visualization::PointCloudColorHandlerRandom<pcl::PointXYZ> random_handler (detections->at(i)->getMetainfoCluster()); + viewer.render(detections->at(i)->getMetainfoCluster(), random_handler, std::string("cluster_") + std::to_string(i)); - pcl::PointXYZ pos; pos.x = detections->at(i)->getLocation()[0]; pos.y = detections->at(i)->getLocation()[1]; pos.z = detections->at(i)->getLocation()[2]; - vis.addText3D (detections->at(i)->getId(), pos, 0.015f, 1, 0, 1, cluster_name.str() + "_txt", 0); + pos.x = detections->at(i)->getLocation()[0]; + pos.y = detections->at(i)->getLocation()[1]; + pos.z = detections->at(i)->getLocation()[2]; + viewer.addText(detections->at(i)->getId(), pos, 0.015f, cv::Scalar(1, 0, 1)); \ No newline at end of file diff --git a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md index f8c7c5f5..d8d25e16 100644 --- a/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md +++ b/doc/doxygen/tutorials_doxygen/gsoc2016_blog_giacomo.md @@ -93,7 +93,7 @@ While continuing to restructure the library I came across a decision which can b and the compiler would need the include folder where the file is located to compile. This means that the file should be localted directly in the upper include folder, but this is not our case since the structure is like *detectors/global3D/detection/* so it would be better to have @code -#include <detectors/global3D/detection/ODCADDetector3DGlobal.h> +#include <detectors/global3D/detection/CADDetector3DGlobal.h> @endcode and include the upper level folder. To use the first solution we would need to include *detectors/global3D/detection/* but this is not really appealing since it would mess with the whole include structure. For now I opted for the second solution, but the include structure could be changed at any time. This way the include file structure resables exactly the source file structure: diff --git a/doc/doxygen/tutorials_doxygen/introduction_general.md b/doc/doxygen/tutorials_doxygen/introduction_general.md index d74a5ca0..9c1a88b6 100644 --- a/doc/doxygen/tutorials_doxygen/introduction_general.md +++ b/doc/doxygen/tutorials_doxygen/introduction_general.md @@ -30,23 +30,18 @@ The main philosophy is that all the APIs of detection we provide, irrespective o To detect face using the default cascade detector, the usage looks like: \code{.cpp} -od::g2d::ODCascadeDetector *detector = new od::g2d::ODCascadeDetector; //chose a detector type -detector->setTrainingDataLocation(trained_cascade); -detector->init(); //init with the required options, default values are provided -ODDetections2D *detections = detector->detectOmni(scene); //Use the detect* methods for detection. sene is a scene object from frameGenerator - -showimage(detections->renderMetainfo(*scene).getCVImage()) //do something with the detections, +boost::shared_ptr<od::Detector2D> detector = boost::make_shared<od::g2d::CascadeDetector>(gpu, cam); +boost::shared_ptr<od::Detections2D> detections = detector->detectOmni(scene); //Use the detect* methods for detection. sene is a scene object from frameGenerator \endcode For detecting people with a default pretrained HOG based detector, the usage looks like: \code{.cpp} -ODDetector *detector = new od::g2d::ODHOGDetector; //chose a detector type -detector->init(); //init with the required options, default values are provided -ODDetections2D *detections = detector->detectOmni(scene); //Use the detect* methods for detection. sene is a scene object from frameGenerator - -showimage(detections->renderMetainfo(*scene).getCVImage()) //do something with the detections, +od::g2d::HOGDetector detector; +detector.setTrainedDataLocation(trained_data_dir); +detector.init(); //init with the required options, default values are provided +boost::shared_ptr<od::Detections2D> detections = detector.detectOmni(scene); //Use the detect* methods for detection. sene is a scene object from frameGenerator \endcode To train your own HOG based detector and then use the trained detector for detection, the usage looks like: @@ -61,16 +56,14 @@ trainer->setTrainHardNegetive(true); trainer->train(); //train! //detect: do as many detection using the trained value -ODDetector *detector = new od::g2d::ODHOGDetector; //chose a detector type +boost::shared_ptr<od::g2d::HOGDetector> detector= boost::make_shared<od::g2d::ODHOGDetector>(); //chose a detector type detector->setTrainingDataLocation(trained_data_dir); detector->init(); //init with the required options -ODDetections2D *detections = detector->detectOmni(scene); //Use the detect* methods for detection. sene is a scene object from frameGenerator +boost::shared_ptr<od::Detections2D> detections = detector->detectOmni(scene); //Use the detect* methods for detection. sene is a scene object from frameGenerator -//infer -showimage(detections->renderMetainfo(*scene).getCVImage()) //do something with the detections, \endcode -And so on... Our APIs are highly structured and follow deep polymorphism for the detector hierarchy. The library is in C++ for the fast implementation. The details are provided in the next sections. +And so on... Our APIs are highly structured and follow deep polymorphism for the detector hierarchy. The library is in C++11 for the fast implementation. The details are provided in the next sections. Usage and target audience {#introduction_general4} ---- diff --git a/examples/objectdetector/framegenerator.cpp b/examples/objectdetector/framegenerator.cpp index 7243ac80..a7db5697 100644 --- a/examples/objectdetector/framegenerator.cpp +++ b/examples/objectdetector/framegenerator.cpp @@ -49,7 +49,6 @@ int main(int argc, char *argv[]) boost::shared_ptr<od::ScenePointCloud<pcl::PointXYZRGBA>> frame = frameGenerator.getNextFrame(); - viewer.remove("frame"); viewer.update(frame, "frame"); viewer.spin(); From 36ea8f85479f4dba988fa906e32fe3e11432c216 Mon Sep 17 00:00:00 2001 From: giacomo <giacomo.dabisias@gmail.com> Date: Wed, 7 Sep 2016 00:46:56 +0200 Subject: [PATCH 79/79] minor fixes --- doc/doxygen/tutorials_doxygen/detection_2d.md | 12 ++++++------ doc/doxygen/tutorials_doxygen/getting_started.md | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/doc/doxygen/tutorials_doxygen/detection_2d.md b/doc/doxygen/tutorials_doxygen/detection_2d.md index 77e7f085..5f7864c9 100644 --- a/doc/doxygen/tutorials_doxygen/detection_2d.md +++ b/doc/doxygen/tutorials_doxygen/detection_2d.md @@ -5,9 +5,9 @@ Detection 2D {#detection_2d} Detection 2D {#detection_2d1} === -This article goes through the 2D detection methods covered in OD. Specifically, it covers the classes - od::g2d::ODHOGDetector through a tutorial. +This article goes through the 2D detection methods covered in OD. Specifically, it covers the classes - od::g2d::HOGDetector through a tutorial. -2D detection methods are performed by the classes Detector2D. They accept a `SceneImage` and performs detection/recognition on them. Currently Detector2Ds are classified into g2d and l2d namespaces. g2d covers detection methods which uses global 2D features (like HOG/Cascade) while l2d covers detection methods which uses local 2D features (like SIFT/SURF/ORB) for detection/recognition. Different 2D detectors that are available currently: od::g2d::ODHOGDetector, od::g2d::ODCascadeDetector, od::g2d::ODFaceRecognizer, od::l2d::ODCADRecognizer2DLocal. +2D detection methods are performed by the classes Detector2D. They accept a `SceneImage` and performs detection/recognition on them. Currently Detector2Ds are classified into g2d and l2d namespaces. g2d covers detection methods which uses global 2D features (like HOG/Cascade) while l2d covers detection methods which uses local 2D features (like SIFT/SURF/ORB) for detection/recognition. Different 2D detectors that are available currently: od::g2d::HOGDetector, od::g2d::CascadeDetector, od::g2d::FaceRecognizer, od::l2d::CADRecognizer2DLocal. The gpu module contains the gpu implementation of the algorithms where present. The ::gpu:: namespace has to be added after od in order to use that interface which has usually exactly the same API as the cpu version. ##HOG feature based detection {#detection_2d2} @@ -16,7 +16,7 @@ HOGDetector is a HOG feature based linear classifier. It accepts an image (od::O A complete example including training is provided in `examples/objectdetector/hog_train.cpp`. For positive and negetive example get the data from the INRIA human dataset: http://pascal.inrialpes.fr/data/human/. -In this tutorial we will go through a more complete application in present in `examples/apps/global2D/od_multihog_app.cpp`. The code is provided here verbatime: +In this tutorial we will go through a more complete application in present in `examples/apps/global2D/multihog_app.cpp`. The code is provided here verbatime: \code{.cpp} @@ -104,7 +104,7 @@ Depending on the input video, you will see something like the following: ###Code explanation {#detection_2d4} We first init 3 different instances of HOGDetector of different settings. - std::vector<od::g2d::HOGDetector> detectors; + std::vector<od::g2d::HOGDetector> detectors; od::g2d::HOGDetector detector1; // messages.push_back("OpenCV Default People"); detectors.push_back(detector1); @@ -118,7 +118,7 @@ We first init 3 different instances of HOGDetector of different settings. messages.push_back("Custom HOG from trained data"); detectors.push_back(detector3); -You can set different types of linear SVMs for HOG detector using `setSvmtype` function. For adding a custom SVM use the type OD_CUSTOM and set the linear SVM weight vector using `setSVMDetector()`. You have to then update the other HOG detector parameters accordingly (like winSize etc) with which the your SVM was trained. If you give a trained data directory, it will use the xml file from the directory. Here, we set three different available HOG detectors. +You can set different types of linear SVMs for HOG detector using `setSvmtype` function. For adding a custom SVM use the type CUSTOM and set the linear SVM weight vector using `setSVMDetector()`. You have to then update the other HOG detector parameters accordingly (like winSize etc) with which the your SVM was trained. If you give a trained data directory, it will use the xml file from the directory. Here, we set three different available HOG detectors. After setting all the parameters you need to call `init()` which is done in the following line. @@ -129,7 +129,7 @@ After setting all the parameters you need to call `init()` which is done in the Then we create a FrameGenerator object, which is a templated class for grabbing both Images and Point Clouds (from kinect). Use od::GENERATOR_TYPE_DEVICE for grabbing frames from camera (kinekt for Point Cloud) or video which is done in the following line. //get scenes - od::ODFrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_DEVICE> frameGenerator(input_video); + od::FrameGenerator<od::ODSceneImage, od::GENERATOR_TYPE_DEVICE> frameGenerator(input_video); The next valid frame can be accessed now by `frameGenerator.getNextFrame();` which is being done in loop until exhaustion. diff --git a/doc/doxygen/tutorials_doxygen/getting_started.md b/doc/doxygen/tutorials_doxygen/getting_started.md index 383d7e00..5465a8ee 100644 --- a/doc/doxygen/tutorials_doxygen/getting_started.md +++ b/doc/doxygen/tutorials_doxygen/getting_started.md @@ -16,7 +16,7 @@ git clone https://github.com/krips89/opendetection.git cd opendetection mkdir build; cd build cmake .. -make -j8 +make -j @endcode ##Get Data {#getting_started2} @@ -35,12 +35,12 @@ Make `<path_to_data>/trained_data` as your trained_data location in all the exam Go to the build directory of OD (\<path_to_source\>/build). Run a face detector from your webcam using: @code{.bash} -./examples/objectdetector/od_cascade_cam \<path_to_data\>/trained_data/ +./examples/objectdetector/cascade_cam \<path_to_data\>/trained_data/ @endcode Or get some images containing people (suppose in \<path_to_images\>/\*.JPG) and run the people detector using: @code{.bash} -./examples/objectdetector/od_image_hog_files \<path_to_data\>/trained_data/ "\<path_to_images\>/\*.JPG" +./examples/objectdetector/image_hog_files \<path_to_data\>/trained_data/ "\<path_to_images\>/\*.JPG" @endcode Depending on the image, you will see something like the following.